KME -- Kewl Mutation Engine -- User's Manual

## 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 раза, а при нескольких слоях возможно и еще больше.

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

слой# | --- | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13
| --- | 5000 | 11k | 34k | 101k | 300k | 893k | 2.66M | 7.9M
| FLAG_NOCMD | 5000 | 1280 | 380 | 200 | 225 | 342 | 531 | 813 | 1.2k | 1.8k | 3k | 4k | 6k | 10k

При получении данных зашифровывался 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.

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

- MOV для загрузки начальных значений регистров,
- PUSH и XOR для записи данных в стэк
- ADD и JMPreg для выхода из декриптора

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

[В