Playing with VMProtect - Sample devirtualizeme32_vmp_3.0.9_v2

Update: this blog post has an error, I have mistakenly labeled NOR instruction as NAND, fornication with broken condom ...

Proof courtesy of wolframalpha:
They are the representing the same thing, stay tuned for an update.

In this blog post, I'm going to show you the manual approach I used to generate a text based representation of the executed instructions of VMProtect (I've only studied devirtualizeme32_vmp_3.0.9_v2) for a specific function (or code portion?).
The first step was to manually create a file containing all yara rules for all handlers, this will allow us to automatically detect a specific handler and classify it. The automatic handler detection step is essential because it will allow you to save some time when you are dealing with targets, but that approach is not guaranteed to work for every target, because of the (unique?) algorithm used to decrypt the bytecode (that bytecode which is itself no more no less than an encrypted relative addresses that is used to jump to the next correspondent handler, given the current handler address, and also there is other algorithms that is responsible, for instance, to decrypt constants of types 32bits, 16bits, 8bits, every constant has a decryption algorithm that corresponds to the constant size. ).

Update (28/11/2018): Thanks to this paper, I didn't know the name of this interpretation technique, dubbed Threaded Interpretation, (Which is very different from the well-known switch based fetch-decode-dispatch, whatever blabla...)

I spent a couple of days, if not couple of weeks, to create vmp_rules.yar file, that file contains all the rules for (most/every?) used handler.
Because that approach covers only one execution path, in order to cover other paths one should manipulate the execution flow (somehow) in order to force other paths to be executed (I've never tried to do that and really have no idea about it).
The 2nd step, was to develop an ida python script,, that will automatically detect using yara and capstone libraries, the name of the executed instruction, decrypts and extracts the corresponding parameter, if any, and then logs the result to ida Output window. The script is based on IDA 7.0 (Nah, I'm not wealthy to not use the leaked version).
After generating the text based representation of the instructions, I think the only remaining step is to develop a compiler that, based on the text representation, will read the instructions and generate the correspondent optimized machine code, and I think that this step is the difficult one, because the attacker should be familiar with compiler techniques, in order to remove that crap of that damned architecture.
After the execution of the script, the full listing of the instructions will be printed:

012CCA42: PopReg32 38
012CCA3D: PopReg32 18
012CCA38: PopReg32 0
012CCA33: PopReg32 3C
012CCA2E: PopReg32 34
012CCA29: PopReg32 2C
012CCA24: PopReg32 28
012CCA1F: PopReg32 24
012CCA1A: PopReg32 1C
012CCA15: PopReg32 30
012CCA10: PopReg32 C
012CCA0B: PushReg32 1C
012CCA06: PushEsp
012CCA02: PopReg32 20
012CC9FD: PushImm32 414
012CC9F5: PushEsp
012CC9F1: PushImm32 4
012CC9E9: Add32
012CC9E5: PopReg32 4
012CC9E0: PushEsp
012CC9DC: PushImm32 8
012CC9D4: Add32
012CC9D0: PopReg32 8
012CC9CB: Nand32
012CC9C7: PopReg32 1C
012CC9C2: Add32
012CC9BE: PopReg32 1C
012CC9B9: PushEsp
012CC9B5: DerefMemSs32
012CC9B1: Nand32
012CC9AD: PopReg32 10
012CC9A8: PopEsp
012CC9A4: PushReg32 1C
012CC9A4: PushReg32 1C
012CC99F: PushReg32 1C
012CC99A: Nand32
012CC996: PopReg32 4
012CC991: PushImm32 FFFFF7EA
012CC989: Nand32
012CC985: PopReg32 C
012CC980: PushReg32 10
012CC97B: PushReg32 10
012CC976: Nand32
012CC972: PushReg32 20
012CC96D: PushImm32 FFFFFFFC
012CC965: Add32
012CC961: PopReg32 8
012CC95C: PopReg32 C
012CC957: PopReg32 28
012CC952: PushImm32 815
012CC94A: Nand32
012CC946: PopReg32 14
012CC941: Add32
012CC93D: PopReg32 28
012CC938: PopReg32 28
012CC933: PushImm32 104
012CC92B: PushReg32 20
012CC926: PushImm32 FFFFFFFC
012CC91E: Add32
012CC91A: PopReg32 8
012CC915: SetMemSs32
012CC911: PushReg32 C
012CC90C: PushReg32 20
012CC907: PushImm32 FFFFFDF4
012CC8FF: Add32
012CC8FB: PopReg32 30
012CC8F6: PopReg32 30
012CC8F1: PushReg32 30
012CC8EC: PushImm32 448A99
012CC8E4: PushReg32 38
012CC8DF: Add32
012CC8DB: PopReg32 1C
012CC8D6: PushImm32 411000
012CC8CE: PushReg32 38
012CC8C9: Add32
012CC8C5: PopReg32 14
012CC8C0: DerefMem32
012CC8BC: PushReg32 20
012CC8B7: PushReg32 28
012CC8B2: PushReg32 30
012CC8AD: PushReg32 2C
012CC8A8: PushReg32 34
012CC8A3: PushReg32 3C
012CC89E: PushReg32 0
012CC899: PushReg32 18
012CC894: Exit
//Detected vm_enter after vm_exit
//Added bp @ 0132728

For the full virtualized code, take a look at vmp-program.vmp

I can't say that I've understand every single instruction of the previous listing, but at least I have a clue that at some point before virtualization, there was some code that looks like the following:

mov eax, ????????
push eax
push 448A99                  ; has fixup
call dword ptr [411000] ; has fixup

The 3rd step, I was unable to accomplish that step, which is to create a compiler for that crap code in order to generate elegant and readable x86 machine code.

So, to conclude, I have to read books about compilers, and go deeper in the field in order to gather more knowledge and find a way to compile such listing to an optimized (and correct of course) x86 machine code that can be read and understood by human beings.


Popular posts from this blog

RTCDbg Announcement: Progress after 6 months