x86 - переменная длина команды, что означает, что ее очень трудно разобрать. Не рекомендуется, если это ваш первый дизассемблер.
Говоря, что ... подход, который я использую, заключается в том, что вы должны идентифицировать в двоичном виде байты, которые являются первым байтом кода операции, и отделить их от байтов, которые являются вторыми или другими байтами в коде операции или данных. Как только вы узнаете, что можете начать в начале двоичного файла и разобрать коды операций.
Как вы узнаете коды операций из других байтов? Вам нужно пройти все возможные пути выполнения, звучит как проблема рекурсии, и может быть, но это не обязательно. Посмотрите на таблицу векторов прерываний и / или все аппаратные точки входа в код. Это дает вам короткий список байтов кода операции. Подход без рекурсии состоит в том, чтобы сделать много проходов по двоичному файлу, просматривая каждый байт, помеченный как код операции, декодировать его настолько, чтобы узнать, сколько он потребляет байтов. Вам также необходимо знать, является ли это безусловной ветвью, условной ветвью, возвратом, вызовом и т. Д. Если это не безусловная ветвь или возврат, вы можете предположить, что байт после этой инструкции является первым байтом следующей инструкции. Каждый раз, когда вы сталкиваетесь с ответвлением или каким-либо вызовом, вычислите адрес назначения, добавьте этот байт в список. Продолжайте делать проходы, пока вы не сделаете проход, который не добавляет новых байтов в список. Вы также должны убедиться, что если, скажем, вы нашли байт, который является 3-х байтовой инструкцией, но байт после того, как он помечен как инструкция, то у вас есть проблема. Такие вещи, как условные ветви, которым предшествует то, что гарантирует, что они никогда не будут ветвиться. Вы не увидите этого много, если вообще с высокоуровневым кодом, скомпилированным в двоичный файл, но старые добрые времена написанного вручную ассемблера или люди, которые хотят защитить свой код, будут делать такие вещи.
К сожалению, если все, что у вас есть - это двоичный файл, для набора инструкций переменной длины вы не получите идеальную разборку. Некоторые направления ветвлений вычисляются во время выполнения, иногда сборка с ручным кодированием изменяет стек перед выполнением возврата, чтобы изменить следующий код, который выполняется следующим образом: если это единственный путь к этому коду, то вы, скорее всего, не поймете это программно, если не зайдете так далеко смоделировать код. И даже при моделировании вы не сможете охватить все пути выполнения.
С помощью набора инструкций фиксированной длины, например, ARM (при условии, что это рука, а не смесь рук и большого пальца), вы можете просто начинать с начала двоичного файла и разбирать, пока у вас не закончатся слова. Вы можете разбирать слово данных на действительную или недействительную или маловероятно используемую инструкцию, но это нормально.
Я не удивлюсь, если где-нибудь в эльфе будет что-то, что указывает, какие части двоичного файла являются исполняемыми, а какие - данными. может быть, даже настолько, что вам не нужно проходить пути данных, я сомневаюсь, что objdump выполняет такую задачу, что он, вероятно, использует что-то в файле elf.
Формат файла elf задокументирован во многих местах. Существует базовая структура, и поставщики могут добавлять определенные типы блоков, которые будут задокументированы поставщиком.