Thread: VMP vm
View Single Post
  #3  
Old 09-26-2014, 01:00
pDriLl
 
Posts: n/a
Hi.
I have also looke at VMProtect in the last past weeks and I will show a bit differt way of looking at it.
Insted of going to deep into every instruction I will show how to get a better overview of whats going on by
looking at the code flow and a few instruction only.

I wrote a small program that will run the protected exe and attach a debuger to it.
My debugger will set a breakpoint on the fetch instruction at address 01199609. So every time a vm
instruction is fetched my debugger will stop it and I can look at the vm's register and stack etc.
So in the x86 CPU ESI will be vm's instruction pointer, EDI is ptr to vm's registers r0-r15 and Ebp
will be vm's stack pointer.


.vmp1:01199609 mov ecx, ds:vm_dispatch_table_1[eax*4] <- Fetch instruction
.vmp1:0119A8F0 add ecx, 7782C4B9h

Using this info we can decode the opcode/dispatch table
After decoding we can see all instructions and the address where they are executed.
For now I wont try to understand all om them. Only the opcodes that calls out of the VM.

Instruction (opcode) Table:
00 - 01198355
01 - 011999EE
02 - 0119854B
03 - 01199FD0
04 - 0119856B
05 - 01199406
06 - 0119AA44
07 - 01198C5A
08 - 0119955D
09 - 0119A5C5
0A - 01198EFF
0E - 0119A3DD
11 - 01199487
12 - 01199B6D
13 - 0119A1EB
14 - 0119A52A
17 - 01198F6E
18 - 01198CFD
1B - 0119A16B
1D - 01199A2E
1E - 01198739
20 - 0119A469
22 - 0119A000
24 - 01199615
25 - 01198F18
26 - 01199F94
28 - 01198338
29 - 0119972A
2C - 0119A06E
2D - 01198E62
2F - 0119996D
34 - 011986A3
35 - 0119A6A0
36 - 011982C5
37 - 011983FA
3C - 0119853E
3D - 01199B02
3E - 0119A6AE
3F - 01199B1E
41 - 011982EB
42 - 011984BA
44 - 0119A8E7
47 - 0119A7FE
49 - 01198948
4C - 01199A4E
4E - 01199978
51 - 01199DFE
54 - 01199643
55 - 0119A7C5
57 - 0119A625
59 - 011993C1
5D - 011998B7
61 - 0119892D
63 - 0119A80C
67 - 01199900
6D - 0119848C
6E - 01198BA1
7A - 0119A0CD
8A - 0119A5B3
8C - 011999B6
A6 - 01199510

Found 61 instructions

Not all of them is in use when we run it.
After a full run we can see some stats about which opcode that are executed and the number of times they are executed.

Number of used opcodes 48
25 - 1198F18 cnt:75945 <- pop rx (x can be 0-15)
14 - 119A52A cnt:14002
61 - 119892D cnt:11788
1E - 1198739 cnt:44532
7A - 119A0CD cnt:11759
2C - 119A06E cnt:3341
42 - 11984BA cnt:15
22 - 119A000 cnt:297
01 - 11999EE cnt:513
28 - 1198338 cnt:24
1D - 1199A2E cnt:516
12 - 1199B6D cnt:10353
02 - 119854B cnt:24146
37 - 11983FA cnt:3517
2D - 1198E62 cnt:6463
4C - 1199A4E cnt:1126
04 - 119856B cnt:1489 <- Jump
17 - 1198F6E cnt:2
6E - 1198BA1 cnt:3
54 - 1199643 cnt:1625
07 - 1198C5A cnt:337 <- Call out function
44 - 119A8E7 cnt:856
6D - 119848C cnt:98
06 - 119AA44 cnt:691
8A - 119A5B3 cnt:989
03 - 1199FD0 cnt:248
20 - 119A469 cnt:521
A6 - 1199510 cnt:1354
4E - 1199978 cnt:1013
1B - 119A16B cnt:1012
24 - 1199615 cnt:519
5D - 11998B7 cnt:26
3C - 119853E cnt:2
08 - 119955D cnt:1
3D - 1199B02 cnt:5
0A - 1198EFF cnt:43
26 - 1199F94 cnt:4
2F - 119996D cnt:15
34 - 11986A3 cnt:19
11 - 1199487 cnt:3
18 - 1198CFD cnt:3
29 - 119972A cnt:3
41 - 11982EB cnt:4
55 - 119A7C5 cnt:17
47 - 119A7FE cnt:2
09 - 119A5C5 cnt:2
05 - 1199406 cnt:2
59 - 11993C1 cnt:1 <- Exit VM and decode registers

