Maximize
Bookmark

VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Register Initialising Using Only Arithmetic Instructions

roy g biv
Ready Rangers Liberation Front [7]
July 2006

[Back to index] [Comments]

What is it?

Probably all polymorphic engines use explicit register initialising. By this, I mean one of these instructions:

        xor     reg, reg
        sub     reg, reg
        mov     reg, value
        push value / pop reg
 

It means that anyone can see the start of the decryptor because of these instructions. We can try to hide the decryptor by using lots of fake routines and similar tricks, but we can't completely avoid this problem. Or can we?

What if we did not use any of those instructions? What if we use only AND and OR to initialise instructions? Then we can have many fake instructions before the real ones, and it is hard to see where the decryptor really starts. We can also add other instructions, like ADD/SUB/XOR. Before the registers are initialised, we can use these instructions to temporarily alter the value, but we must restore the original value before we attempt to update any of the bits. After the registers are initialised, we can use these instructions to select the next value to use.

How does it work?

We initialise the registers by keeping an array of "unknown" bits for each register, and an array of values for each register. We start by setting all of the "unknown" bits to 1, to say that we don't know any of the values. Whenever we control some of the bits using AND or OR, we clear the corresponding "unknown" bits, and update the same bits in the register values. Once all of the "unknown" bits are cleared, we can begin to use the register, and the whole value is known.

This method works for any size of register, so it's even possible on the 64-bit platform. However, only Itanium supports 64-bit immediates. For the AMD64 platform, all 32-bit immediates are treated as signed values, so we must be very careful to use only 31-bit immediates, otherwise the sign-extension can cause unexpected behaviour if we access memory using registers.

We use AND and OR because they are the simplest method. For any clear bit in the AND mask, the same "unknown" bits can be cleared, and the register value bits can be cleared. For any set bit in the OR mask, the same "unknown" bits can be set, and the register value bits can be set. Here is an example of that. Let ABCDEFGH be eight unknown bits. Let us perform some operations and see what happens:

    value        unknown
    ABCDEFGH     11111111
    AND
    11001100
    =
    AB00EF00     11001100

Four unknown bits left.

    AB00EF00     11001100
    OR
    10101010
    =
    1B001F10     01000100

Two unknown bits left.

    1B001F10     01000100
    AND
    10111011
    =
    10001010     00000000

No unknown bits left, and our value is fully known!

In a more complex case, we can also use ADD, but it can initialise only one bit per round. In that case, an unknown bit that is one position to the left of a known set bit can be cleared by adding the value of the known bit. The problem is that once that is done, every bit to the left of the newly cleared bit becomes unknown, until we reach a known clear bit. At that point, the known clear bit becomes unknown, but any other known bits remain known. Here is an example of that. Let us assume that B and C are 0, and G is 1.

    value        unknown
    A00DEF1H     10011101
    ADD
    00000010
    =
    A0CDE10H     10111001

Now we see that B is unchanged, C becomes unknown, and F becomes known. The use of ADD is better when all of the bits to the left of the known bit are unknown, to avoid "losing" bits like the C bit in the example.

In some cases, we can also use unexpected instructions like ADC and SBB, even CMP. For example, we know that AND/OR/XOR always clear the carry, so ADC and SBB behave just like ADD and SUB in those cases. Also, if we know the top bit of our register value, even if no other bits are known, then we can use CMP and "guess" the result if the top bit of our register value is different from the top bit of our operation value. Of course, if we know the whole value, then we can use CMP directly to know the result.

Entry Point Obscuring

Now let us talk about hiding the start of the decryptor. It seems like just using lots of fake instructions would be enough, but it is not so, because when we reach the real decryptor, we will initialise the registers properly, no matter what value they have. There is no way to defend against that, but we can interfere a little bit by altering sensitive registers (like ESP) in the fake instructions. As before, we must restore those registers to their original value if we want to use them, but in the fake instructions before the decryptor, we just "forget" to restore them. This allows us to attack the CPU emulators. A good emulator will see an exception for the bad memory access, and a bad emulator will allow the memory access but sometimes corrupt the decryptor instead.

For a register like ESP, where we can never know its true value, there are still some bits that are known. For the 32-bit platform, we know that ESP is always dword-aligned, so we can play with the low two bits. I wanted to completely destroy the ESP value before the decryptor, by using AND and OR with large values, but then I realised that it could be used to know when the decryptor starts, since at that time, such instructions cannot appear. That means that in a sequence like this one:

        or      esp, 12345678
        and     esp, 87654321
        adc     esp, 12345678
        sub     esp, 12345678
        or      esp, 00000001 <- decryptor starts here
        and     esp, fffffffe
 

the first two instructions can be safely skipped, because they are obviously fake. Instead, I use the allowed values, but I don't restore the register:

        adc     esp, 12345678
        or      esp, 00000001 <- forget to sub first
        add     esp, 12345678 <- make it worse
        and     esp, ffffffff <- forget to and bit 0
 

This is harder to detect, and now ESP points randomly in memory.

Greets to friendly people (A-Z)

Active - Benny - Obleak - Prototype - Ratter - Ronin - RT Fishel - sars - SPTH - The Gingerbread Man - Ultras - uNdErX - Vallez - Vecna - VirusBuster - Whitehead

rgb/defjam jul 2006
[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