Kewl Mutation Engine (TM)
release 1.xx
User's Manual

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


Decryptor structure

There is no "polymorphic decryptor" and "encrypted data" as different objects, it is all decryptor, whose task is just to push some data to the stack in reversed order and then pass control to that data using instructions equivalent to JMP ESP.

Source data: (virus) Encrypted data:
db 22
db 33
db 44
db 55
db 66
db 77
db 88
ADD     EAX, (88776655h - curr_eax)
ROR     EBX, 4
XOR     EBX, (44332290h xor curr_ebx)

Instead of standard polymorphic engines which produces data of length DECRYPTOR_LENGTH + SOURCE_DATA_LENGTH, KME produces data of length SOURCE_DATA_LENGTH * k, where k is some data expansion coefficient, which depends upon many parameters.

Decryptor length/Compression

When all features are enabled, produced data is 2-3 times more than source data. But there exists an interesting effect: when all "logic" is disabled (i.e. there is no register encryption, see FLAG_NOCMD) and data to encrypt is a repeating byte/word/dword, then produced decryptor will be shorter than source data. Maximal compression for single polimorphic layer is 4 times, but it can be greater when using multilayer technique.

How decryptor length depends on number of layers

layer# ---12345678910111213

To create these pictures i was encrypting 5k NOP-buffer. Red color -- "logic" enabled, blue color -- logic disabled (FLAG_NOCMD). As you can see, on third layer achieved 25-times compression.


You can use KME everywhere -- ring3 and ring0, NT and win9X at least. Flat memory model, no segment registers used. There is no system calls inside of the engine, only register/memory manipulation. All parameters are passed on the stack. Engine's local variables are located in the stack too. Engine's code as well as produced decryptor's code is non-sensitive to offset displacement, 'coz there is no data used.

You can specify the following parameters:
- registers usage (min 1, max 7),
- commands usage,
- presence and parameters of JMPs (when pieces of code has random location in the decryptor and are linked with jmps),
and some other parameters.

Also you can specify RandSeed -- number to initialize random number generator. As a result all the produced decryptor depends on parameters you pass to engine, RandSeed and source data.

Decryptor is produced in a single pass, and no extra memory used to do it. As a result length of source data/decryptor is unlimited. So you can create decryptor of 100MB-length and start virus from such file every boot. It is also possible to create multilayer decryptors by multiple calls to KME. (see examples)

How to link/include

KME exists in three forms:

1. Compiled OBJ-file (KME32.OBJ and KME32.INT)

include KME32.INT
extrn kme_main:PROC

2. Sources (KME32.INC and KME32.INT)

include KME32.INT
include KME32.INC

3. As "binary include" file (KME32BIN.INC и KME32.INT)

include KME32.INT
include KME32BIN.INC

How to call

KME has only one PUBLIC near-procedure, called kme_main. It has pascal-convention call, i.e. it ends with "RET xxxx" command. All 13 parameters are of DWORD-type.

   push    Flags        ; flags, FLAG_XXX
   push    CommandMask  ; command set, CMD_XXX
   push    RegMask      ; register set, REG_XXX
   push    RandSeed     ; random number generator initializer
   push    JmpProb      ; (1/probability) of jmps. JMP if rnd(JmpProb)==0
                          used only if FLAG_NOJMPS is not specified
   push    OutEntryPtr  ; pointer to DWORD, where decryptor entrypoint
                          will be stored. if FLAG_EIP0, 0 will be stored.
   push    OutSizePtr   ; pointer to DWORD with produced decryptor size
                          w/o jmps -- ~InputSize*k here will be stored
                          with jmps -- OutMaxSize will be stored
   push    OutFiller    ; character to initialize decryptor buffer with
   push    OutMaxSize   ; maximal decryptor buffer size
   push    OutPtr       ; pointer to decryptor buffer
   push    InputEntry   ; virus entry point (where to pass control to)
   push    InputSize    ; source data (virus) size
   push    InputPtr     ; source buffer
   call    kme_main

   jc      error

Return values:

Registers unchanged, DF=0 (CLD)

CF=0 if all ok
CF=1 if error (no free space in output buffer)


All constants are defined in KME32.INT.

Flags (first parameter)

FLAG_DEBUG insert INT3 (0CCh) to decryptor start/end
FLAG_NOLOGICdisable "logic" (register encryption)
FLAG_NOJMPS disable jmps
FLAG_EIP0 disable random decryptor entrypoint selection (if FLAG_NOJMPS)
FLAG_NOSHORTdisable short-instructions for EAX

CommandMask (second parameter)

Define command set which may be used in the decryptor.

CMD_xxxsee KME32.INT
CMD_ALLuse all commands

All commands of type CMD_xxx (not CMD2_xxx) may be globally disabled by FLAG_NOLOGIC in the Flags parameter.

If CMD2_ADD, CMD2_SUB and CMD2_XOR are all disabled at the same time, CMD2_XOR will be used.

Of course, some commands will be used anyway:

RegMask (third parameter)

Define set of registers which may be used in the decryptor. Totally 7 registers may be used (all 32-bit regs except ESP)

REG_xxxsee KME32.INT
REG_ALLuse all possible registers (EAX/EBX/ECX/EDX/ESI/EDI/EBP)

If no registers defined, EAX will be used.

Entry points (InputEntry and OutputEntryPtr)

All entry points are relative to their buffers.

Passing control from decryptor to virus

After data is decrypted, JMP (ESP+InputEntry) is executed.

At this time all registers from RegMask set are destructed, and virus with N-1 layers (assuming there were N layers) are pushed on the stack Size of all this shit can be calculated as sum of lengths of virus and all decryptors except first, but each length is dword-aligned ((InputSize+3) and (not 3)).

(c) 1999 Z0MBiE,