So we have 48 instructions that are in use.

No when we run it the debugger starts printing out the execution flow and as we can see here
(VM Entry point. VM is running backward!)

Looks like my IP is one byte off coz of the code running backward. This will not affect the analyze.

011900BC: 25->25 [x86:01198F18] (sp[0]:00000000 sp[1]:00000000 sp[11]:EF9353B9)
011900BA: AB->14 [x86:0119A52A] (sp[0]:00000000 sp[1]:00000000 sp[11]:C49DA64B)
011900B5: 61->61 [x86:0119892D] (sp[0]:050E5A20 sp[1]:00000000 sp[11]:EF9353B9)
011900B4: F0->25 [x86:01198F18] (sp[0]:00000202 sp[1]:050E5A20 sp[11]:EF9353B9)
011900B2: F0->25 [x86:01198F18] (sp[0]:050E5A20 sp[1]:00000000 sp[11]:C49DA64B)
011900B0: CF->25 [x86:01198F18] (sp[0]:00000000 sp[1]:7C90E514 sp[11]:7C817077)
011900AE: 25->25 [x86:01198F18] (sp[0]:7C90E514 sp[1]:7FFDB000 sp[11]:00B500B4)
011900AC: 25->25 [x86:01198F18] (sp[0]:7FFDB000 sp[1]:0006FFB0 sp[11]:00B700B6)
011900AA: F0->25 [x86:01198F18] (sp[0]:0006FFB0 sp[1]:0006FFF0 sp[11]:7FFDB000)
011900A8: F0->25 [x86:01198F18] (sp[0]:0006FFF0 sp[1]:7C90E514 sp[11]:80544CFD)
011900A6: F0->25 [x86:01198F18] (sp[0]:7C90E514 sp[1]:00B700B6 sp[11]:0006FFC8)
011900A4: CF->25 [x86:01198F18] (sp[0]:00B700B6 sp[1]:00000246 sp[11]:865FC370)
011900A2: CF->25 [x86:01198F18] (sp[0]:00000246 sp[1]:00B500B4 sp[11]:FFFFFFFF)
011900A0: 25->25 [x86:01198F18] (sp[0]:00B500B4 sp[1]:EF9353B9 sp[11]:7C839AD8)
0119009E: FD->25 [x86:01198F18] (sp[0]:EF9353B9 sp[1]:C49DA64B sp[11]:7C817080)
0119009C: 25->25 [x86:01198F18] (sp[0]:C49DA64B sp[1]:7C817077 sp[11]:00000000)
...
.. cut ..
...

(Instruction 0x25 should be pop rx)

Now by looking at the execution flow I could see that opcode 0x07 was very interesting.

010DFD3C: 19->07 [x86:01198C5A] (sp[0]:010D432E sp[1]:0006FF88 sp[11]:7C91BEC2) lit:00000000
01194772: 19->07 [x86:01198C5A] (sp[0]:01195873 sp[1]:7C800000 sp[11]:0006FA08) lit:00000000
01194772: 19->07 [x86:01198C5A] (sp[0]:01195873 sp[1]:7C800000 sp[11]:0006FA08) lit:00000000

Oh and btw. My debugger reads the original byte code and looks it up in my opcode table to find the
first entry of that opcode. For opcode 0x19 we can see that it is the same as opcode 0x07.

