Maximize
Bookmark

VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Несколько слов о дизассемблерах

pr0mix
Январь 2009

[Вернуться к списку] [Комментарии]

Re:)

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

Это своего рода справочная портянка для создания (простого) дизасма.

Предполагается, что тот, кто собирается прочитать сей текст, знает формат команд IA-32 (так как здесь он описан коротко). Если это не так, то сначала желательно просмотри ссылки в конце данного текста.

Итак, поехали.

Для начала определение:

Машинные Команды - это сформированные по определенным правилам последовательности байтов. Так вот, правила эти называют форматом команд, и формат этот в свою очередь состоит из нескольких полей, ОБЯЗАТЕЛЬНЫМ из которых является код операции (опкод). Остальные поля могут отсутствовать (тогда команда содержит только 1 байт - опкод). Формат команд IA-32:

ПРЕФИКС  ОПКОД  МОДРМ  СИБ  СМЕЩЕНИЕ  НЕПОСРЕДСТВЕННЫЙ ОПЕРАНД

А вот, собственно, вариант дизассемблирования:

  1. Определение префиксов команды
  2. Опкод (разбор)
  3. Парсинг ModR/M
  4. Парсинг SIB
  5. Смещение и непосредственный операнд

Теперь по порядку.

1. ОПРЕДЕЛЕНИЕ ПРЕФИКСОВ КОМАНДЫ.

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

Префиксы делятся на 4 группы:

Максимальная длина команды равна 15 байтам. Все 15 байт префиксами быть не могут (почему? - вспоминаем про опкод:) - по ходу возникнет #UD (недействительный код операции). LOCK можно использовать только один раз и только с определенными командами. REP/E/NE - могут использоваться с командами обработки срок, могут быть частью команд из расширенного набора или вообще "быть не при делах". Разрешено многократное использование данных префиксов, однако это будет равносильно однократному + использоваться будет последний встреченный. 2 группа префиксов - по идее также используется последний встреченный. Здесь же: 2Eh и 3Eh (появились в Pentium 4 & Xeon) - еще и определяют (только с инструкциями Jcc):

Префиксы 66h/67h изменяют разрядность операнда/адреса: 16 или 32 бита, а также могут быть частью команд из расширенного набора. При создании дизасма - быть внимательным к этим префиксам(блин, и к другим тоже). В общем, здесь все. Остальное - внимательное изучение доков от Intel.

2. ОПКОД (РАЗБОР).

Код оперции (опкод)
обязательный элемент, описывающий операцию, выполняемую командой. Может занимать 1,2,3 байта, а также и часть битов для некоторых машинных команд в байте ModR/M (о нем чуть ниже).

Опкод не имеет однозначной структуры. В зависимости от определенных команд может иметь от 1 до 3 элементов, уточняющих детали операции:

Бывают ситуации, что после разбора опкода надо вернуться к пункту 1 (посмотреть на выделенные префиксы - для некоторых команд из расширенного набора - но и это решается).

Теперь, собственно, о декодировании команд. Так вот, лучше всего декодировать по характеристикам опкода. Как это делается? Тут есть несколько вариков: можно искать опкод

  1. кучей if{} else{}, и если нашли - установить соответствующие флаги (характеристики) - это имеющиеся в данном опкоде поля, набор параметров и другие.
  2. switch (аналог if/else) - тогда их будет 2: 1-ый - для однобайтных опкодов, а 2-ой - для 0x0F, и также, при нахождении данного опкода выставлять соответствующие флаги.

