Как распознать инструкцию "вызова" сборки в памяти? - PullRequest
0 голосов
/ 09 июня 2018

Как можно отличить в исполняемой памяти процесса коды операций для инструкции "вызова" сборки (E8 [Адрес]) от других байтов E8 (например, одного, который находится в середине другой инструкции)?(С точки зрения языка C, предпочтительно)

Достаточно ли проверки того, что четыре байта сразу после байта E8 ссылаются на действительный адрес, и затем проверки, начинается ли эта область (то есть начало вызываемой функции)с байтами, соответствующими кодам операций «push ebp» и «mov ebp, esp» (большинство функций используют этот пролог)?Или есть лучший вариант, например, проверка каждого кода операции от точки входа до точки выхода?

Кстати, у меня мало опыта в этой теме, поэтому любая информация приветствуется.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 10 июня 2018

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

Нет гарантии, что это сработает, так как обычно тривиально отключить дизассемблер и вызвать его сбой.

Простой пример, есть много способов сделать это.

set flag
blah
blah
jump if flag to hello
put the first opcode byte but not the rest of the instruction here as data
hello:
real stuff here

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

Компиляторы, если они не предназначены или не имеют тенденцию не создавать код, имеющий проблемы с разборкой (в порядке исполнения не линейно).Но если вы вернетесь и скажете «дизассемблирование» классических дисков с видеоиграми, вы можете обнаружить такие проблемы с дизассемблированием, а затем придется проделать дополнительную работу, в основном анализ кода, чтобы определить, какой из этих путей выполнения был правильным.

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

0 голосов
/ 10 июня 2018

Правильная интерпретация начинается с адреса, который, как известно, является началом инструкции.

(Этот ответ, конечно, относится к процессорам с инструкциями переменной длины, таким как архитектура Intel x86.)

Когда процессор интерпретирует инструкции, он всегда начинает интерпретацию в определенном месте, потому что:

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

Все это места, которые, как известно, являются началом инструкций, потому что они были разработаны таким образом: мы писали программное обеспечение, и в этих местах мыпоставить инструкцию.

При интерпретации инструкции процессор следует правилам кодирования команд.Он просматривает первый байт, и биты в этом байте указывают, являются ли следующие несколько байтов операционным кодом, модификаторами или операндами для текущей инструкции.Затем он интерпретирует следующие байты соответственно.Таким образом, E8 16 в первом байте инструкции будет интерпретироваться иначе, чем E8 16 байт в другом месте.

Дизассемблер запускается там, где пользователь говорит ему.Часто это адрес, заданный меткой в ​​программе (такой как имя функции) или другой информацией, такой как значение счетчика текущей программы или адрес возврата, найденный в стеке.Эти адреса - все начало команд, поэтому дизассемблер интерпретирует байты так же, как процессор.

Иногда может не быть легкого доступа к информации о том, где начинаются инструкции в определенной области, и он может просто сказатьдизассемблер, чтобы начать дизассемблирование по произвольному адресу в середине региона.В этом случае первые несколько дизассемблированных инструкций могут быть неверными, поскольку дизассемблер интерпретирует байты, начиная с мест, которые обычно не являются началом инструкций.(Типичным примером этого является то, что кто-то отлаживает и знает текущий счетчик программы, но хочет оглянуться назад на последние дюжины или около того инструкций, не начиная с начала текущей подпрограммы. В этом случае можно сказать дизассемблеручтобы начать разборку 100 байт назад от счетчика текущей программы.) Однако часто случается так, что, по сути, случайно, одна из неправильно интерпретированных инструкций заканчивается в том месте, где начинается правильная инструкция.Затем дизассемблер правильно разбирает эту инструкцию, и она синхронизируется с правильной последовательностью команд, а оставшаяся разборка выполняется правильно.Поскольку большинство инструкций короткие, а длины инструкций значительно различаются, существует высокая вероятность, что это происходит в течение нескольких инструкций.

Можно поиграть с кодировками инструкций и создать последовательность байтов, которая представляет два разныхпотоки команд зависят от того, где вы начинаете выполнять его, так что байты кода операции одного потока являются байтами модификатора / операнда другого, и наоборот.Это не делается в обычном программировании, но демонстрирует, что интерпретация байтов инструкций зависит от начальной позиции.

...