Hacker Disassembler Engine 64 C 0.04 FINAL ========================================== 1. Что это такое? 2. Как использовать? 3. Неправильные инструкции 4. Контакты Прежде, чем использовать HDE64, обязательно прочти данное руководство, чтобы потом не было неприятных неожиданностей. Предполагается, что читатель хотя бы немного разбирается в процессорах x86-64 и под рукой у него есть Интеловские мануалы или другие справочники. Руководство написано в разговорном стиле, слова "инструкция" и "каманда" я употреблял как синонимы. Для ассемблерных мнемоник использовался intel-синтаксис (как в nasm), прототипы и определения записаны в Си-нотации. 1. Что это такое? ================= Небольшой дизассемблерный движок, предназначенный для анализа x86-64 кода. HDE64 получает информацию о команде (длина, префиксы, опкод, ModR/M,..), а также анализирует ее на вилидность. Движок может пригодиться, например, при работе с исполняемыми файлами, написании вирусов, когда простого дизассемблера длин недостаточно, а большой дизассемблер тащить тяжело, к тому же большинство из них служат лишь для получения человекочитаемой мнемоники. HDE64 предназначен для "внутреннего" анализа, поэтому поддержки мнемоник в нем принципиально никогда не будет. Из-за этого он очень мал, но получает достаточно информации для анализа. + поддержка General-Purpose, FPU, MMX, SSE-SSE3, 3DNow! инструкций + высокая скорость и маленький размер (~ 2.5 кб) + не привязан ни к какой ОС 2. Как использовать? ==================== Для дизассемблирования команды необходимо вызвать функцию hde64_disasm, первый аргумент -- указатель на код, второй -- указатель на структуру hde64s: unsigned int hde64_disasm(const void *code, hde64s *hs); Аргументы следует передавать в C конвенции. Функция возвращает длину команды, а также заполняет структуру "hde64s": typedef struct { uint8_t len; // длина команды uint8_t p_rep; // префикс rep/repz (0xf3) или repnz (0xf2) uint8_t p_lock; // префикс lock: 0xf0 uint8_t p_seg; // сегментный префикс: 0x26,0x2e,0x36,0x3e,0x64,0x65 uint8_t p_66; // префикс переопределения размера операнда: 0x66 uint8_t p_67; // префикс переопределения размера адреса: 0x67 uint8_t rex; // префикс REX uint8_t rex_w; // REX.W uint8_t rex_r; // REX.R uint8_t rex_x; // REX.X uint8_t rex_b; // REX.B uint8_t opcode; // опкод uint8_t opcode2; // второй опкод (если первый 0x0f) uint8_t modrm; // ModR/M байт uint8_t modrm_mod; // ModR/M.mod uint8_t modrm_reg; // ModR/M.reg uint8_t modrm_rm; // ModR/M.r/m uint8_t sib; // SIB байт uint8_t sib_scale; // SIB.scale uint8_t sib_index; // SIB.index uint8_t sib_base; // SIB.base union { uint8_t imm8; // непосредственное значение imm8 uint16_t imm16; // непосредственное значение imm16 uint32_t imm32; // непосредственное значение imm32 uint64_t imm64; // непосредственное значение imm64 } imm; union { uint8_t disp8; // смещение disp8 uint16_t disp16; // смещение disp16 uint32_t disp32; // смещение disp32 } disp; uint32_t flags; // флаги } hde32s; Частным случаем непосредственного значения может быть относительный адрес. В предыдущих версиях движка в структуре для них были выделены специальные поля hde64s.rel*, но теперь они записываются в свое законное место -- hde64s.imm*, а отличить их можно по флагу F_RELATIVE, который устанавливается вместе с одним из флагов F_IMM* (см. ниже). Выравнивание структуры "hde64s" равно 1 байту, т.е. его нет. Помните это при работе с компиляторами, которые любят выранивать данные по кратным адресам. Рекомендую брать хидеры из этого дистрибутива, там все настроено. Поля "hde64s.opcode" и "hde64s.len" заполняются всегда. Присутствие остальных полей следует определять по следущим флагам поля "hde64s.flags": #define F_MODRM 0x00000001 // присутствует ModR/M #define F_SIB 0x00000002 // присутствует SIB #define F_IMM8 0x00000004 // присутствует значение imm8 #define F_IMM16 0x00000008 // присутствует значение imm16 #define F_IMM32 0x00000010 // присутствует значение imm32 #define F_IMM64 0x00000020 // присутствует значение imm64 #define F_DISP8 0x00000040 // присутствует смещение disp8 #define F_DISP16 0x00000080 // присутствует смещение disp16 #define F_DISP32 0x00000100 // присутствует смещение disp32 #define F_RELATIVE 0x00000200 // imm* является относительным адресом #define F_PREFIX_REPNZ 0x01000000 // присутствует префикс repnz #define F_PREFIX_REPX 0x02000000 // присутствует префикс rep(z) #define F_PREFIX_REP 0x03000000 // присутствует префикс rep(z) или repnz #define F_PREFIX_66 0x04000000 // присутствует префикс 0x66 #define F_PREFIX_67 0x08000000 // присутствует префикс 0x67 #define F_PREFIX_LOCK 0x10000000 // присутствует префикс lock #define F_PREFIX_SEG 0x20000000 // присутствует префикс сегмента #define F_PREFIX_REX 0x40000000 // присутствует префикс REX #define F_PREFIX_ANY 0x7f000000 // присутствует любой префикс Движок гарантирует, что он прочитает, начиная с места в памяти, на которое указывает "const void *code", не более 26 байт. Причем если инструкция правильная (см. п.3), то движок прочитает не более "hde64s.len" байт, которое, в свою очередь, не больше 15. 3. Неправильные инструкции ========================== HDE64C анализирует команды на правильность, причем достаточно тщательно, а не просто беглой проверкой опкода и длины (более половины размера движка занимает код и таблицы для определения ошибочных инструкций). Если HDE64C считает команду ошибочной, он устанавливает флаг "F_ERROR" в поле флагов: #define F_ERROR 0x00001000 // инструкция неправильная Кроме того, устанавливаются следущие флаги, которые немного проясняют причины установки флага ошибки: #define F_ERROR_OPCODE 0x00002000 // неправильный опкод #define F_ERROR_LENGTH 0x00004000 // длина инструкции больше 15 байт #define F_ERROR_LOCK 0x00008000 // префикс lock недопустим #define F_ERROR_OPERAND 0x00010000 // недопустимый операнд В случае флага "F_ERROR_OPCODE" под опкодом понимается не только байт, который записан в "hde64s.opcode(2)" но и его расширение за счет ModR/M.reg, всего ModR/M или префикса (для двухбайтовых опкодов), так что команды типа "c6 c8 00" будут верно определяться как ошибочные, т.к. опкод "c6 /1" ошибочный. Если движок установил флаг "F_ERROR_LENGTH", то в качестве длины команды он вернет 15, т.о. максимальное значение поля "hde64s.len" равно 15. Если движок определил команду как ошибочную, то дизассемблирование на этом не останавливается, а продолжается по общим правилам, просто ко всему прочему будет установлен флаг ошибки (F_ERROR) и причины ошибки (F_ERROR_*). 4. Контакты =========== Автор: Вячеслав Патьков E-mail: patkov-mail на mail.ru WWW: http://hde32.narod.ru/ Пожалуйста, пишите письма на русском или английском языке, в обычном текстовом виде, не надо мне слать красивые письма в HTML или (еще хуже) в DOC. P. S. Мне скучно с HDE64. Поэтому, скорее всего, эта версия последняя. И не исключено, что есть баги (но пока я ни одного сообщения о них не получал, и мои тесты движок прошёл). Если ВЫ хотите развивать HDE64 -- fork'айте на здоровье.