Copy Link
Add to Bookmark
Report

451: Long Time Mutation Engine (LTME)

eZine's profile picture
Published in 
451
 · 27 Mar 2022

When we talk about permutation, we usually mention the permuting/mutating engine. This is our goal. With permutation, the appearance of the code changes and its analysis is also difficult. After disassembling, the source code becomes almost unreadable.

The permutation engine must somehow know about the type of code being permuted. Without this information, it is impossible to correctly change the code. The options for providing this information can be different: a table of commands (here, you can represent commands abstractly and make a table of command replacements when mutated), then you need to describe a table element for each command of the permuted code, which is irrational. The most universal solution seems to disassembly the code into some kind of peudo-code, permutate it and reassembling it back into commands.

Because the mutating engine must disassemble the code, and this process is not simple, then it is necessary to somehow simplify this process. Therefore, you should refuse to use data in the code. In this case the code can be disassembled easily: one command after another, without referring to any jumps.

In addition, when the code changes, the addresses of the variables it uses will also change, and the use of data in such a permutable code is not applicable, the offsets of procedures / commands also change, and the code should not use absolute offsets.

The first step in permutation is disassembly. Commands are disassembled, and the result of this operation is used at the list generation stage. Next, you need to adjust all JMPS, LOOP, JECXZ / JCXZ, JCCS to equivalent instructions using the maximum operand (dward), because the above use a short operand (+-127 bytes), and when changing commands, it is not a fact that this gap will be enough for subsequent linking.

After that, jump commands such as JMP, CALL, etc. are mutually linked to the labels they point to so that they can be linked together later. Commands that point to external labels require special mutation, but often they are simply not allowed because it is not safe to use them.

After that, you can start changing the code.


Logical Permutations (LP)

Logical permutations are just a permutation of adjacent commands among themselves. A couple of commands are taken and the permutation conditions are looked at, if they are met, then LP occurs. This is one of the best ways of mutation. The permuted commands will give a new code that is equivalent in program logic (hence the name "logical permutation"), but with a changed crc and, which is nice, it is impossible to determine the initial position of the commands after this operation. In total, if there is a group of n commands that is subjected to LP, then maybe n! options for their position. You can only go through the combinations and count them crc, or intelligently proxify them all modulo and consider this as the crc of the group, there can be many such groups and, accordingly, many signatures will be required.

Not all commands can be logically rearranged. The main conditions are that these commands are of the same type and that they do not use each other's work. It is worth noting that here you will either have to work with the opcode table, which contains the type of operation of the command (absolute change, reversible change, irreversible change, etc.), or work with only a few groups of specific commands.

LP example:

  mov eax,[ebx]           ;\ 
mov esi,[ebp+12345678h] ; } These commands are subject to LP because they are not
mov ecx,[esi] ;/ use each other's work.
|
|
+------------------+-------+------------------+-------+
| | | | |
mov ecx,[esi] | mov eax,[ebx] | mov esi,[ebp+12345678h]
mov eax,[ebx] | mov ecx,[esi] | mov ecx,[esi]
mov esi,[ebp+12345678h] | mov esi,[ebp+12345678h] | mov eax,[ebx]
| |
| |
mov ecx,[esi] mov esi,[ebp+12345678h]
mov esi,[ebp+12345678h] mov eax,[ebx]
mov eax,[ebx] mov ecx,[esi]


Here is another group of commands and its LP, where it gives a completely non-equivalent code, since the second command uses the results of the first one:

   add ebx,[ecx+40]    add acx,[ebx] 
add acx,[ebx] add ebx,[ecx+40]


An example of commands that do not explicitly use each other's results, but perform operations of different types, which leads to non-equivalence after LP:

   in al, 666        ; absolute change 
add al,15 ; reversible change


Something similar to:

   sub ebx,00345678h   ; reversible change 
and ebx,00000001h ; irreversible change


Since the commands with the same opcode are mostly rearranged, it is not necessary to pay attention to the change in the flags, otherwise, if a comparison of the types of changes is used, this must also be taken into account.


Physical permutations (FP)

Logical permutations are not always applicable, but physical permutations are still used. CALL modifies the stack and this is not good, although it is possible.

Here is an example of FP using insert:

     ...                                  	... 

mov edx,offset HELLoWORLD; (*0) mov edx,offset HELLoWORLD;(*0)
push edx ; (*1) jmp x1
push size hello ; (*1) call print ; (*3)
call print ; (*3) x2:
...
... jmp x3
x1:
(*2) push edx ; (*1)
push size hello ; (*1)
jmp x2
x3:
(*2)
...


