[Русский] [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.
Естественно, при отключении всех команд некоторые из них таки будут использоваться:
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
[В