Но, по-моему, лучше

  1. соcтавить таблицы, содержащие Характеристики уже всех опкодов. Некоторые спрашивают, как же создаются эти самые таблицы и как узнать, какие из характеристик соответствуют какому опкоду? Так вот, таблицы (массивы) строятся так, чтобы опкод команды был ИНДЕКСОМ (индексом в массиве) элемента, описывающего данный опкод. Дизасм выбирает характеристики опкода из таблицы, после чего декодирует опкод уже по набору этих характеристик. Насчет характеристик: все находится в мануалах Intel (IA-32 Intel Architecture Software Developer's Manual Volume 2B Instruction Set Reference N-Z - где-то в конце книги Appendix A Opcode Map). Короче, приведу несколько примеров:
    Opcode	Instruction	Description
    88 /r	MOV r/m8,r8	Move r8 to r/m8
    C6 /0	MOV r/m8,imm8	Move imm8 to r/m8
    
    	Instruction	Description
    88 /r	MOV r/m8,r8	Move r8 to r/m8
    C6 /0	MOV r/m8,imm8	Move imm8 to r/m8
    

Вспоминаем, что означают условные знаки "/r", "rb","/0", "r8", "imm32", etc.

Обозначим, например, наличие ModR/M в команде как B_MODRM equ 01h, а наличие непосредственного однобайтного значения(imm8) как B_DATA8.

Так, для опкода 0x88 выставлено "/r" - это означает, что ModR/M задаёт оба операнда (регистр и r/m) - значит выставляем флаг B_MODRM. Улавливаешь мыслю? Еще пример: опкод 0xC6. Напомню: "/0" говорит о том, что поле Reg в ModR/M всегда равно 0. Видно, что здесь также присутствует ModR/M. Но, еще здесь есть "imm8" - значит опкод 0xC6 будет содержать следующие характеристики: B_MODRM+B_DATA8. И так далее.

3. РАЗБОР ModR/M.

Байт ModR/M несет информацию об операндах и режиме адресации и состоит из следующих полей:

Поле mod - определяет режим адресации и количество байтов поля СМЕЩЕНИЯ в команде. Поля Reg и R/M обычно служат для указания операндов. Следует уточнить, что в Reg для некоторых команд может кодироваться расширение опкода.

modaddress
00b[reg] - если reg==5, то сразу за modrm идет 32-битное смещение
- если reg==4, то сразу за modrm идет байт SIB
01b[reg+8-битное смещение] - если reg==4, то сразу за modrm следует байт SIB
10b[reg+32-битное смещение] - если reg==4, то сразу за modrm следует байт SIB
11breg

С префиксом 0x67 32-битное смещение превратится в 16-битное. Также при наличии 0x67, если mod=0, rm=6 (110b), то за modrm идет 16-битное смещение.

4. РАЗБОР SIB (Scale-Index-Base).

Этот байт используется для расширения возможности адресации операндов. Его присутствие можно определить из байта ModR/M (смотри выше, как). SIB состоит из 3-х полей:

С помощью байта SIB можно задавать выражения вида [reg1+reg2*scale+СМЕЩЕНИЕ]. Структура SIB:

Биты 6-7: scale

Биты 3-5: reg2 - если reg2 == 4, то reg2 не используется

Биты 0-2: reg1 - если reg1==5 и если mod в ModR/M равен 00, то за сразу SIB идет 32-битное смещение, иначе ebp

Важно: в 16-разрядном режиме (0x67) байта SIB нет!

5. СМЕЩЕНИЕ и НЕПОСРЕДСТВЕННЫЙ ОПЕРАНД.

Эти поля - 8, 16 или 32-разрядные числа. Разрядность определяется по характеристикам опкода + префиксам (0x67/0x66).

Также, хотелось бы черкануть пару слов о дизасмах в вирусах. Такую силу должен иметь каждый нормальный вирь:) Пока еще большинство прог (но не все!) юзают стандартные & fpu команды. Поэтому задумайся - так ли тебе нужны хери типа mmx, sse1/2/3? Дизассемблер должен быть быстр и предельно компактен - короче оптимизация во все щели. Но, как это часто случается, страдает либо морда, либо жопа. Компактен - не означает, что после написания движка к нему хер прикрутишь новые команды. Это важно. И естесственно, код должен быть базонезависимым.

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

Добавлю, что для написания своего дизасма в первую очередь нужно решить - для чего он тебе нужен? Ну а потом уже штудировать маны от Intel + сорцы других движков. Удачи!

P.S. Начать изучение сорсов можешь с моего простенького дизассемблера (новая ссылка).

Используемые ссылки:

	[email protected]
[Вернуться к списку] [Комментарии]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxheaven.org aka vx.netlux.org
deenesitfrplruua