So it looks likes opcode 0x07 is a call out of vm function so lets look closer at this instruction.
Whenever this instruction is executed I dump vmprotects stack (Ebp)
So now I run the code without printing every instruction but only instruction 0x07.

Opcode 0x07 stack log
0 ( 1) f(010D432E) (0006FF88, 010D29DB, 00530057, 000066FF).. time 00000000 ticks
1 ( 1) f(01195873) (7C800000, 0006FF78, 010D073B, 00530057).. time 00000000 ticks
2 ( 2) f(01195873) (7C800000, 0006FF78, 010D50DE, 00530057).. time 00000000 ticks
3 ( 3) f(01195873) (7C800000, 0006FF7C, 010D1E42, 00530057).. time 00000000 ticks
..
18 ( 1) f(0118DC2B) (010DA75D, 01001000, 00530057, 000066FF).. time 00000006 ticks
19 ( 2) f(0118DC2B) (010E0EAA, 01009000, 00530057, 000066FF).. time 00000000 ticks
20 ( 3) f(0118DC2B) (010E20E5, 0100B000, 00530057, 000066FF).. time 0000009E ticks
..
335 ( 1) f(01018870) (01000000, 00000001, BB1606C4, 00530057).. time 00000070 ticks
336 ( 2) f(7C801AD4) (01001000, 00007748, 00000020, 0006FF98).. time 00000000 ticks


So opcode 0x07 calls x86 code and then returns back to VM. Code that is executed is f(xxx)

After a full run again we can see what functions are called. I could easy find LoadLibrary
and GetProcAddress by looking at the stack.
I do not find the number of argumens that every function is using so I always print out 4 of them.
It is also easy to see that function f(0118DC2B)(src, dst) will unpack/decode data to the original section.

Call out function count:
010D432E (10) LoadLibrary
01195873 (312) GetProcAddress
7C80B475 (1)
7C810800 (1)
7C810B17 (1)
7C80943C (1)
7C80B9A5 (1)
7C80BA14 (1)
7C809BE7 (2)
7C801AD4 (2)
0118DC2B (3) Copy/Unpack data
7C809A2D (1)
01018870 (1)

So after some time this vm will exit and jump into the program's code section like all packers usually does.

Last opcode executed before jump to code section:
0103F39A: 12->12 [x86:01199B6D] (sp[0]:0006FF88 sp[1]:00000286 sp[11]:010A8244) lit:00000000
0103F399: FD->25 [x86:01198F18] (sp[0]:00000286 sp[1]:00000286 sp[11]:010A8244) lit:00000000
0103F397: 1E->1E [x86:01198739] (sp[0]:00000286 sp[1]:E108B29A sp[11]:01007568) lit:00000000
0103F395: 78->59 [x86:011993C1] (sp[0]:0102E07B sp[1]:00000286 sp[11]:010A8244) lit:00000000

So opcode 0x59 is the last instruction and is probably the exit vm function.
This should execute the code at location stack[11].
(Every vm's I have looked at have this address at stack location 11)

Opcode 0x59 stack log
01007568: f(010A8244) (0103CDF1, 01001898, 00000070, 7C817077)..

Here we can see the return address (01007568) after the function is called like a standard API call.

Function 010A8244 will decode the x86 registers and jump to 1007568.
When I follow this code I can see some small function that is executed before the coe jumps into a new vm again.
We can do the same thing for the new vm and analyze it at a higher level before diving downt and decode every instructions.


So by looking at this analyze it looks like the first vm will prepare all sections in the program and then unpack/decode them.
After this the vm will look up imports I guess and then jump to the code section.
GetProcAddress was called 312 times for 10 dll's.
The 0x7Cxxxxxx is DLL calls but I did not bother to look them up.

Hope this will help some to get a better overview of what giong on in the vm.

Oh and also. Sorry for my bad english and my typos.
Reply With Quote
The Following 4 Users Gave Reputation+1 to For This Useful Post:
Conquest (09-26-2014), DMichael (09-26-2014), niculaita (09-26-2014), tonyweb (10-04-2014)