Maximize
Bookmark

VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Analysis of the "Offensive Polymorphic Engine v2"

Alumna
March 2010

[Back to index] [Comments]

Each layer decryptor begins by using PUSH EBP/MOV EBP,ESP to build a fake stack-frame. It is fake because stack-pointer is not moved forward to alloc space and there is no LEAVE or POP EBP, but there is RET that is reached depending on if the encrypted data can be moved or not. There are instructions to access the stack using the base-pointer to get values but not to write as memory access, for example: mov [ebp], randval/reg32.

The initialisation of the original key vary only from MOV/LEA/PUSH+POP. However, this value cannot be located that easy. There can be initialisations of bogus values everywhere. But can be located by analysing the decryption method.

The decryptor is PIC (position-independent code), this lead us to the first recongnisable instructions. The first CALL appears and is part of the CALL+POP trick (delta-offset), there is a dummy instruction after the CALL and later the destination, so it doesn't look like "e8 00000000".

The destination begins by POPing the address to a random register (ESP excluded) which will be the pointer for decryption. This register needs to be adjusted, this is made by two ADD, the first uses a negative value so it works like a SUB instead, the value is a VA that points within. The second value is the VA of where the encrypted data is placed.

No other than this sequence is implemented:

        call    dest
        ...
dest:   pop     reg32
        add     reg32, -VA
        add     reg32, VA
 

I call this reg32, D0.

The initialisation of the loop n times vary only from MOV/LEA/PUSH+POP. Less complex than delta-offset. However, this value cannot be located that easy. There can be initialisations of bogus values everywhere. But can be located by analysing the decryption loop, it is constant and regswap only (because all registers are selected randomly). I call this register seen at the end, D2.

So, to locate the decryptor, is as simple as looking the instruction that matches ADD/SUB/XOR on writing mode, because all others are for stack, on read mode. The destiny register must match the D0 register. If so, the D1 (it is so for the original key) can be retrieved from this instruction, so to get the original key, must go back to beginning and find a register initialised by MOV/LEA/PUSH+POP. Important values are not initialised twice or adjusted before the decryption begins.

The decryption continues by running the key, this is SUB/ADD D1 by some random value. Now the D0 register is DECremented. The D2 goes next, and to know if must continue the decryption it uses a TEST. DEC+TEST is a sequence and regswap only, so would look:

        dec     D2
        test    D2, D2
        jne     decrypt ;lack of optimisation in the polymorphic engine ;)
 

If zero, the layer decryptor calls either the next layer decryptor or some executable garbage before virus code. If any next layer it would be down the actual layer (opposite to execution flow).

So, how looks the decryptor?

        push    ebp
        mov     ebp, esp
        ...
        push    KEY
        pop     D1
        ...
        call    dest
        ...
dest:   pop     D0
        add     D0, -VA
        add     D0, VA
        ...
        mov     D2, loop n times
        ...
decrypt:...
        sub     dword ptr [D0], D1
        ...
        dec     D0
        ...
        sub     D1, run key
        ...
        dec     D2
        test    D2, D2
        jne     decrypt
        ...
        call    next
        ...
        ret
 

Now, '...' can go from JMP/PUSH/POP/MOV/ADD/SUB/INC/DEC/XOR/OR/AND/LEA to CMP and TEST s with JXX. Also instructions of 16-bit type.

We can draw from the example that the decryption goes backwards modifying 1 DWORD per round using a running key, and as the pointer is DECremented by 1, would be like:

                 apply o key
                 ||||
        decryptorxxxxxxxxxxxxxxxx
        
        decrease pointer and run key:

                  apply key
                  ||||
        decryptorvbbbxxxxxxxxxxxx

v = virus b = any value

I used for the example (see below 'decrypt: ...') the instruction SUB, however it might vary using ADD/XOR, too. I used for the example (see 'sub D1, run key') the instruction SUB, but as I said before it could use ADD.

The virus is also surrounded by trash and executable garbage, it would look like:

	trash
	exec	grbg
	virus
	trash
	decryptor

This of course might vary.

The W32.Anunnaki virus activates another functionality in the polymorphic engine when possible. If there is a section (Sx) other than default code section that can virtually hold all data (encrypted code, trash, decryptors, etc), a new code is generated, but this one moves the encrypted data to Sx before applying decryption. The code is PIC and the sequence is the same and regswap only, we will set some new register names:

        push    ebp
        mov     ebp, esp
        ...
        call    assingR0
        ...
assingR0:
        pop     R0
        add     R0, -VA
        add     R0, VA
        ...
        call    assingR1
        ...
assingR1:
        pop     R1
        add     R1, -VA
        add     R1, VA
        ...
 
movedata:
        ...
        mov     R4, dword ptr [R0]
        mov     dword ptr [R1], R4
        ...
        add     R0, 4
        ...
        add     R1, 4
        ...
        dec     R3
        ...
        test    R3, R3
        jne     movedata
        ...
        push    KEY
        pop     D1
        ...
        call    dest
        ...
dest:   pop     D0
        add     D0, -VA
        add     D0, VA
        ...
        push    loop n times
        pop     D2
        ...
decrypt:...
        sub     dword ptr [D0], D1
        ...
        dec     D0
        ...
        sub     D1, run key
        ...
        dec     D2
        test    D2, D2
        jne     decrypt
        ...
        call    dest
        ...
dest:   pop     R5
        add     R5, -VA
        add     R5, VA
        ...
        push    R5
        ...
        ret
 

The encrypted virus body, trash, garbage code, decryptors, are copied to Sx and from the actual section decrypted, then it RETurns control to the next decryptor (if any) in Sx.

The tell-tale sequences in the code are enough to detect any decryptor. The engine is yet young, some generations may surprise, but not fool. I remain hopeful, though, Dark Prophet could code a much better engine, I think.

[email protected]

By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxheaven.org aka vx.netlux.org
deenesitfrplruua