As you can see, part of the entire command block (*1) was cut and pasted between part (*3) and (*2), then the code was connected using JMPs in the order in which it appeared in the unmutated block.

Command Mutation

Mutation of commands implies the replacement of a command with an equivalent command or group of commands:

   lea ebx,[esi+edx*2] -> mov ebx,esi 
lea ebx,[ebx+edx*2]


This is quite simple, but you should be aware of the flags as by replacing the command, you can influence its action on the flags, thereby making the result unpredictable. This is followed by work with the stack, here you either need to refuse to work with ESP, as with a temporary register, or do not mutate the commands working with ESP.

Each command has its own principle of operation, therefore, it is not necessary to replace all commands universally, based on the opcode, but you have to write something specially to replace individual commands.

Some kind of mutation is a change in the instructions of the conditional jump type JCC. Here the condition is reversed and, following the command, JMP is placed on the label, to which, in theory, control should have been transferred if the condition (before the mutation) was fulfilled. The JCC label should now point to the command after the JMP:

        	before 			after 

... ...
jnc a jc b
jmp a
... b: ...
a:
a: ...


Trash

In order to change the checksums of the code and keep it in working condition, junk commands are also used that do not affect the operation of the program. When "diluting" the original code with garbage, it becomes difficult to analyze and perceive it, but adding garbage to the code significantly reduces the speed of code execution and it can run very slowly. Here you should either remove garbage before mutation, if possible, and then add it in a new way, or it is very rare to generate it i.e. with low probability. With the first option, a situation may arise when the garbage has been mutated and it cannot be so easily determined. Therefore, the mutator must be called before garbage is generated, but then the degree of garbage modification decreases.

Because code can use flags, then garbage should not change them, or at least not affect the logic of the code, this can be done by adding garbage commands that change flags before commands that also change flags.


Implementation

Let's clearly divide the functions of the permuting engine into parts. it rarely changes and should always be present. Consider what this part should do.

When disassembling, a doubly linked list performs well, which consists of elements like this

 struct list{ 
DWORD next;
DWORDprev;
DWORD link;
DWORD label;
used_data struct;
}


Where next is a pointer to the next element, prev is a pointer to the previous one. These pointers can be either an index in the array or directly the address of the element, which increases the speed of working with the list.

To indicate the end and beginning of the list, you can enter 0 in the next and prev fields of the last and first element, respectively, or you can create somewhere a couple of pointers pointing to the first and last elements, which also increases the speed when inserting new elements.

link - the number of the label to which the command is linked.
label - a unique number of the command's label, and the values ​​of link and label are not serial numbers of commands in the list, but are determined in some other way. Let's say 0 is a sign of the absence of a label, and the numbers 1-n determine its number. Otherwise, if the number of the element in the list determined the label, then when inserting a new command, the entire list would have to be restructured.

An example of the described list:

	+----------+   +------------+   +------------+ 
| next-----> | next-------> | next-------> ...
... <------prev |<------prev |<------ prev |
| link:33 -+ | link:0 | | link:0 |
| label:0| | | label:0 | | label:33|
+----------+ | +------------+ +------|-----+
+-------------------------+


Then the mutator is called and after that the entire list is reassembled back into code, taking into account the contents of the link/label fields.

The user_data structure contains information about the command (the command itself, opcode, operand, flags, etc.) and is needed for mutation and analysis. LTME uses a structure that is about 60 bytes in size.


LTME

As you might guess, LTME is a permutation engine that implements almost everything described above. It consists of 2 parts - core and mutator.

Core disassembles the code, converts jmps/jccs to their 4-byte operand counterparts, binds jump instructions and labels, and then calls the mutator.
This scheme allows the use of various mutators, which is actually very important in the development of a permuting virus. a specific mutator is required for each individual virus, which "knows" about the storage location of the information necessary for the virus to work. Another case is the use of the engine while working on the intercepted ring0 functions, speed is important there, so it is advisable to use a simpler mutator and it is connected very simply, without interfering with the work of the core

Core is always used, it is something like a shell that offers its own interface, and the method of mutating commands is a user process.

Core call (All procedures use C call) :

 VOID* __cdecl ltme_core( 
VOID* ibuf, // input buffer with
// code
VOID* *malloc(
DWORD cnt // number of bytes
),

VOID free (
VOID* buffer // pointer to buffer
),
DWORD csize, // code size
VOID* mutator (
// mutator
// .....
),

DWORD*dasm(
VOID* dasm_ibuf, // input buffer
CMD* dasm_obuf, // pointer to cmd
VOID* dasm_table // pointer to
// tables
)

VOID* dasm_tables, // tables
// disassembler

DWORD flags, // flags
DWORD* params, // pointer to
// parameters
DWORD* rnd (
DWORD* seed, // pointer to seed
DWORD range // limit
)
DWORD* seed // pointer to seed
);


Pointers to malloc/free functions must point to the memory allocation/freeing procedure.

Dasm is a pointer to a disassembler that returns information about the command in the cmd structure:

 typedef struct  { 

BYTE lc_size; // command length
BYTE lc_psize; // length of prefixes

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; // offset

BYTE lc_operand[6]; // operand

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

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


rnd - a pointer to some RNG with the specified parameters.

The params field is a pointer to a structure that does not have a well-defined format, only the first 3 fields are static and are intended for use by core and mutator. Because parameters are passed to the mutator, then the rest of the space in the parameters can be used for the purposes of the custom mutator.

If an error occurs while core is running, then FFFFFFF is returned in EAX. Otherwise - 0, the size and offset of the permuted code in the parameters. The resulting buffer with the code, after use, must be freed through the free function passed to the engine.

 typedef struct { 

DWORD build_size, // length
// received code

DWORD build_offset, // buffer pointer
// with the received code

DWORD mixer_maxswp, // number of blocks
// for FP (max)
} ltmeparam;


The higher the value of mixer_maxswp , the more "mixed" the code will be and the less fast it will be.

mutator - a pointer to the mutation procedure. In principle, you should write your own mutator, inserting a call to the standard one into it, thereby expanding its capabilities:

 VOID* __cdecl ltme_mutator( 
DWORD csize, // code size
VOID* list, // pointer to
// list

DWORD lastlabelPtr, // last
// used
// label

DWORD flags, // flags
DWORD* params, // pointer to
// parameters

DWORD* rnd (
DWORD* seed, // pointer to seed
DWORD range // limit
)
DWORD* seed // pointer to seed
VOID* *malloc(
DWORD cnt // number of bytes
),

VOID free (
VOID* buffer // pointer to buffer
)
)


During operation, LTME allocates a buffer to organize the list/tables, this may require at least 60*number_of_command bytes plus memory for tables.

list is a pointer to a list generated by core , with 3 yards at the beginning:

  • pointer to the first element of the list
  • pointer to the last element of the list
  • pointer to a memory area available for writing a new element of the list into it.

Next comes the list itself, consisting of elements of the one structure:

 typedef struct { 
DWORD one_next,
DWORD one_prev,
ltmedata one_data
} one;

typedef struct {

cmd ltmed_desc,
BYTE ltmed_command[23],
DWORD ltmed_link,
DWORD ltmed_label,
DWORD ltmed_flags

} ltmedata;


There are several procedures for working with a list located in core\list.inc and mutator\list.inc:

 List initialization: 
*list_init( EBX=memory pointer for list)

Adding an element to the list:
*list_add( EBX=list,
EDX=number of the element to be followed by the inclusion
(FFFFFFFFh if after the last, 0 - before the first)
EDI=pointer to ltmedata to include)

Removing an element from the list:
*list_kill( EBX=list,
EDX=number of the element for which it will be deleted
(FFFFFFFFh if after last, 1 - first))

Finding the address of an element:
*list_get( EBX=list,
EDX=number of element whose address is to be found
(1<->FFFFFFFF))
EAX = element address.

Swapping element values:
*list_swap( EBX=list,
EDX = element number #1,
ECX = item number #2)

Getting the number of elements in a list:
*list_getmax( EBX=list)
EAX = number of elements.


If an element has the LTMED_EXTERNAL flag in the ltmed_flags field, then this element has a reference outside the code in question.

Flags

As flags, a combination of the following bits is passed to core and mutator, which determine the operation of the engine. Of course, you can also pass your own flags to the mutator, for example, when expanding its standard features:

LTMEF_MSTACK	00000001b    Use commands that work with the stack 
LTMEF_GARBAGE 00000010b generate garbage
LTMEF_PSWAP 00000100b Use FP
LTMEF_LSWAP 00001000b Use LP
LTMEF_JCC 00010000b Change JCC Terms
LTMEF_CMD 00100000b Mutate commands
LTMEF_ALL 00111111b Use everything ;)


Remarks

The standard mutator is designed to be called only once. does not modify the data required for another such mutation (i.e. does not change the cmd structure of mutated commands).

And if you want the command not to be mutated, then you should set the LF_RAW bit in the ltmed_desc.lc_flags flags, in which case it is considered that the command is not fully disassembled and it is not mutated.

original russian text

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

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

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

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

Первым шагом при пермутации и является дизассемблирование .Команды дизассемблируются ,а результат этой операции используется на этапе формирования списка. Далее необходимо скорректировать все JMPS,LOOP, JECXZ/JCXZ,JCCS на эквивалентные команды ,использующие максимальный операнд (дворд) ,т.к. вышеперечисленные используют короткий операнд (+-127 байт), а при изменении команд не факт,что этого промежутка хватит для последующей линковки.

После этого команды перехода ,такие ка JMP,CALL и т.д. взаимнолинкуются с метками ,на которые они указывают,чтобы можно было потом снова их связать вместе.Команды, указывающие на внешние метки требуют особой оговоренности, часто их просто недопускают т.к. использовать их ненадежно.

После этого можно начинать изменение кода.


Логические перестановки (ЛП)

Логические перестановки это всего лишь перестановка рядом стоящих команд между собой.Т.е. берется пара команд и смотрятся условия перестановки,если они выполняются,то ЛП происходит.Это один из наилучших способов мутации. Переставленные команды дадут новый код ,эквивалентный по логике программы (отсюда и название "логическая перестановка"), но с измененным crc и ,что приятно, после этой операции определить первоначальное положение команд невозможно.Всего если есть группа из n команд,которая подвергается ЛП ,то может быть n! вариантов их положения.Можно лишь перебирать комбинации и считать их crc, либо разумно проксорить их всех по некому модулю и считать это за crc группы ,таких групп может быть много и потребуется соответственно много сигнатур.

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

Пример ЛП:

  mov eax,[ebx]             ;\ 
mov esi,[ebp+12345678h] ; } Эти команды подвержены ЛП т.к. они не
mov ecx,[esi] ;/ используют результаты работы друг друга.
|
|
+------------------+-------+------------------+-------+
| | | | |
mov ecx,[esi] | mov eax,[ebx] | mov esi,[ebp+12345678h]
mov eax,[ebx] | mov ecx,[esi] | mov ecx,[esi]
mov esi,[ebp+12345678h] | mov esi,[ebp+12345678h] | mov eax,[ebx]
| |
| |
mov ecx,[esi] mov esi,[ebp+12345678h]
mov esi,[ebp+12345678h] mov eax,[ebx]
mov eax,[ebx] mov ecx,[esi]


Вот другая группа команд и ее ЛП,где она дает совсем не эквивалентный код т.к. вторая команда использует результаты первой :

   add ebx,[ecx+40]          add acx,[ebx] 
add acx,[ebx] add ebx,[ecx+40]


Пример команд ,которые не используют результаты друг друга в явном виде ,но выполняют операции разного типа,что и приводит к неэквивалентности после ЛП:

   in al, 666  	    	; абсолютное изменение 
add al,15 ; обратимое изменение


Что-то похожее:

   sub ebx,00345678h	; обратимое изменение 
and ebx,00000001h ; необратимое изменение


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


Физические перестановки (ФП)

Логические перестановки применимы не всегда ,но еще используются физические перестановки.Смысл ФП состоит в обмене частей кода между собой или вставкой одних частей в другие и последующем связывании команд командами перехода, чаще всего JMP'ами т.к. CALL модифицирует стек и это не хорошо,хотя и можно.

Вот пример ФП с помощью вставки:

     ...                                  	... 

mov edx,offset HELLoWORLD; (*0) mov edx,offset HELLoWORLD;(*0)
push edx ; (*1) jmp x1
push size hello ; (*1) call print ; (*3)
call print ; (*3) x2:
...
... jmp x3
x1:
(*2) push edx ; (*1)
push size hello ; (*1)
jmp x2
x3:
(*2)
...


Как видно часть всего блока команд (*1) была вырезана и вставлена между частью (*3) и (*2), далее код был связан с помощью JMP'ов в порядке своего следования в немутированом блоке.


Мутирование команд

Мутирование команд подразумевает под собой замену команды на эквивалентную ей команду или группу команд:

   lea ebx,[esi+edx*2]      ->    mov ebx,esi 
lea ebx,[ebx+edx*2]


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

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

Некоторой разновидностью мутации является изменение команд условного перехода типа JCC. Тут условие меняется на противоположное и следом за командой ставится JMP на метку, на которую по идее должно было передаться управление, если бы условие (до мутации) выполнялось. Метка JCC же должна теперь указывать на команду после JMP:

        	до                     	после 

... ...
jnc a jc b
jmp a
... b: ...
a:
a: ...

Мусор

Для того ,чтобы изменить чексуммы кода и оставить его в рабочем состоянии также применяются мусорные команды , не влияющие на работу программы. При "разбавлении" оригинального кода мусором затрудняется его анализ и восприятие,но добавление мусора в код заметно снижает скорость выполнения кода и он может выполняться очень медленно .Тут следует либо убирать мусор перед мутацией, если это возможно, и затем добавлять его по новому, либо очень редко его генерировать т.е. с малой вероятностью. При первом варианте может возникнуть такая ситуация,когда мусор был промутирован и его нельзя так просто определить.Следовательно мутатор надо вызывать до генерирования мусора,но тогда снижается степень видоизмененности мусора.

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


Реализация

Четко разделим функции пермутирующего движка на части.Целесообразно вынести функции дизассемблирования / линковки и др. в отдельную часть т.к. она редко подвергается изменениям и должна присутствовать всегда.Рассмотрим что должна сделать эта часть.

При дизассемблировании хорошо себя проявляет двусвязный список,который состоит из элементов, подобных этому

 struct	 list{ 
DWORD next;
DWORD prev;
DWORD link;
DWORD label;
used_data struct;
}


Где next - это указатель на следующий элемент, prev - на предыдущий. В качестве этих указателей может быть как индекс в массиве, так и непосредственно адрес элемента, что увеличивает скорость работы со списком.

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

link - номер метки, с которой связана команда.
label - уникальный номер метки команды, причем значения link и label не являются порядковыми номерами команд в списке, а определяются неким другим способом. Допустим 0 - это признак отсутсвия метки,а номера 1-n определяют ее номер. Иначе если бы номер элемента в списке определял метку, то при вставке новой команды, пришлось бы реструктуризовать весь список.

Пример описанного списка:

	+----------+   +------------+   +------------+ 
| next-----> | next-------> | next-------> ...
... <------prev |<------prev |<------ prev |
| link:33 -+ | link:0 | | link:0 |
| label:0| | | label:0 | | label:33|
+----------+ | +------------+ +------|-----+
+-------------------------+


Затем вызывается мутатор и после этого весь список реассемблируется обратно в код с учетом содержания полей link/label.

Структура user_data содержит информацию о команде (саму команду, опкод, операнд, флаги и т.д.) и нужна для мутации и анализа. LTME использует структуру, размер которой составляет что-то около 60-ти байт.


LTME

Как можно догадаться LTME - пермутирующий движок,реализующий почти все описаное выше.Он состоит из 2-х частей - core и mutator.

Core дизассемблирует код,преобразует jmps/jccs в их аналоги с 4- байтовым операндом,связывет команды перехода и метки, после этого вызывается мутатор.
Эта схема позволяет использовать различные мутаторы ,что собственно очень важно при разработке пермутирующего вируса т.к. требуется специфический мутатор для каждого отдельного вируса, который "знает" о месте хранения, необходимой для работы вируса, информации. Другой случай - использование движка во время работы на перехватываемых функциях ring0,там важна скорость, поэтому целесообразно использовать более простой мутатор и подключается он очень просто, без вмешательства в работу core

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

Вызов Core (Во всех процедурах используется C call) :

 VOID*	__cdecl	ltme_core( 
VOID* ibuf, // входной буфер с
// кодом
VOID* *malloc (
DWORD cnt // количество байт
),

VOID* free (
VOID* bufer // указатель на буфер
),
DWORD csize, // размер кода
VOID* mutator (
// мутатор
// .....
),

DWORD* dasm (
VOID* dasm_ibuf,// входной буфер
CMD* dasm_obuf,// указатель на cmd
VOID* dasm_table// указатель на
// таблицы
)

VOID* dasm_tables, // таблицы
// дизассемблера

DWORD flags, // флаги
DWORD* params, // указатель на
// параметры
DWORD* rnd (
DWORD* seed, // указатель на seed
DWORD range // предел
)
DWORD* seed // указатель на seed
);


Указатели на функции malloc/free должны указывать на процедуру выделения/ освобождения памяти.

Dasm - указатель на дизассемблер ,возвращающий информацию о команде в структуре 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;


rnd - указатель на какой-нибуди ГСЧ с указанными параметрами.

Поле params является указателем на структуру неимеющую четко определенного формата, лишь 3 первых поля статичны и предназначены для использвания core и mutator'oм. Т.к. параметры передаются в mutator, то остальное пространство в параметрах может быть использовано в целях нестандартного мутатора.

Если при работе core произошла ошибка, то в EAX возвращается FFFFFFF. Иначе - 0, размер и смещение пермутированого кода в параметрах. Полученный буфер с кодом, после использования, надо освобождать через функцию free, передаваемую движку.

 typedef		struct{ 

DWORD build_size, // длина
// полученного кода

DWORD build_offset, // указатель на буфер
// с полученным кодом

DWORD mixer_maxswp, // количестфо блоков
// для ФП (max)
} ltmeparam;


Чем больше значение mixer_maxswp ,тем более будет "перемешанный" код и тем менее он быст.

mutator - указатель на процедуру мутирования .В принципе, следует писать свой мутатор, вставляя в него вызов стандартного тем самым рашриряя его возможности:

 VOID*	__cdecl	ltme_mutator( 
DWORD csize, // размер кода
VOID* list, // указатель на
// список

DWORD lastlabelPtr, // последняя
// использованая
// метка

DWORD flags, // флаги
DWORD* params, // указатель на
// параметры

DWORD* rnd (
DWORD* seed, // указатель на seed
DWORD range // предел
)
DWORD* seed // указатель на seed
VOID* *malloc (
DWORD cnt // количество байт
),

VOID* free (
VOID* bufer // указатель на буфер
)
)


В процессе работы, LTME выделяет буфер для организации списка/таблиц ,это может потребовать как минимум 60*количество_команд байт плюс память на таблицы.

list - указатель на список ,сгенерированый core , в его начале расположены 3 дворда :

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

Далее идет сам список,состоящий из элементов структуры one:

 typedef	struct	{ 
DWORD one_next,
DWORD one_prev,
ltmedata one_data
} one;

typedef struct {

cmd ltmed_desc,
BYTE ltmed_command[23],
DWORD ltmed_link,
DWORD ltmed_label,
DWORD ltmed_flags

} ltmedata;


Для работы со списком имеется несколько процедур,расположенных в core\list.inc и mutator\list.inc:

 Инициализация списка: 
*list_init( EBX=указатель на память для списка)

Добавление элемента в список:
*list_add( EBX=список,
EDX=номер элемента,за которым будет включение
(FFFFFFFFh если за последним,0 - перед первым)
EDI=указатель на включаемый ltmedata)

Удаление элемента из списка:
*list_kill( EBX=список,
EDX=номер элемента,за который будет удален
(FFFFFFFFh если за последним,1 - первый))

Нахождение адреса элемента:
*list_get( EBX=список,
EDX=номер элемента,адрес которого требуется найти
(1<->FFFFFFFF))
EAX = адрес элемента.

Обмен местами значения элементов:
*list_swap( EBX=список,
EDX = номер элемента #1,
ECX = номер элемента #2)

Получение количества элементов в списке:
*list_getmax( EBX=список)
EAX = количество элементов.


Если у элемента в поле ltmed_flags стоит флаг LTMED_EXTERNAL, то этот элемент имеет ссылку за пределы рассматриваемого кода.

Флаги

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

LTMEF_MSTACK	00000001b	Использовать команды,работающие со стеком 
LTMEF_GARBAGE 00000010b Генерировать мусор
LTMEF_PSWAP 00000100b Использовать ФП
LTMEF_LSWAP 00001000b Использовать ЛП
LTMEF_JCC 00010000b Изменять условия JCC
LTMEF_CMD 00100000b Мутировать команды
LTMEF_ALL 00111111b Использовать все;)

Ремарки

Стандартный мутатор расчитан на вызов только лишь один раз т.к. не модифицирует данные, необходимые для еще одной такой же мутации(т.е. не изменяет структуру cmd мутированых команд).

И если требуется ,чтобы команда не была промутирована,то следует поставить во флагах ltmed_desc.lc_flags бит LF_RAW ,в таком случае считается,что команда не до конца продизассемблирована и она не мутируется.

← 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