Copy Link
Add to Bookmark
Report

451: LDIZX Disassembler

eZine's profile picture
Published in 
451
 · 2 years ago

LDIZX

v 1.01

The disassembler is an example of what is not easy to write right away. In the process of working with it, new needs for something appear that were not relevant yesterday, and you have to modify the disassembler for your needs again. Tomorrow, you may need an even more flexible system - and history will repeat again.

As you can understand, LDIZX is a disassembly engine and is the product of such a lack of demand.

Main characteristics:

  • Sufficiently complete information obtained at the output for the analysis / assembly of the command.
  • Use as just a length disassembler
  • Contains no data/absolute offsets
  • Used universal C call i.e. can be included in any HLL
  • Treats int 20h (0CD20h) as a 6-byte command, i.e. as VxDcall (win32-orientation). If it is necessary to transfer it somewhere else, then this is achieved by changing 1-5 lines in the source.

Description of functions

Because it uses C calls, here is a description of functions in C, although this is just a way of passing parameters to a procedure.

Tables are required for the engine to work. Unpacking procedure:

ldizx_init  ( VOID* TablePtr    // Kilobyte buffer for tables 
);


After executing this procedure, the tables of the engine, which are used during disassembly, are unpacked into memory at the address TablePtr.

Main function (actually disassembling code):

ldizx       ( VOID* InPtr,	// Pointer to input code 
CMD* OutPtr, // Pointer to CMD structure
VOID* TablePtr // Pointer for unpacked tables
)


After executing this procedure, the structure specified by the second parameter is filled based on the command that is being disassembled. On output, LDIZX passes the length of the command in EAX, or FFFFFFFFh on error.

If the disassembly requires no information about the instruction other than its length, then 0 is passed as the second parameter and LDIZX returns only the length of the instruction in EAX.

The procedure for assembling the cmd structure is not included in the command, because almost always requires a specific construction of the instruction or, conversely, very simple and it is better to write the assembly separately.


Structures and flags

The second parameter is passed to the engine as a pointer to the CMD structure, into which, after the execution of the main procedure, information about the command is written. Its format is presented below:

typedef struct { 

BYTE lc_size; // command length
BYTE lc_psize; // prefix length

DWORD lc_flags; // flags
BYTE lc_tttn; // tttn

BYTE lc_sib; // sib
BYTE lc_modrm; // modrm

BYTE lc_reg; // reg
BYTE lc_mod; // mod
BYTE lc_ro; // r/o

BYTE lc_rm; // r/m

BYTE lc_base; // base
BYTE lc_index; // index
BYTE lc_scale; // scale

DWORD lc_offset; // bias

BYTE lc_operand[6]; // operand

BYTE lc_soffset; // offset length
BYTE lc_soperand; // operand length

BYTE lc_mask1; // mask
BYTE lc_mask2; //
} cmd;


The content of the fields lc_sib, lc_modrm, lc_rm, lc_ro, lc_base, lc_index, lc_scale I think is clear from their name, I note that the mod field is given shifted by 6 bits to the left. it is often shifted in order to restore the team and it would be reasonable to leave it as it is in the team (i.e. occupying the 6th and 7th bits).

The operand is 6 bytes given commands such as JMP FAR/CALL FAR where there is both a selector and an offset, so it is written in the order of its bytes in the instruction. when referring to the lc_operand field, it is necessary to take into account the size of the operand contained in the lc_soperand field.

tttn is meaningful in conditional execution commands (JCC/SETCC, etc.), for which this field contains the command execution condition.

You should look at lc_reg only when the command does not have modr / m and works with a register. The content of this field determines the register used.

The length of the prefixes is given in order to identify non-standard commands in which there are several identical prefixes.

The lc_mask1 and lc_mask2 fields contain the command mask. Moreover, it is given in full, including 0F, if the command has this prefix. This field is useful when you need to find a specific command by its mask. the engine will determine the command mask itself and enter it into these fields.

The most important information about the command contains flags (lc_flags), depending on their content, most of the fields in the CMD structure are interpreted, so you should first of all look at the flags, and only then at the contents of the fields.

