![]() |
|
#3
|
|||
|
|||
|
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. |
| Thread Tools | |
| Display Modes | |
|
|