·─T─┌O┐─T─┌A┐L···─Z┐┌0┐┌M┐┌B·i┌F─i┌C─┌A┐─T─i┌O┐┬N┬··························
   │ │ │ │ ├─┤│    / │ ││││├┴┐┬├─ ┬│  ├─┤ │ ┬│ ││└┤  Issue #1, January-2001
 ··│·└─┘·│·│·│└─··└──└─┘│·│└─┘││··│└──│·│·│·│└─┘│·│··························

 ············································································
                   СТЭКОВЫЙ ПОЛИМОРФНЫЙ ДВИЖОК KME v3.50         [kme350.zip]
 ············································································

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

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

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

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

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

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

                                Возможности

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

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

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

                                Как вызывать

     KME  имеет  всего  одну PUBLIC near-процедуру, kme_main. Все параметры
 типа  DWORD,  17  штук.  Тип  вызова  cdecl, то есть выход из процедуры по
 RET, и вызывающий код должен сделать ADD ESP, 4*17.

   push    Flags        ; флаги, FLAG_XXX
   push    CommandMask  ; маска команд, CMD_XXX
   push    CommandMask2 ; маска команд, CMD2_XXX
   push    RegMask      ; маска регистров, REG_XXX
   push offset my_random ; внешний рандомер
   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     ; буфер с исходными данными (вирусом)
   push    offset initregs   ;   указатели на 8-двордовые буфера со
   push    offset exitregs   ;   значениями регистров, -1=не используется
   push    user_param   ; пользовательский параметр, передается в рандомер
   call    kme_main
   add     esp, 17*4

   or      eax, eax     ; 0=все OK
   jnz     error

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

  Регистры без изменения, DF=0
  EAX=0 если все в порядке
  EAX=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/CommandMask2 (второй/третий параметр)

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

  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)  --  относительные
 смещения от начал своих буферов.

                        Параметры initregs/exitregs

     Эти  два  указателя  показывают  на буфера, в каждом по 8 двордов, под
 дворду  для  соответствующего  регистра.  -1  означает,  что  значение  не
 используется.
     Initregs   --   массив  значений  регистров,  которые  будут  переданы
 полиморфному   расшифровщику   при  вызове.  Расшифровщик  будет  сгенерен
 завязанным  на  эти значения, и если хоть одно из них не совпадет -- нихуя
 не расшифруется.
     Exitregs  --  массив  значений регистров, которые будут получены после
 выхода  из  расшифровщика.  То  есть  расшифровщик  будет  сгенерен таким,
 чтобы  при выходе в заданных регистрах образовались именно эти значения. К
 этим значениям можно привязать вирус.

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

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

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


     На этом движке были написаны вирусы ZMorph, а также он был использован
 в I-Worm.Hybris.

 ············································································