LF_PCS		0x00000001	Prefix present CS 
LF_PDS 0x00000002 Prefix present DS
LF_PES 0x00000004 Prefix present ES
LF_PSS 0x00000008 Prefix present SS
LF_PFS 0x00000010 Prefix present FS
LF_PGS 0x00000020 Prefix present GS
LF_POP 0x00000040 There is a prefix for replacing the bitness of the operand
LF_POF 0x00000080 There is a prefix for replacing the bitness of the address
LF_PLOCK 0x00000100 Prefix present LOCK
LF_PREPZ 0x00000200 Prefix present REPZ
LF_PREPNZ 0x00000400 Prefix present REPNZ
LF_MODRM 0x80000000 Present modr/m
LF_SIB 0x40000000 Present sib
LF_OFFSET 0x20000000 Offset present
LF_OPERAND 0x10000000 Operand present
LF_REG 0x08000000 Present reg (команда без modr/m)
LF_REG1 0x04000000 R/m is a register and makes sense
LF_REG2 0x02000000 R/o is a register and makes sense
LF_BASE 0x01000000 The base in sib is present and has a value
LF_BASE 0x00800000 The index in sib is present and has a value
LF_MEM 0x00400000 The team works with memory (т.е. mod <> 11b )
LF_TTTN 0x00200000 Present tttn
LF_RAW 0x00100000 cmd does not contain complete information
LF_D 0x00008000 В opcode present d
LF_S 0x00004000 В opcode present s
LF_SDV 0x00002000 s or d is 1 (depending on the value of LF_S and LF_S)
LF_W 0x00001000 В opcode present w
LF_WV 0x00000800 w equals 1


Flags LF_REG1, LF_REG2, LF_BASE, LF_INDEX indicate that r/m, r/o, base and index are respectively registers. this means that they must be treated as registers. This is done in order to separate commands that do not use these fields as registers. When directly addressing a command like:

   mov [12345678h],edx


They have in r / m 101b i.e. the ebp register and, in theory, indirect addressing should be used, but according to the logic of the command, there is no register. This is a feature of the command structure. It's the same with the base, index. In r / o there may not be a register, but a code specifying the operation. Therefore, these flags are introduced.


original russian text


LDIZX

v 1.01

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

Как можно понять LDIZX - дизассемблирующий движок и является продуктом такой вот невостребованности.

Основные характеристики :

  • Достаточно полная информация ,получаемая на выходе для анализа/сборки команды.
  • Использование в качестве только лишь дизассемблера длин
  • Не содержит данных/абсолютных смещений
  • Использован универсальный C call т.е. может быть включен в какой-нибудь HLL
  • Обрабатывает int 20h (0CD20h) как 6-ти байтную команду, т.е. как VxDcall (win32-ориентация).Если необходимо перенести его куда-нибудь еще ,то это достигается изменением 1-5 строк в исходнике.

Описание функций

Т.к. использован C call,то тут дано описание ф-ций на C , хотя это просто способ передачи параметров процедуре.

Для работы движка необходимы таблицы. Процедура их распаковки:

ldizx_init  ( VOID* TablePtr    // Килобайтный буфер для таблиц 
);


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

Главная функция (собственно дизассемблирующая код):

ldizx    ( VOID* InPtr,	     // Поинтер на входной код 
CMD* OutPtr, // Поинтер на структуру CMD
VOID* TablePtr // Поинтер на распакованые таблицы
)


После выполнения данной процедуры структура ,заданная вторым параметром заполняется исходя из команды ,которая дизассемблируется. На выходе LDIZX передает длину команды в EAX либо FFFFFFFFh в случае ошибки.

Если при дизассемблировании не требуется информация о команде ,кроме ее длины, то в качестве второго параметра передается 0 и LDIZX возвращает лишь длину команды в EAX.

Процедура ассемблирования структуры cmd в команду не включена, т.к. почти всегда требуется специфичное конструирование команды или, наоборот, очень простое и лучше писать ассемблирование отдельно.


Структуры и флаги

Вторым параметром в движок передается поинтер на структуру CMD ,в которую после выполнения главной процедуры, записывается информация о команде.Ее формат представлен ниже:

