Отличие данных от инструкций в ARM

В (32-разрядных) ядрах ARM Linux, как отличить данные, встроенные в раздел кода, от инструкций?

Лучше иметь облегченный подход, такой как битовые маски, которые можно легко реализовать. Диссемблер в ядро ​​встраивать неразумно.


person WindChaser    schedule 18.11.2016    source источник


Ответы (1)


В общем, то, о чем вы просите, невозможно.

Рассмотрим эту функцию, которая использует слишком большое значение данных для немедленного кодирования:

@ void patch_nop(void *code_addr);
patch_nop:
    ldr r1, =0xe1a00000
    str r1, [r0]
    bx lr

который к моменту прохождения ассемблера и обратно выглядит так:

$ arm-none-eabi-objdump -d a.out

a.out:     file format elf32-littlearm


Disassembly of section .text:

    00000000 <patch_nop>:
       0:   e59f1004        ldr     r1, [pc, #4]    ; c <patch_nop+0xc>
       4:   e5801000        str     r1, [r0]
       8:   e12fff1e        bx      lr
       c:   e1a00000        .word   0xe1a00000

Благодаря данным ELF мы все еще можем установить, где заканчивается функция и начинается пул литерала, но работа objdump по копанию разделов и символов вряд ли «легковесна», и кто сказал, что они у вас есть? Что, если у вас есть просто код?

$ arm-none-eabi-objcopy -Obinary a.out bin
$ arm-none-eabi-objdump -D -marm -bbinary bin

bin:     file format binary


Disassembly of section .data:

00000000 <.data>:
   0:   e59f1004        ldr     r1, [pc, #4]    ; 0xc
   4:   e5801000        str     r1, [r0]
   8:   e12fff1e        bx      lr
   c:   e1a00000        nop                     ; (mov r0, r0)

Там. У вас есть встроенные в ваш поток инструкций данные, которые являются инструкциями. Даже данные, которые случайно случайно выглядят как инструкция. Вы буквально ничего не можете взять из одних только этих 32 бит, чтобы сделать вывод, что они не будут выполняться (ну, по крайней мере, не из этого места).

Есть несколько эвристик, которые могут помочь сделать обоснованное предположение, особенно если можно предположить, что какие-либо дополнительные предварительные знания сужают его:

  • Все, что может быть закодировано как непосредственное, почти наверняка является инструкцией, потому что компилятор/ассемблер не выдал бы ее как литерал. Однако в идеале вы хотели бы знать, по крайней мере, является ли предыдущий код ARM или Thumb, чтобы знать, каков соответствующий непосредственный диапазон*.

  • Все, что является неопределенной инструкцией, обычно будет данными, если только не случится так, что это код, который хочет намеренно вызвать исключение undef. И вам, по сути, нужно иметь большую часть дизассемблера, чтобы проверить, что что-то не соответствует какой-либо определенной кодировке. Поверх ARM/Thumb.

  • Все, что следует сразу за безусловной ветвью, может быть литеральными данными, особенно если у вас есть символы и вы можете сказать, что это очень близко к началу следующей функции, или если у вас есть некоторое представление о данных, которые вы ищете, и они выглядят как данные. Последний пункт, безусловно, актуален, если вы просто смотрите на дизассемблирование - на практике литеральные данные, как правило, представляют собой такие вещи, как адреса, которые обычно выделяются как больной палец, когда вы смотрите на код в целом. .

  • Самый надежный способ проверить, является ли что-то литералом, — это просмотреть предыдущий код (до 1025 инструкций), проверяя нагрузку, относящуюся к ПК, нацеленную на этот адрес. Вам нужно только проверить буквальные кодировки загрузки (есть ваша простая операция битмаскирования), а затем декодировать относительное смещение, если вы его найдете. В идеале вы хотели бы решить проблему ARM/Thumb, чтобы избежать ложных срабатываний при проверке на неподходящие кодировки, и в самом абсолютно патологическом случае вы все равно можете столкнуться с некоторыми данными в предыдущем литеральном пуле, который выглядит как литеральный таргетинг нагрузки. ваш адрес; никогда не говори никогда.

И, конечно же, это все еще предполагает, что литеральные пулы автоматически создаются компилятором/ассемблером; когда дело доходит до полностью написанного от руки ассемблерного кода, все ставки сняты:

patch_nop2:
    ldr r1, [pc, #-4]
    mov r0, r0
    str r1, [r0]
    bx lr

Это код? да. Это данные? да.

* Между прочим, различение кода ARM и Thumb сводится, по сути, к той же проблеме, что и эта: "Что означает этот битовый шаблон?" - и столь же нетривиально без посторонней помощи.

† ​​Не каламбур

person Notlikethat    schedule 18.11.2016
comment
И вот гораздо более коварный. - person Notlikethat; 19.11.2016