KME-32
   Kewl Mutation Engine (TM)
версия 1.xx
Руководство Пользователя



[Русский] [English]



Содержание



Структура декриптора

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

исходные данные: зашифрованные данные:
...
nop
db 22
db 33
db 44
db 55
db 66
db 77
db 88
...
...
XOR     EAX, EBX
ADD     EAX, (88776655h - curr_eax)
ROR     EBX, 4
XOR     EBX, (44332290h xor curr_ebx)
...
PUSH    EAX
...
PUSH    EBX
...

Легко заметить, что если обычный полиморфный движок производит данные длиной РАЗМЕР_ДЕКРИПТОРА + ДЛИНА_ИСХОДНЫХ_ДАННЫХ, то стэковый движок производит данные длиной ДЛИНА_ИСХОДНЫХ_ДАННЫХ * k, где k -- некоторый коэффициент увеличения данных, зависящий от многих параметров.



Длина расшифровщика/Компрессия

У KME при всех включенных фичах код увеличивается в 2-3 раза. Но тут возможен интересный эффект: при отсутствии "логики" (т.е. без шифровки регистров, см. FLAG_NOCMD) и одинаковых шифруемых данных (1 и тот же повторяющийся дворд/ворд/байт), декриптор получится меньше, то есть произойдет сжатие. Максимальное сжатие для одного полиморфного слоя -- в 4 раза, а при нескольких слоях возможно и еще больше.

Зависимость длины расшифровщика от количества полиморфных слоев:

слой# ---12345678910111213
  ---500011k34k101k300k893k2.66M7.9M
  FLAG_NOCMD500012803802002253425318131.2k1.8k3k4k6k10k


При получении данных зашифровывался 5-килобайтный буфер из NOPов, красным цветом -- шифровка регистров включена, синим -- выключена (FLAG_NOCMD). Как видим на третьем слое получено сжатие в 25 раз.



Возможности

Пользовать KME можно практически везде -- ring3 и ring0, NT и win9X как минимум, для остальных операционок х/з. Используется флэт-модель памяти, никаких сегментных регистров. Внутри движка нет никаких системных вызовов, одни регистры и память. Все параметры передаются на стэке. Внутренние переменные лежат там же. Код движка, как и код декриптора, не чувствителен к изменению оффсета.

У декриптора можно регулировать:
- используемые регистры (минимум 1, максимум 7),
- используемые команды (около десятка),
- наличие и параметры "пятен" (код разбросан по памяти и связан jmp-ми),
а также некоторые другие детали

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

Декриптор генерируется за 1 проход, и никакой дополнительной памяти по этому поводу не используется. В результате размер исходных данных и декриптора не ограничен. Таким образом реально создать расшифровщик в 100 мегабайт и оттуда запускать вирус при каждой загрузке. Возможно также несколькими вызовами KME создавать полиморфные "слои", то есть зашифровывать данные многократно. (см. примеры)



Как подключать

KME поставляется в трех формах.

1. В виде OBJ-файла (KME32.OBJ и KME32.INT)

YOURMAKE.BAT YOURSRC.ASM:
tasm  YOURSRC.ASM
tlink YOURSRC.OBJ KME32.OBJ
include KME32.INT
extrn kme_main:PROC

2. В исходниках (KME32.INC и KME32.INT)

YOURSRC.ASM
include KME32.INT
include KME32.INC

3. В "бинарном инклюднике" (KME32BIN.INC и KME32.INT)

YOURSRC.ASM
include KME32.INT
include KME32BIN.INC


Как вызывать

KME имеет всего одну PUBLIC near-процедуру, kme_main. Тип вызова паскалевский, то есть выход из процедуры по "RET xxxx". Все параметры типа DWORD, 13 штук.

   push    Flags        ; флаги, FLAG_XXX
   push    CommandMask  ; маска команд, CMD_XXX
   push    RegMask      ; маска регистров, REG_XXX
   push    RandSeed     ; дворд для инициализии генератора случайных чисел
   push    JmpProb      ; (1/вероятность) jmp-ов. JMP если rnd(JmpProb)==0
   push    OutEntryPtr  ; указатель на DWORD, в который будет записана
                          точка входа в декриптор.
                          если FLAG_EIP0, то это будет 0
   push    OutSizePtr   ; указатель на DWORD, в который будет записан
                          размер полученного декриптора.
                          без "пятен" -- здесь будет ~InputSize*k,
                          с "пятнами" -- k смысла не имеет,
                                         здесь будет значение OutMaxSize
   push    OutFiller    ; байт, которым инициализировать декриптор
   push    OutMaxSize   ; максимальный размер буфера декриптора
   push    OutPtr       ; указатель на буфер в котором будет декриптор
   push    InputEntry   ; точка входа в зашифровываемые данные (куда
                          декриптор отдаст управление)
   push    InputSize    ; размер исходных данных
   push    InputPtr     ; буфер с исходными данными (вирусом)
   call    kme_main

   jc      error

Возвращаемое значение:

Регистры без изменения, DF=0

CF=0 если все в порядке
CF=1 если ошибка (отсутствует свободное место в выходном буфере)

Комментарии:

Значения констант описаны в KME32.INT. Все константы суть степени двойки. Можно их ORить либо складывать.

Flags (первый параметр)

FLAG_DEBUG вставить INT3 (0CCh) в начало и в конец декриптора
FLAG_NOLOGICотключить команды изменяющие значения регистров
FLAG_NOJMPS не использовать "пятна" (jmp-ы)
FLAG_EIP0 точка входа в декриптор совпадает с его началом, а не выби рается случайно. Актуально только если включены JMPы (отсутствует FLAG_NOJMPS)
FLAG_NOSHORTотключить использование "коротких" инструкций для EAX (которые на 1 байт меньше -- XOR,ADD,SUB,...)

CommandMask (второй параметр)

Задает набор команд, которые можно использовать в декрипторе.

CMD_xxxсм. KME32.INT
CMD_ALLиспользовать все команды

Глобально все команды типа CMD_xxx могут быть отключены битом FLAG_NOLOGIC в параметре Flags, и тогда актуальны только CMD2_xxx.

В случае отсутствия сразу CMD2_ADD, CMD2_SUB и CMD2_XOR, будет использоваться CMD2_XOR.

Естественно, при отключении всех команд некоторые из них таки будут использоваться:

RegMask (третий параметр)

Задает набор регистров, которые можно использовать в декрипторе. Всего 7 регистров (все РОНы кроме ESP)

REG_xxxсм. KME32.INT
REG_ALLиспользовать все возможные регистры (EAX/EBX/ECX/EDX/ESI/EDI/EBP)

Если не задано ни одного регистра, используется EAX

Точки входа (InputEntry и OutputEntryPtr)

Все точки входа -- относительные смещения от начал своих буферов.



Получение управления от декриптора

После расшифровки исходных данных в стэк происходит JMP (ESP+InputEntry), то есть передача управления вирусу.

При этом разрушены все регистры из RegMask, а в стэке находится сам вирус а также N-1 декриптор в случае N слоев. Размер всей этой хрени в стэке вычисляется как сумма длин вируса и всех декрипторов кроме первого, причем каждая длина выровнена на границу 4-х байт ((InputSize+3) and (not 3)).



(c) 1999 Z0MBiE, http://z0mbie.host.sk

[В начало] [Содержание] [English]