typedef struct { 

BYTE lc_size; // длина команды
BYTE lc_psize; // длина префиксов

DWORD lc_flags; // флаги
BYTE lc_tttn; // tttn

BYTE lc_sib; // sib
BYTE lc_modrm; // modrm

BYTE lc_reg; // reg
BYTE lc_mod; // mod
BYTE lc_ro; // r/o

BYTE lc_rm; // r/m

BYTE lc_base; // base
BYTE lc_index; // index
BYTE lc_scale; // scale

DWORD lc_offset; // смещение

BYTE lc_operand[6]; // операнд

BYTE lc_soffset; // длина смещения
BYTE lc_soperand; // длина операнда

BYTE lc_mask1; // маска
BYTE lc_mask2; //
} cmd;


Содержание полей lc_sib,lc_modrm,lc_rm,lc_ro,lc_base,lc_index,lc_scale думаю понятно из их названия , замечу ,что поле mod дано сдвинутым на 6 бит влево т.к. его часто с целью восстановления команды так сдвигают и было бы разумно его таким оставить, каким оно есть в команде (т.е. занимающем 6-й и 7-й биты)

Операнд является 6-ти байтовым с учетом таких команд, как JMP FAR/CALL FAR, где есть и селектор и смещение, поэтому он пишется в порядке следования его байтов в команде.Т.е. необходимо при обращении к полю lc_operand учитывать размер операнда ,содержащийся в поле lc_soperand.

tttn имеет смысл в командах условного выполнения (JCC/SETCC и т.д.), для них это поле содержит условие выполнения команды.

На lc_reg стоит смотреть только тогда, когда команда не имеет modr/m и работает с регистром.Содержание этого поля и определяет использованный регистр.

Длина префиксов дана с целью выявления нестандартных команд,в которых по нескольку одинаковых префиксов.

В полях lc_mask1 и lc_mask2 содержится маска команды. Причем она дается в полном виде включая 0F, если этот префикс у команды есть.Это поле полезно, когда надо найти определенную команду по ее маске.Т.е. движок сам определит маску команды и занесет ее в эти поля.

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

LF_PCS		0x00000001	Присутствует префикс CS 
LF_PDS 0x00000002 Присутствует префикс DS
LF_PES 0x00000004 Присутствует префикс ES
LF_PSS 0x00000008 Присутствует префикс SS
LF_PFS 0x00000010 Присутствует префикс FS
LF_PGS 0x00000020 Присутствует префикс GS
LF_POP 0x00000040 Присутствует префикс замены разрядности операнда
LF_POF 0x00000080 Присутствует префикс замены разрядности адреса
LF_PLOCK 0x00000100 Присутствует префикс LOCK
LF_PREPZ 0x00000200 Присутствует префикс REPZ
LF_PREPNZ 0x00000400 Присутствует префикс REPNZ
LF_MODRM 0x80000000 Присутствует modr/m
LF_SIB 0x40000000 Присутствует sib
LF_OFFSET 0x20000000 Присутствует смещение
LF_OPERAND 0x10000000 Присутствует операнд
LF_REG 0x08000000 Присутствует reg (команда без modr/m)
LF_REG1 0x04000000 R/m является регистром и имеет смысл
LF_REG2 0x02000000 R/o является регистром и имеет смысл
LF_BASE 0x01000000 База в sib присутствует и имеет значение
LF_BASE 0x00800000 Индекс в sib присутствует и имеет значение
LF_MEM 0x00400000 Команда работает с памятью (т.е. mod <> 11b )
LF_TTTN 0x00200000 Присутствует tttn
LF_RAW 0x00100000 cmd не содержит полной информации
LF_D 0x00008000 В опкоде присутствует d
LF_S 0x00004000 В опкоде присутствует s
LF_SDV 0x00002000 s либо d равен 1 (в зависимосити от флагов LF_S и LF_S)
LF_W 0x00001000 В опкоде присутствует w
LF_WV 0x00000800 w равен 1


Флаги LF_REG1,LF_REG2,LF_BASE,LF_INDEX показывают ,что r/m,r/o, base и index соответственно являются регистрами.Т.е. это значит ,что их необходимо трактовать как регистры .Это сделано для того , чтобы отделить команды ,не использующие эти поля как регистры.При прямой адресации команды типа:

   mov [12345678h],edx


Имеют в r/m 101b т.е.регистр ebp и по идее должна быть использована косвенная адресация, но по логике работы команды нет никакого регистра т.к. это особенность строения команд. Тоже самое и с базой, индексом.В r/o может быть и не регистр ,а уточняющий операцию код.Поэтому и введены эти флаги.

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT