1.3.伪指令汇总
调试VMP前期的一个重要的体力活是,识别出所有的伪指令,并根据它的用途给它相应的命名。以后就可以在DISPATCH部件的最后跳转地址:
0043E11F |. C2 5000 RETN 50
下断点,再盯着VM堆栈就可以知道VM的所有操作。
我们先来了解所有伪指令的DISPATCH(调遣)部件:
0043E6BF |. 8A46 FF MOV AL,BYTE PTR DS:[ESI-1] ; *
0043E6C4 |. 30D8 XOR AL,BL ; *
0043E6CE |. F6D0 NOT AL ; *
0043E6D6 |. FEC8 DEC AL ; *
0043E6DA |. C0C8 07 ROR AL,7 ; *
0043E6E1 |. 83EE 01 SUB ESI,1 ; *
0043E6ED |. 30C3 XOR BL,AL ; *
0043D02F |. 0FB6C0 MOVZX EAX,AL ; *
0043F124 |. 8B1485 DBE143 MOV EDX,DWORD PTR DS:[EAX*4+43E1DB] ; *
0043E100 |> /81C2 6B197FB6 ADD EDX,B67F196B ; *
0043E10A |. 895424 3C MOV DWORD PTR SS:[ESP+3C],EDX ; *
0043E11B |. FF7424 4C PUSH DWORD PTR SS:[ESP+4C] ; *
0043E11F |. C2 5000 RETN 50
首先从ESI中解密获得下一条伪指令在DispatchTable(调遣表)中的偏移量,使用[EAX*4+43E1DB]来读取出伪指令地址,简单的ADD解密后,把真正的伪指令地址压入ESP栈顶,最后用RETN 50跳转到相应的伪指令。
MOV EDX,DWORD PTR DS:[EAX*4+43E1DB]给我们提供的信息:DispatchTable的起始地址是0043E1DB,最后一个dword的开始地址是以AL的最大值FF作为偏移量[FF*4+43E1DB]=0043E5D7。我们把记事本0043E1DB--0043E5D7的数据粘贴:
CPU Dump
Address Hex dump ASCII
0043E1D0 09|BA C4 49 D0| .I
0043E1E0 BA C4 49 1E|B7 C4 49 E6|C4 C4 49 53|D1 C4 49 05| IIISI
0043E1F0 BE C4 49 75|D1 C4 49 D4|CE C4 49 0C|D6 C4 49 C3| IuII.I
0043E200 BD C4 49 7B|CE C4 49 67|BE C4 49 26|BF C4 49 EB| I{IgI&I
0043E210 C2 C4 49 82|D0 C4 49 3A|BA C4 49 1E|B5 C4 49 A8| II:II
0043E220 C4 C4 49 1E|B5 C4 49 2E|C8 C4 49 B9|BB C4 49 E9| II.II
0043E230 C3 C4 49 2D|B8 C4 49 95|C1 C4 49 82|D0 C4 49 75| I-IIIu
0043E240 D1 C4 49 C3|BE C4 49 16|B6 C4 49 2D|B8 C4 49 75| I镁II-Iu
0043E250 D1 C4 49 95|C1 C4 49 EB|C2 C4 49 52|BF C4 49 B4| IIIRI
0043E260 D3 C4 49 8B|D3 C4 49 05|CE C4 49 52|BF C4 49 D4| IIIRI
0043E270 CE C4 49 E8|B8 C4 49 C3|BD C4 49 C3|BE C4 49 5E| II媒I镁I^
0043E280 B4 C4 49 B1|B8 C4 49 61|BD C4 49 5D|BF C4 49 E9| IIaI]I
0043E290 C3 C4 49 26|BF C4 49 5F|D0 C4 49 B4|D3 C4 49 E6| I&I_II
0043E2A0 C4 C4 49 EC|B7 C4 49 1E|B5 C4 49 0D|C0 C4 49 0D| III.I.
0043E2B0 C0 C4 49 C3|BD C4 49 5D|BF C4 49 7B|CE C4 49 C3| I媒I]I{I
0043E2C0 BD C4 49 1E|B5 C4 49 82|D0 C4 49 8A|B9 C4 49 A6| IIII
0043E2D0 D1 C4 49 EB|C2 C4 49 D4|CE C4 49 61|BD C4 49 09| IIIaI.
0043E2E0 BA C4 49 53|D1 C4 49 61|BD C4 49 3A|BA C4 49 3A| ISIaI:I:
0043E2F0 D0 C4 49 0C|D6 C4 49 3A|BA C4 49 1E|B7 C4 49 05| I.I:II
0043E300 CE C4 49 0D|C0 C4 49 82|D0 C4 49 27|D2 C4 49 7C| I.II'I|
0043E310 BD C4 49 E8|B8 C4 49 41|C2 C4 49 E9|C3 C4 49 25| IIAII%
0043E320 CE C4 49 53|C6 C4 49 61|BD C4 49 53|C6 C4 49 83| ISIaISI
0043E330 D6 C4 49 53|C6 C4 49 5D|BF C4 49 53|C6 C4 49 A8| ISI]ISI
0043E340 C4 C4 49 53|C6 C4 49 5F|D0 C4 49 53|C6 C4 49 E6| ISI_ISI
0043E350 C4 C4 49 53|C6 C4 49 3A|BA C4 49 53|C6 C4 49 00| ISI:ISI.
0043E360 C7 C4 49 53|C6 C4 49 2D|B8 C4 49 53|C6 C4 49 25| ISI-ISI%
0043E370 CE C4 49 53|C6 C4 49 83|D6 C4 49 53|C6 C4 49 1E| ISIISI
0043E380 B7 C4 49 53|C6 C4 49 C3|BD C4 49 53|C6 C4 49 62| ISI媒ISIb
0043E390 CF C4 49 53|C6 C4 49 12|D3 C4 49 53|C6 C4 49 E8| ISIISI
0043E3A0 B8 C4 49 05|CE C4 49 1E|B7 C4 49 8A|B9 C4 49 B4| IIII
0043E3B0 D3 C4 49 B9|BB C4 49 A6|D1 C4 49 E8|B8 C4 49 FE| IIII
0043E3C0 C0 C4 49 82|D0 C4 49 53|D1 C4 49 2D|B8 C4 49 52| IISI-IR
0043E3D0 BF C4 49 3A|D0 C4 49 C3|BE C4 49 A6|C1 C4 49 C3| I:I镁II
0043E3E0 BE C4 49 5E|B4 C4 49 82|D0 C4 49 7C|BD C4 49 C3| I^II|I
0043E3F0 BD C4 49 C3|BE C4 49 1E|B7 C4 49 61|BD C4 49 A6| I镁IIaI
0043E400 C1 C4 49 82|D0 C4 49 12|D3 C4 49 FE|C0 C4 49 25| IIII%
0043E410 CE C4 49 0C|D6 C4 49 09|BA C4 49 2E|C8 C4 49 67| I.I.I.Ig
0043E420 BE C4 49 8A|B9 C4 49 EB|C2 C4 49 D4|CE C4 49 95| IIII
0043E430 C1 C4 49 D1|C7 C4 49 09|BA C4 49 00|C7 C4 49 B9| II.I.I
0043E440 BB C4 49 D1|C7 C4 49 B4|D3 C4 49 5E|B4 C4 49 D1| III^I
0043E450 C7 C4 49 A8|C4 C4 49 A6|C1 C4 49 12|D3 C4 49 B4| IIII
0043E460 D3 C4 49 D0|BA C4 49 41|C2 C4 49 82|D0 C4 49 B1| I泻IAII
0043E470 B8 C4 49 1E|B5 C4 49 27|D2 C4 49 82|D0 C4 49 75| II'IIu
0043E480 D1 C4 49 5E|B4 C4 49 25|CE C4 49 16|B6 C4 49 7B| I^I%II{
0043E490 CE C4 49 7C|BD C4 49 EB|C2 C4 49 27|D2 C4 49 83| I|II'I
0043E4A0 D6 C4 49 12|D3 C4 49 2E|C8 C4 49 1E|B5 C4 49 E6| II.II
0043E4B0 C4 C4 49 C3|BD C4 49 95|C1 C4 49 1E|B5 C4 49 EC| I媒III
0043E4C0 B7 C4 49 B9|BB C4 49 5F|D0 C4 49 83|D6 C4 49 8A| II_II
0043E4D0 B9 C4 49 A6|C1 C4 49 D4|CE C4 49 8B|D3 C4 49 0D| IIII.
0043E4E0 C0 C4 49 E8|B8 C4 49 2D|B8 C4 49 61|BD C4 49 82| II-IaI
0043E4F0 D0 C4 49 12|D3 C4 49 1E|B5 C4 49 7C|BD C4 49 D1| III|I
0043E500 C7 C4 49 7C|BD C4 49 05|CE C4 49 A6|C1 C4 49 5F| I|III_
0043E510 D0 C4 49 1E|B7 C4 49 7B|CE C4 49 0C|D6 C4 49 05| II{I.I
0043E520 BE C4 49 9F|C2 C4 49 B9|BB C4 49 9F|C2 C4 49 D4| IIII
0043E530 CE C4 49 9F|C2 C4 49 EC|B7 C4 49 9F|C2 C4 49 62| IIIIb
0043E540 CF C4 49 9F|C2 C4 49 2D|B8 C4 49 9F|C2 C4 49 0C| II-II.
0043E550 D6 C4 49 9F|C2 C4 49 0D|C0 C4 49 9F|C2 C4 49 05| II.II
0043E560 BE C4 49 9F|C2 C4 49 C3|BD C4 49 9F|C2 C4 49 53| II媒IIS
0043E570 D1 C4 49 9F|C2 C4 49 75|D1 C4 49 9F|C2 C4 49 05| IIuII
0043E580 CE C4 49 9F|C2 C4 49 75|D1 C4 49 9F|C2 C4 49 27| IIuII'
0043E590 D2 C4 49 9F|C2 C4 49 09|BA C4 49 9F|C2 C4 49 B9| II.II
0043E5A0 B5 C4 49 E6|C4 C4 49 09|BA C4 49 8B|D3 C4 49 25| II.II%
0043E5B0 CE C4 49 0D|C0 C4 49 B9|B5 C4 49 E9|C3 C4 49 12| I.III
0043E5C0 D3 C4 49 FE|C0 C4 49 05|CE C4 49 83|D6 C4 49 0D| IIII.
0043E5D0 C0 C4 49 EC|B7 C4 49 D0|BA C4 49 II泻I
虽然DispatchTable的数据很多,但是很多不同的偏移量指向的相同的数据,这是一种保护方式。我们反过来想,如果DispatchTable中每个dword指向的是不同的伪指令,这就意味着每个EAX偏移量指向着唯一的一条伪指令,进一步的来说就是每个ESI值代表着唯一的一条伪指令。那么,如果有人逆向这个算法,就可以知道每个ESI值对应的是哪个伪指令,这样就可以直接读取ESI值而了解VMP的执行伪指令,基本等于半自动识别VMP。一名对VMP经验丰富的人,只要看着VM执行的伪指令盯着EBPSTACK堆栈,就可以理解VM的情况。现在,由于多个ESI值指向相同的伪指令,还有动态EBX解码,将会艰难的多。
我们在OD中寻找一个空间,写一段循环代码,把DispatchTable的数据全部解密出来:
原来的代码:
0043F11F \38F5 CMP CH,DH
0043F121 66:FFC2 INC DX
0043F124 8B1485 DBE14300 MOV EDX,DWORD PTR DS:[EAX*4+43E1DB] ; *
0043F12B F9 STC
0043F12C 84F4 TEST AH,DH
0043F12E 60 PUSHAD
0043F12F ^ E9 CC5EFCFF JMP 0043E100
把最后一条指令修改为:
0043F12F ^\E9 CC5EFCFF JMP 00405000
在00405000添加循环代码:
CPU Disasm
Address Hex dump Command Comments
00405000 60 PUSHAD
00405001 BE DBE14300 MOV ESI,0043E1DB ; DispatchTable地址
00405006 BF 00514000 MOV EDI,00405100 ; 解密循环地址
0040500B B9 00010000 MOV ECX,100
00405010 31DB XOR EBX,EBX
00405012 8B0433 MOV EAX,DWORD PTR DS:[ESI+EBX]
00405015 05 6B197FB6 ADD EAX,B67F196B ; 解密指令只有1条ADD
0040501A 89043B MOV DWORD PTR DS:[EDI+EBX],EAX
0040501D 83C3 04 ADD EBX,4
00405020 49 DEC ECX
00405021 ^ 75 EF JNE SHORT 00405012
00405023 61 POPAD
00405024 E9 8E900200 JMP 0043E100
循环结束后,在00405100中就还原了整个DispatchTable:
CPU Dump
Address Hex dump ASCII
00405100 74 D3 43 00|3B D4 43 00|89 D0 43 00|51 DE 43 00| tC.;C.C.QC.
00405110 BE EA 43 00|70 D7 43 00|E0 EA 43 00|3F E8 43 00| C.pC.C.?C.
00405120 77 EF 43 00|2E D7 43 00|E6 E7 43 00|D2 D7 43 00| wC..C.C.C.
00405130 91 D8 43 00|56 DC 43 00|ED E9 43 00|A5 D3 43 00| C.VC.C.C.
00405140 89 CE 43 00|13 DE 43 00|89 CE 43 00|99 E1 43 00| C.C.C.C.
00405150 24 D5 43 00|54 DD 43 00|98 D1 43 00|00 DB 43 00| $C.TC.C..C.
00405160 ED E9 43 00|E0 EA 43 00|2E D8 43 00|81 CF 43 00| C.C..C.C.
00405170 98 D1 43 00|E0 EA 43 00|00 DB 43 00|56 DC 43 00| C.C..C.VC.
00405180 BD D8 43 00|1F ED 43 00|F6 EC 43 00|70 E7 43 00| C.C.C.pC.
00405190 BD D8 43 00|3F E8 43 00|53 D2 43 00|2E D7 43 00| C.?C.SC..C.
004051A0 2E D8 43 00|C9 CD 43 00|1C D2 43 00|CC D6 43 00| .C.C.C.C.
004051B0 C8 D8 43 00|54 DD 43 00|91 D8 43 00|CA E9 43 00| C.TC.C.C.
004051C0 1F ED 43 00|51 DE 43 00|57 D1 43 00|89 CE 43 00| C.QC.WC.C.
004051D0 78 D9 43 00|78 D9 43 00|2E D7 43 00|C8 D8 43 00| xC.xC..C.C.
004051E0 E6 E7 43 00|2E D7 43 00|89 CE 43 00|ED E9 43 00| C..C.C.C.
004051F0 F5 D2 43 00|11 EB 43 00|56 DC 43 00|3F E8 43 00| C.C.VC.?C.
00405200 CC D6 43 00|74 D3 43 00|BE EA 43 00|CC D6 43 00| C.tC.C.C.
00405210 A5 D3 43 00|A5 E9 43 00|77 EF 43 00|A5 D3 43 00| C.C.wC.C.
00405220 89 D0 43 00|70 E7 43 00|78 D9 43 00|ED E9 43 00| C.pC.xC.C.
00405230 92 EB 43 00|E7 D6 43 00|53 D2 43 00|AC DB 43 00| C.C.SC.C.
00405240 54 DD 43 00|90 E7 43 00|BE DF 43 00|CC D6 43 00| TC.C.C.C.
00405250 BE DF 43 00|EE EF 43 00|BE DF 43 00|C8 D8 43 00| C.C.C.C.
00405260 BE DF 43 00|13 DE 43 00|BE DF 43 00|CA E9 43 00| C.C.C.C.
00405270 BE DF 43 00|51 DE 43 00|BE DF 43 00|A5 D3 43 00| C.QC.C.C.
00405280 BE DF 43 00|6B E0 43 00|BE DF 43 00|98 D1 43 00| C.kC.C.C.
00405290 BE DF 43 00|90 E7 43 00|BE DF 43 00|EE EF 43 00| C.C.C.C.
004052A0 BE DF 43 00|89 D0 43 00|BE DF 43 00|2E D7 43 00| C.C.C..C.
004052B0 BE DF 43 00|CD E8 43 00|BE DF 43 00|7D EC 43 00| C.C.C.}C.
004052C0 BE DF 43 00|53 D2 43 00|70 E7 43 00|89 D0 43 00| C.SC.pC.C.
004052D0 F5 D2 43 00|1F ED 43 00|24 D5 43 00|11 EB 43 00| C.C.$C.C.
004052E0 53 D2 43 00|69 DA 43 00|ED E9 43 00|BE EA 43 00| SC.iC.C.C.
004052F0 98 D1 43 00|BD D8 43 00|A5 E9 43 00|2E D8 43 00| C.C.C..C.
00405300 11 DB 43 00|2E D8 43 00|C9 CD 43 00|ED E9 43 00| C..C.C.C.
00405310 E7 D6 43 00|2E D7 43 00|2E D8 43 00|89 D0 43 00| C..C..C.C.
00405320 CC D6 43 00|11 DB 43 00|ED E9 43 00|7D EC 43 00| C.C.C.}C.
00405330 69 DA 43 00|90 E7 43 00|77 EF 43 00|74 D3 43 00| iC.C.wC.tC.
00405340 99 E1 43 00|D2 D7 43 00|F5 D2 43 00|56 DC 43 00| C.C.C.VC.
00405350 3F E8 43 00|00 DB 43 00|3C E1 43 00|74 D3 43 00| ?C..C.<C.tC.
00405360 6B E0 43 00|24 D5 43 00|3C E1 43 00|1F ED 43 00| kC.$C.<C.C.
00405370 C9 CD 43 00|3C E1 43 00|13 DE 43 00|11 DB 43 00| C.<C.C.C.
00405380 7D EC 43 00|1F ED 43 00|3B D4 43 00|AC DB 43 00| }C.C.;C.C.
00405390 ED E9 43 00|1C D2 43 00|89 CE 43 00|92 EB 43 00| C.C.C.C.
004053A0 ED E9 43 00|E0 EA 43 00|C9 CD 43 00|90 E7 43 00| C.C.C.C.
004053B0 81 CF 43 00|E6 E7 43 00|E7 D6 43 00|56 DC 43 00| C.C.C.VC.
004053C0 92 EB 43 00|EE EF 43 00|7D EC 43 00|99 E1 43 00| C.C.}C.C.
004053D0 89 CE 43 00|51 DE 43 00|2E D7 43 00|00 DB 43 00| C.QC..C..C.
004053E0 89 CE 43 00|57 D1 43 00|24 D5 43 00|CA E9 43 00| C.WC.$C.C.
004053F0 EE EF 43 00|F5 D2 43 00|11 DB 43 00|3F E8 43 00| C.C.C.?C.
00405400 F6 EC 43 00|78 D9 43 00|53 D2 43 00|98 D1 43 00| C.xC.SC.C.
00405410 CC D6 43 00|ED E9 43 00|7D EC 43 00|89 CE 43 00| C.C.}C.C.
00405420 E7 D6 43 00|3C E1 43 00|E7 D6 43 00|70 E7 43 00| C.<C.C.pC.
00405430 11 DB 43 00|CA E9 43 00|89 D0 43 00|E6 E7 43 00| C.C.C.C.
00405440 77 EF 43 00|70 D7 43 00|0A DC 43 00|24 D5 43 00| wC.pC..C.$C.
00405450 0A DC 43 00|3F E8 43 00|0A DC 43 00|57 D1 43 00| .C.?C..C.WC.
00405460 0A DC 43 00|CD E8 43 00|0A DC 43 00|98 D1 43 00| .C.C..C.C.
00405470 0A DC 43 00|77 EF 43 00|0A DC 43 00|78 D9 43 00| .C.wC..C.xC.
00405480 0A DC 43 00|70 D7 43 00|0A DC 43 00|2E D7 43 00| .C.pC..C..C.
00405490 0A DC 43 00|BE EA 43 00|0A DC 43 00|E0 EA 43 00| .C.C..C.C.
004054A0 0A DC 43 00|70 E7 43 00|0A DC 43 00|E0 EA 43 00| .C.pC..C.C.
004054B0 0A DC 43 00|92 EB 43 00|0A DC 43 00|74 D3 43 00| .C.C..C.tC.
004054C0 0A DC 43 00|24 CF 43 00|51 DE 43 00|74 D3 43 00| .C.$C.QC.tC.
004054D0 F6 EC 43 00|90 E7 43 00|78 D9 43 00|24 CF 43 00| C.C.xC.$C.
004054E0 54 DD 43 00|7D EC 43 00|69 DA 43 00|70 E7 43 00| TC.}C.iC.pC.
004054F0 EE EF 43 00|78 D9 43 00|57 D1 43 00|3B D4 43 00| C.xC.WC.;C.
Intel的Little Endian(小尾)方式存储让我们看的非常的别扭。在OD的主窗口(CPU窗口)中来到00405000 .data段,看一下00405100的显示:
004050F2 0000 ADD BYTE PTR DS:[EAX],AL
004050F4 0000 ADD BYTE PTR DS:[EAX],AL
004050F6 0000 ADD BYTE PTR DS:[EAX],AL
004050F8 0000 ADD BYTE PTR DS:[EAX],AL
004050FA 0000 ADD BYTE PTR DS:[EAX],AL
004050FC 0000 ADD BYTE PTR DS:[EAX],AL
004050FE 0000 ADD BYTE PTR DS:[EAX],AL
00405100 ^ 74 D3 JE SHORT 004050D5
00405102 43 INC EBX
00405103 003B ADD BYTE PTR DS:[EBX],BH
00405105 D4 43 AAM 43
00405110 BE EA430070 MOV ESI,700043EA
OD把我们的数据当做代码来显示了。点击右键-->Analysis-->Analyse code Ctrl + A ,弹出对话框是否分析,点击确定。显示如下:
004050FB 00 DB 00
004050FC 00 DB 00
004050FD 00 DB 00
004050FE 00 DB 00
004050FF 00 DB 00
00405100 . 74D34300 DD 0043D374
00405104 . 3BD44300 DD 0043D43B
00405108 . 89D04300 DD 0043D089
0040510C . 51DE4300 DD 0043DE51
00405110 . BEEA4300 DD 0043EABE
00405114 . 70D74300 DD 0043D770
00405118 . E0EA4300 DD 0043EAE0
0040511C . 3FE84300 DD 0043E83F
00405120 . 77EF4300 DD 0043EF77
00405124 . 2ED74300 DD 0043D72E
00405128 . E6E74300 DD 0043E7E6
0040512C . D2D74300 DD 0043D7D2
OD正确的以数据方式显示,并且已经按照我们日常的习惯把数据按照Big Endian(大尾)方式显示。
对于DispatchTable中重复的数据,我们也要把他清除,在刚才00405000的汇编代码下面继续:
CPU Disasm
Address Hex dump Command Comments
00405000 60 PUSHAD
00405001 BE DBE14300 MOV ESI,0043E1DB ; DispatchTable地址
00405006 BF 00514000 MOV EDI,00405100 ; 解密循环地址
0040500B B9 00010000 MOV ECX,100
00405010 31DB XOR EBX,EBX
00405012 8B0433 MOV EAX,DWORD PTR DS:[ESI+EBX]
00405015 05 6B197FB6 ADD EAX,B67F196B ; 解密指令只有1条ADD
0040501A 89043B MOV DWORD PTR DS:[EDI+EBX],EAX
0040501D 83C3 04 ADD EBX,4
00405020 49 DEC ECX
00405021 ^ 75 EF JNE SHORT 00405012
00405023 61 POPAD
00405024 EB 03 JMP SHORT 00405029
00405026 90 NOP
00405027 90 NOP
00405028 90 NOP
00405029 60 PUSHAD
0040502A BE 00514000 MOV ESI,00405100 ; TispatchTable
0040502F BF 005A4000 MOV EDI,00405A00 ; new DispatchTable
00405034 B9 00010000 MOV ECX,100
00405039 BA 00000000 MOV EDX,0
0040503E 8D1C8D 00000000 LEA EBX,[ECX*4]
00405045 8B06 MOV EAX,DWORD PTR DS:[ESI]
00405047 83F8 00 CMP EAX,0
0040504A 74 1A JE SHORT 00405066
0040504C 8907 MOV DWORD PTR DS:[EDI],EAX
0040504E 83C7 04 ADD EDI,4
00405051 83C2 04 ADD EDX,4
00405054 39DA CMP EDX,EBX
00405056 74 0E JE SHORT 00405066
00405058 3B0432 CMP EAX,DWORD PTR DS:[ESI+EDX]
0040505B ^ 75 F4 JNE SHORT 00405051
0040505D C70432 00000000 MOV DWORD PTR DS:[ESI+EDX],0
00405064 ^ EB EB JMP SHORT 00405051
00405066 83C6 04 ADD ESI,4
00405069 49 DEC ECX
0040506A ^ 75 CD JNE SHORT 00405039
0040506C 61 POPAD
0040506D E9 8E900200 JMP 0043E100
第一部分是前面解密代码,第二部分是分别比较00405100中的数据,把相同的全部放00000000,同时把非0的数据存入00405A00中。
执行完这些代码后,00405A00中生成了VM中所有的伪指令,在通过OD把它按照数据显示出来如下:
CPU Disasm
Address Hex dump Command Comments
00405A00 . \74D34300 DD 0043D374
00405A04 . 3BD44300 DD 0043D43B
00405A08 . 89D04300 DD 0043D089
00405A0C . 51DE4300 DD 0043DE51
00405A10 . BEEA4300 DD 0043EABE
00405A14 . 70D74300 DD 0043D770
00405A18 . E0EA4300 DD 0043EAE0
00405A1C . 3FE84300 DD 0043E83F
00405A20 . 77EF4300 DD 0043EF77
00405A24 . 2ED74300 DD 0043D72E
00405A28 . E6E74300 DD 0043E7E6
00405A2C . D2D74300 DD 0043D7D2
00405A30 . 91D84300 DD 0043D891
00405A34 . 56DC4300 DD 0043DC56
00405A38 . EDE94300 DD 0043E9ED
00405A3C . A5D34300 DD 0043D3A5
00405A40 . 89CE4300 DD 0043CE89
00405A44 . 13DE4300 DD 0043DE13
00405A48 . 99E14300 DD 0043E199
00405A4C . 24D54300 DD 0043D524
00405A50 . 54DD4300 DD 0043DD54
00405A54 . 98D14300 DD 0043D198
00405A58 . 00DB4300 DD 0043DB00
00405A5C . 2ED84300 DD 0043D82E
00405A60 . 81CF4300 DD 0043CF81
00405A64 . BDD84300 DD 0043D8BD
00405A68 . 1FED4300 DD 0043ED1F
00405A6C . F6EC4300 DD 0043ECF6
00405A70 . 70E74300 DD 0043E770
00405A74 . 53D24300 DD 0043D253
00405A78 . C9CD4300 DD 0043CDC9
00405A7C . 1CD24300 DD 0043D21C
00405A80 . CCD64300 DD 0043D6CC
00405A84 . C8D84300 DD 0043D8C8
00405A88 . CAE94300 DD 0043E9CA
00405A8C . 57D14300 DD 0043D157
00405A90 . 78D94300 DD 0043D978
00405A94 . F5D24300 DD 0043D2F5
00405A98 . 11EB4300 DD 0043EB11
00405A9C . A5E94300 DD 0043E9A5
00405AA0 . 92EB4300 DD 0043EB92
00405AA4 . E7D64300 DD 0043D6E7
00405AA8 . ACDB4300 DD 0043DBAC
00405AAC . 90E74300 DD 0043E790
00405AB0 . BEDF4300 DD 0043DFBE
00405AB4 . EEEF4300 DD 0043EFEE
00405AB8 . 6BE04300 DD 0043E06B
00405ABC . CDE84300 DD 0043E8CD
00405AC0 . 7DEC4300 DD 0043EC7D
00405AC4 . 69DA4300 DD 0043DA69
00405AC8 . 11DB4300 DD 0043DB11
00405ACC . 3CE14300 DD 0043E13C
00405AD0 . 0ADC4300 DD 0043DC0A
00405AD4 . 24CF4300 DD 0043CF24
这个VM一共有52条伪指令,在本节中我将一一列出这52条伪指令。每个分析VMP的人都有自己对伪指令的命名方式。
移动到EBPSTACK的数据使用PUSH指令,移动到EDISTACK的数据使用MOV指令。在VM中,对数据的操作包括byte和dword,而存储的方式是word和dword,当遇到byte和word交织在一起的时候,可以就把数据作为word操作来看。
下面我以:VM_PUSHw_MEMORYb为例说明我的命名规则:
伪指令的命名统一使用VM_开头;并接上直观的助记符PUSH;EBPSTACK是移动的目的地;MEMORY是移动的来源地;w代表word、b代表byte、dw代表dword;这条指令的表示:这是一条移动指令,把1个byte的数据从内存块移动到EBPSTACK,存储时是按照word来存储。
在VMP的伪指令中包含有大量的花指令和junk code,在本文列出的伪指令都是去除了这些无用代码。
0043DC0A命名:
VM_MOVdw_EDISTACKdw_EBPSTACKdw
代码:
0043DC19 |. F6D8 NEG AL ; *
0043DC26 |. C0C8 07 ROR AL,7 ; *
0043DC34 |. 2C 20 SUB AL,20 ; *
0043DC41 |. 24 3C AND AL,3C ; *
0043E080 |$ 8B55 00 MOV EDX,DWORD PTR SS:[EBP] ; *
0043E086 |. 83C5 04 ADD EBP,4 ; *
0043D3D7 /> /891438 MOV DWORD PTR DS:[EDI+EAX],EDX ; *
功能:
把EBPSTACK栈顶1个dword的数据存储到EDISTACK
0043E7E6命名:
VM_MOVw_EDISTACKw_EBPSTACKw
代码:
0043E7EC 0FB646 FF MOVZX EAX,BYTE PTR DS:[ESI-1] ; *
0043E7F6 28D8 SUB AL,BL ; *
0043E7FE C0C8 05 ROR AL,5 ; *
0043E80C F6D8 NEG AL ; *
0043E816 34 0E XOR AL,0E ; *
0043E822 28C3 SUB BL,AL ; *
0043E82E 66:8B55 00 MOV DX,WORD PTR SS:[EBP] ; *
0043E835 83C5 02 ADD EBP,2 ; *
0043F03F 4E DEC ESI ; *
0043F045 66:891438 MOV WORD PTR DS:[EDI+EAX],DX ; *
功能:
把EBPSTACK栈顶1个word的数据存储到EDISTACK
0043D374命名:
VM_MOVb_EDISTACKb_EBPSTACKw
代码:
0043D377 |. 8A46 FF MOV AL,BYTE PTR DS:[ESI-1] ; *
0043F148 /> \30D8 XOR AL,BL ; *
0043D460 |. FEC0 INC AL ; |*
0043D469 |. C0C8 07 ROR AL,7 ; |*
0043D473 |. FEC0 INC AL ; |*
0043D215 |. 30C3 XOR BL,AL ; *
0043EA9C |. 4E DEC ESI ; *
0043EAA0 |. 66:8B55 00 MOV DX,WORD PTR SS:[EBP] ; *
0043EAAC |. 83C5 02 ADD EBP,2 ; *
0043DBDA /> /881438 MOV BYTE PTR DS:[EDI+EAX],DL ; *
把EBPSTACK栈顶1个word的数据按照byte的方式存储到EDISTACK
0043D21C命名:
VM_PUSHw_IMMEDIATEw
代码:
0043D21F 66:8B46 FE MOV AX,WORD PTR DS:[ESI-2] ; *
0043D22D 86E0 XCHG AL,AH ; *
0043E01A 66:29D8 SUB AX,BX ; *
0043E022 66:05 71F2 ADD AX,0F271 ; *
0043E036 66:F7D8 NEG AX ; *
0043E03D 66:35 A61C XOR AX,1CA6 ; *
0043E054 66:29C3 SUB BX,AX ; *
0043E054 66:29C3 SUB BX,AX ; *
0043E976 8D76 FE LEA ESI,[ESI-2] ; *
0043D094 /66:8945 00 MOV WORD PTR SS:[EBP],AX ; *
功能:
从ESI数据中取得1个word的立即数压入EBPSTACK
0043E83F命名:
VM_PUSHdw_IMMEDIATEdw
代码:
0043E845 . 8B46 FC MOV EAX,DWORD PTR DS:[ESI-4] ; *
0043E84D . 0FC8 BSWAP EAX ; *
0043E852 . 01D8 ADD EAX,EBX ; *
0043E857 . C1C8 04 ROR EAX,4 ; *
0043D952 . 8D76 FC LEA ESI,[ESI-4] ; *
0043D956 . 2D E131FF38 SUB EAX,38FF31E1 ; *
0043D967 . C1C0 0A ROL EAX,0A ; |*
0043D96C . 01C3 ADD EBX,EAX ; |*
0043D970 . 83ED 04 SUB EBP,4 ; |*
0043D710 |$ 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
从ESI数据中获得1个dword的立即数,压入EBPSTACK
0043DB11命名:
VM_PUSHdw_IMMEDIATEw
代码:
0043DB1E 66:8B46 FE MOV AX,WORD PTR DS:[ESI-2] ; *
0043D171 /86E0 XCHG AL,AH ; *
0043E948 66:29D8 SUB AX,BX ; *
0043E951 66:05 71F2 ADD AX,0F271 ; *
0043E95C 66:F7D8 NEG AX ; *
0043E969 8D76 FE LEA ESI,[ESI-2] ; *
0043D62C 66:35 A61C XOR AX,1CA6 ; *
0043D640 \66:29C3 SUB BX,AX ; *
0043D648 98 CWDE ; *
0043D190 83ED 04 SUB EBP,4 ; *
0043D933 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
从ESI数据中获得1个word的立即数,按照dword的方式压入EBPSTACK
0043D978命名:
VM_PUSHw_IMMEDIATEb
代码:
0043D979 . 0FB646 FF MOVZX EAX,BYTE PTR DS:[ESI-1] ; *
0043D97E . 30D8 XOR AL,BL ; *
0043D1ED . FEC8 DEC AL ; *
0043D1F0 . F6D8 NEG AL ; *
0043D1F7 . F6D0 NOT AL ; *
0043D1FD . 30C3 XOR BL,AL ; *
0043CEE8 > /83ED 02 SUB EBP,2 ; *
0043DD79 |. 66:8945 00 MOV WORD PTR SS:[EBP],AX ; |*
0043DD62 /$ 4E DEC ESI ; *
功能:
从ESI数据中获得1个byte的立即数,按照word的方式压入EBPSTACK
0043D3A5命名:
VM_PUSHdw_IMMEDIATEb
代码:
0043D3A7 0FB646 FF MOVZX EAX,BYTE PTR DS:[ESI-1] ; *
0043D3AC 30D8 XOR AL,BL ; *
0043D848 FEC8 DEC AL ; *
0043D855 F6D8 NEG AL ; *
0043D866 F6D0 NOT AL ; *
0043D86D 30C3 XOR BL,AL ; *
0043ED8C 66:98 CBW ; *
0043CF7B 98 CWDE ; *
0043EC00 8D76 FF LEA ESI,[ESI-1] ; *
0043DB94 83ED 04 SUB EBP,4 ; *
0043DB9F 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
从ESI数据中获得1个byte的立即数,按照dword的方式压入EBPSTACK
0043CF24命名:
VM_ADDdw_EBPSTACK
代码:
0043CF2F |. 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043EED3 |. 0145 04 ADD DWORD PTR SS:[EBP+4],EAX ; *
0043CE4F |. 9C PUSHFD ; *
0043CE50 |. 8F4424 3C POP DWORD PTR SS:[ESP+3C] ; *
0043D1BF /> \FF7424 3C PUSH DWORD PTR SS:[ESP+3C] ; *
0043D1C3 |. 8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
把EBPSTACK栈顶的2个dword数据相加,结果存储在[EBP+4],EFLAGS标志存储在栈顶。
例:
0013F97C 8021D2F0 !
0013F980 00000000 ....
VM_ADDdw_EBPSTACK
0013F97C 00000286 ..
0013F980 8021D2F0 !
0043D43B命名:
VM_PUSHdw_MEMORYdw
代码:
0043D43F 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D10A 8B00 MOV EAX,DWORD PTR DS:[EAX] ; *
0043D447 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
把EBPSTACK栈顶数据作为内存地址,从中读取1个dword的数据压入EBPSTACK
0043E9CA命名:
VM_PUSHw_MEMORYw
代码:
0043E9D0 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043E9D9 83C5 02 ADD EBP,2 ; *
0043DEBB 66:36:8B00 MOV AX,WORD PTR SS:[EAX] ; *
0043DDC4 66:8945 00 MOV WORD PTR SS:[EBP],AX ; *
功能:
把EBPSTACK栈顶数据作为内存地址,从中读取1个word的数据压入EBPSTACK
0043D8BD命名:
VM_PUSHw_MEMORYb
代码:
0043D57B |. 8B55 00 MOV EDX,DWORD PTR SS:[EBP]
0043D589 |. 83C5 02 ADD EBP,2 ; *
0043D591 |. 8A02 MOV AL,BYTE PTR DS:[EDX] ; *
0043E70B |. 66:8945 00 MOV WORD PTR SS:[EBP],AX ; *
功能:
把EBPSTACK栈顶数据作为内存地址,从中读取1个byte的数据,按照word的方式压入EBPSTACK
0043DC56命名:
VM_PUSHw_EDISTACKw
代码:
0043DC5C 8A46 FF MOV AL,BYTE PTR DS:[ESI-1] ; *
0043DC66 28D8 SUB AL,BL ; *
0043DC6D C0C8 05 ROR AL,5 ; *
0043EADA 4E DEC ESI ; *
0043EE2E \F6D8 NEG AL ; *
0043EE34 34 0E XOR AL,0E ; *
0043E740 28C3 SUB BL,AL ; *
0043E746 66:8B0438 MOV AX,WORD PTR DS:[EDI+EAX] ; *
0043D9E4 83ED 02 SUB EBP,2 ; *
0043EE44 66:8945 00 MOV WORD PTR SS:[EBP],AX ; *
功能:
从EDISTACK中读取1个word数据压入EBPSTACK
0043CF81命名:
VM_PUSHw_EDISTACKb
代码:
0043CF84 8A46 FF MOV AL,BYTE PTR DS:[ESI-1] ; *
0043CF8E 30D8 XOR AL,BL ; *
0043EE0A \FEC0 INC AL ; *
0043EE11 C0C8 07 ROR AL,7 ; *
0043EE1E FEC0 INC AL ; *
0043D59C 30C3 XOR BL,AL ; *
0043D2CE 4E DEC ESI ; *
0043D2D7 8A0438 MOV AL,BYTE PTR DS:[EDI+EAX] ; *
0043D2E6 83ED 02 SUB EBP,2 ; *
0043D075 66:8945 00 MOV WORD PTR SS:[EBP],AX ; *
功能:
从EDISTACK中读取1个byte数据,按照word方式压入EBPSTACK
0043D72E命名:
VM_PUSHdw_EBP
代码:
0043D72F /. 89E8 MOV EAX,EBP ; *
0043E613 /$ 83ED 04 SUB EBP,4 ; *
0043E61C |. 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
复制EBP指针到EBPSTACK栈顶
例:
EBP 0013F9AC
0013F9AC 00000000 ....
VM_PUSHdw_EBP
0013F9A8 0013F9AC .
0013F9AC 00000000 ....
0043EABE命名:
VM_COPYdw_EBPSTACK
代码:
0043EACC 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043DE1B 36:8B00 MOV EAX,DWORD PTR SS:[EAX] ; *
0043DE25 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
把EBPSTACK栈顶数据作为堆栈地址,从中读取一个dword的数据压入EBPSTACK
例:
0013F998 F99E
0013F99C 02460013 .F
0013F9A0 0000 ...
VM_COPYdw_EBPSTACK
0013F998 0246
0013F99C 02460000 ..F
0013F9A0 0000 ...
0043E790命名:
VM_COPYw_EBPSTACK
代码:
0043E79C |. 8B55 00 MOV EDX,DWORD PTR SS:[EBP] ; *
0043E7A7 |. 83C5 02 ADD EBP,2 ; *
0043E7AE |. 36:8A02 MOV AL,BYTE PTR SS:[EDX] ; *
0043D01B |. 66:8945 00 MOV WORD PTR SS:[EBP],AX ; *
功能:
把EBPSTACK栈顶数据作为堆栈地址,从中读取一个byte的数据,按照word的方式压入EBPSTACK
例:
0013F9A8 0013F9AC .
0013F9AC 0000 ....
VM_COPYw_EBPSTACK
0013F9A8 0000
0013F9AC 0000 ....
0043D198命名:
VM_NANDdw
代码:
0043D1A3 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D1AD 8B55 04 MOV EDX,DWORD PTR SS:[EBP+4] ; *
0043DEAE F7D0 NOT EAX ; *
0043DDE1 /F7D2 NOT EDX ; *
0043CDC2 21D0 AND EAX,EDX ; *
0043E0F8 8945 04 MOV DWORD PTR SS:[EBP+4],EAX ; *
0043D0FB /9C PUSHFD ; *
0043D0FC 8F4424 2C POP DWORD PTR SS:[ESP+2C] ; *
0043EC46 FF7424 34 PUSH DWORD PTR SS:[ESP+34] ; *
0043EC4A 8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
dword版的与非门,从EBPSTACK的栈顶读取2个dword作为操作数,结果存储在第二个操作数位置,EFLAGS标志存储在栈顶。
例:
0013F9A8 00000286 ..
0013F9AC 00000286 ..
VM_NANDdw
0013F9A8 00000282 ..
0013F9AC FFFFFD79 y
0043EB92命名:
VM_NANDw
代码:
0043EB94 |. 66:8B45 00 MOV AX,WORD PTR SS:[EBP] ; *
0043EBA5 |. 66:8B55 02 MOV DX,WORD PTR SS:[EBP+2] ; *
0043EBB3 |. F6D0 NOT AL ; *
0043EBBB |. F6D2 NOT DL ; *
0043EBC1 |. 83ED 02 SUB EBP,2 ; *
0043EBC5 |. 20D0 AND AL,DL ; *
0043EBCD |. 66:8945 04 MOV WORD PTR SS:[EBP+4],AX ; *
0043EBD5 |. 9C PUSHFD ; *
0043D26F |$ FF7424 28 PUSH DWORD PTR SS:[ESP+28] ; *
0043D273 |. 8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
word版的与非门,从EBPSTACK的栈顶读取2个word作为操作数,结果存储在第二个操作数位置,EFLAGS标志存储在栈顶。
例:
EBP 0013F9AA
0013F9A8 0000
0013F9AC 0000 ....
VM_NANDw
0013F9A8 00000286 ..
0013F9AC 00FF ...
0043EB11命名:
VM_ADDw_EBPSTACK
代码:
0043EB14 |. 8A45 00 MOV AL,BYTE PTR SS:[EBP] ; *
0043EB1C |. 83ED 02 SUB EBP,2 ; *
0043EB21 |. 0045 04 ADD BYTE PTR SS:[EBP+4],AL ; *
0043EB26 |. 9C PUSHFD ; *
0043EB27 |. 8F4424 20 POP DWORD PTR SS:[ESP+20] ; *
0043E8F9 |> /FF7424 40 PUSH DWORD PTR SS:[ESP+40] ; *
0043E8FD |. |8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
把EBPSTACK栈顶的2个word数据中的低byte相加,结果存储在第二个操作数位置,EFLAGS标志存储在栈顶。
例:
0013F9AC 000000FF ...
VM_ADDw_EBPSTACK
0013F9A8 0286
0013F9AC 00FF0000 ...
0043DFBE命名:
VM_PUSHdw_EDISTACKdw
代码:
0043DFC1 F6D8 NEG AL ; *
0043DFCD C0C8 07 ROR AL,7 ; *
0043DFDA 2C 20 SUB AL,20 ; *
0043DFDD 24 3C AND AL,3C ; *
0043CE6C 8B1438 MOV EDX,DWORD PTR DS:[EDI+EAX] ; *
0043CE71 83ED 04 SUB EBP,4 ; *
0043CE79 8955 00 MOV DWORD PTR SS:[EBP],EDX ; *
功能:
把1个dword的数据从EDISTACK压入EBPSTACK
0043D7D2命名:
VM_SHRdw_EBPSTACK
代码:
0043D7DA 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D7E6 8A4D 04 MOV CL,BYTE PTR SS:[EBP+4] ; *
0043D4F8 83ED 02 SUB EBP,2 ; *
0043D504 D3E8 SHR EAX,CL ; *
0043F17D \8945 04 MOV DWORD PTR SS:[EBP+4],EAX ; *
0043EA2E 9C PUSHFD ; *
0043EA30 FF7424 20 PUSH DWORD PTR SS:[ESP+20] ; *
0043EA34 8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
把EBPSTACK栈顶1个dword作为操作数,[EBP+4]作为移动位数,逻辑右移。结果dword存储在第二个操作数和第一个操作数的高byte,EFLAGS标志存储在栈顶。
例:
0013F99C 0040
0013F9A0 00040000 ...
VM_SHRdw_EBPSTACK
0013F99C 00000202 ..
0013F9A0 00000004 ...
0043E9A5命名:
VM_SHLdw_EBPSTACK
代码:
0043E9A9 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043E9B5 8A4D 04 MOV CL,BYTE PTR SS:[EBP+4] ; *
0043E0B2 >83ED 02 SUB EBP,2 ; *
0043E0BC D3E0 SHL EAX,CL ; *
0043CDEA 8945 04 MOV DWORD PTR SS:[EBP+4],EAX ; *
0043DD1A \9C PUSHFD
0043DD1B 8F4424 28 POP DWORD PTR SS:[ESP+28]
0043DD24 FF7424 2C PUSH DWORD PTR SS:[ESP+2C] ; *
0043DD28 8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
把EBPSTACK栈顶1个dword作为操作数,[EBP+4]作为移动位数,逻辑左移。结果dword存储在第二个操作数和第一个操作数的高byte,EFLAGS标志存储在栈顶。
0043DE51命名:
VM_SHRDdw_EBPSTACK
代码:
0043DE5D 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043DE69 8B55 04 MOV EDX,DWORD PTR SS:[EBP+4] ; *
0043DE6E 8A4D 08 MOV CL,BYTE PTR SS:[EBP+8] ; *
0043DE73 83C5 02 ADD EBP,2 ; *
0043DE7A 0FADD0 SHRD EAX,EDX,CL ; *
0043D38F 8945 04 MOV DWORD PTR SS:[EBP+4],EAX ; *
0043D66C 9C PUSHFD ; *
0043D66D 8F4424 34 POP DWORD PTR SS:[ESP+34] ; *
0043D67F FF7424 40 PUSH DWORD PTR SS:[ESP+40] ; *
0043D683 8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
EBPSTACK双精度右移指令,执行完毕后,结果和EFLAGS存储到EBPSTACK
0043D524命名:
VM_SHLDdw_EBPSTACK
代码:
0043D529 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D537 8B55 04 MOV EDX,DWORD PTR SS:[EBP+4] ; *
0043D545 8A4D 08 MOV CL,BYTE PTR SS:[EBP+8] ; *
0043D550 83C5 02 ADD EBP,2 ; *
0043D558 0FA5D0 SHLD EAX,EDX,CL ; *
0043D637 8945 04 MOV DWORD PTR SS:[EBP+4],EAX ; *
0043CED3 9C PUSHFD
0043D8F4 \FF7424 34 PUSH DWORD PTR SS:[ESP+34] ; *
0043D8F8 8F45 00 POP DWORD PTR SS:[EBP] ; *
功能:
EBPSTACK双精度左移指令,执行完毕后,结果和EFLAGS存储到EBPSTACK
0043D089命名:
VM_JMP
代码:
0043D722 8B75 00 MOV ESI,DWORD PTR SS:[EBP] ; *
0043EF1F \83C5 04 ADD EBP,4 ; *
0043E6A9 89F3 MOV EBX,ESI ; *
0043E6B8 0375 00 ADD ESI,DWORD PTR SS:[EBP] ; *
功能:
把EBPSTACK栈顶地址移动到ESI,重新初始化EBX和ESI。
0043EF77命名:
VM_EBPSTACK_CALL
代码:
0043EF7B 0FB646 FF MOVZX EAX,BYTE PTR DS:[ESI-1] ; *
0043EF82 30D8 XOR AL,BL ; *
0043EF8D FEC8 DEC AL ; *
0043EF99 F6D8 NEG AL ; *
0043EFAF 8D76 FF LEA ESI,[ESI-1] ; *
0043EFB3 F6D0 NOT AL ; *
0043EFC4 30C3 XOR BL,AL ; *
0043EFCD 0FB6C8 MOVZX ECX,AL ; *
0043EFDC 894D FC MOV DWORD PTR SS:[EBP-4],ECX ; *
0043ECEA 31C0 XOR EAX,EAX ; *
0043E0C6 87448D 00 XCHG DWORD PTR SS:[ECX*4+EBP],EAX ; * parameter
0043E0CD 894424 24 MOV DWORD PTR SS:[ESP+24],EAX ; *
0043EE89 83E9 01 SUB ECX,1 ; *
0043EE9C ^\0F85 3FFEFFFF JNE 0043ECE1 ; *
0043CF5B 29C0 SUB EAX,EAX ; *
0043CF6A C74424 04 B7EE4 MOV DWORD PTR SS:[ESP+4],0043EEB7 ; *
0043CF60 8745 00 XCHG DWORD PTR SS:[EBP],EAX ; *
0043DDF9 894424 08 MOV DWORD PTR SS:[ESP+8],EAX ; *
0043DDFD FF7424 04 PUSH DWORD PTR SS:[ESP+4] ; *
0043DE0C FF7424 34 PUSH DWORD PTR SS:[ESP+34] ; *
0043DE10 C2 3800 RETN 38 ; VM_APICALL
功能:
VM中最复杂的伪指令,用于系统API调用和程序过程调用。ESI数据中取得参数的个数,EAX循环取得参数,压入ESP指针指向的常规堆栈。大量使用[ESP+X]的方式调用,掺杂着废压栈操作,静态看代码难以看出。返回地址是常量压入的0043EEB7。这条伪指令涉及内容众多,分支庞大,系统API调用和程序过程调用的走向都是不同的,在后面章节详述。我这里列举的是一次只有1个参数的系统API调用
0043D891命名:
VM_MOVdw_MEMORYdw_EBPSTACKdw
代码:
0043D897 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D8A1 8B55 04 MOV EDX,DWORD PTR SS:[EBP+4] ; *
0043D8A6 83C5 08 ADD EBP,8 ; *
0043D8AA 8910 MOV DWORD PTR DS:[EAX],EDX ; *
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个dword存储到地址内
0043EFEE命名:
VM_MOVdw_MEMORYdw_EBPSTACKdw
代码:
0043EFF3 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043F005 8B55 04 MOV EDX,DWORD PTR SS:[EBP+4] ; *
0043F010 83C5 08 ADD EBP,8 ; *
0043D335 36:8910 MOV DWORD PTR SS:[EAX],EDX ; *
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个dword存储到地址内。与上一条伪指令完全相同
0043D157命名:
VM_MOVdw_MEMORYdw_EBPSTACKdw
代码:
0043D159 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D169 8B55 04 MOV EDX,DWORD PTR SS:[EBP+4] ; *
0043CDF7 83C5 08 ADD EBP,8 ; *
0043CE09 26:8910 MOV DWORD PTR ES:[EAX],EDX ; *
EBPSTACK栈顶数据作为地址,把栈顶的第二个dword存储到地址内。与上两条伪指令完全相同
0043E9ED命名:
VM_MOVw_MEMORYw_EBPSTACKw
代码:
0043E9F7 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043E9FD 66:8B55 04 MOV DX,WORD PTR SS:[EBP+4] ; *
0043EA02 83C5 06 ADD EBP,6 ; *
0043EA0D 66:8910 MOV WORD PTR DS:[EAX],DX ; *
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个word存储到地址内
0043D6CC命名:
VM_MOVb_MEMORYb_EBPSTACKb
代码:
0043D6D3 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D6DB 8A55 04 MOV DL,BYTE PTR SS:[EBP+4] ; *
0043EC6C 83C5 06 ADD EBP,6 ; *
0043D495 36:8810 MOV BYTE PTR SS:[EAX],DL ; *
功能:
EBPSTACK栈顶数据作为地址,把栈顶的第二个byte存储到地址内
0043CE89命名:
VM_HASH
代码:
0043CE98 8B55 00 MOV EDX,DWORD PTR SS:[EBP] ; *
0043CEA0 83C5 04 ADD EBP,4 ; *
0043CEA6 31C0 XOR EAX,EAX ; *
0043DCC0 89C1 MOV ECX,EAX ; *
0043E6FA C1E0 07 SHL EAX,7 ; *
0043E701 C1E9 19 SHR ECX,19 ; *
0043D2BD /09C8 OR EAX,ECX ; *
0043D7EF \3202 XOR AL,BYTE PTR DS:[EDX] ; *
0043D7F2 42 INC EDX ; *
0043DD12 FF4D 00 DEC DWORD PTR SS:[EBP] ; *
0043F023 ^\0F85 7FDEFFFF JNE 0043CEA8 ; *
0043D9FA 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
计算一段数据的HASH值,EBPSTACK栈顶第一个dword是数据地址,第二个dword是数据大小
0043DE13命名:
VM_MOVdw_EBPreg_EBPSTACK
代码:
0043F134 \8B6D 00 MOV EBP,DWORD PTR SS:[EBP] ; *
功能:
给EBP寄存器赋值EBPSTACK栈顶数据
0043DD54命名:
VM_FS:[EBPSTACK]
代码:
0043DD5A 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043F10E 64:8B00 MOV EAX,DWORD PTR FS:[EAX] ; *
0043F112 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
功能:
读取FS[X]数据,X=EBPSTACK栈顶数据
0043D8C8命名:
VM_SEH
代码:
0043D8CF 8B45 00 MOV EAX,DWORD PTR SS:[EBP] ; *
0043D8DE 8B55 04 MOV EDX,DWORD PTR SS:[EBP+4] ; *
0043D8E7 83C5 08 ADD EBP,8 ; *
0043D243 64:8910 MOV DWORD PTR FS:[EAX],EDX ; *
功能:
给FS[X]传递Y数据,X=EBPSTACK栈顶数据,Y=EBPSTACK栈顶第2个数据。在实践中都是用于给FS[0]赋值,构建SEH
0043DA69命名:
VM_EXIT
代码:
0043DA6F 89EC MOV ESP,EBP ; *
0043DA73 58 POP EAX ; *
0043DA7E 59 POP ECX ; *
0043DA87 9D POPFD ; *
0043DA8D 5D POP EBP ; *
0043CDB1 /59 POP ECX ; *
0043CDB8 8B5C24 08 MOV EBX,DWORD PTR SS:[ESP+8] ; *
0043F068 8B6C24 14 MOV EBP,DWORD PTR SS:[ESP+14] ; *
0043F06D 8B4424 38 MOV EAX,DWORD PTR SS:[ESP+38] ; *
0043F06D 8B4424 38 MOV EAX,DWORD PTR SS:[ESP+38] ; *
0043DC99 8B7C24 44 MOV EDI,DWORD PTR SS:[ESP+44] ; *
0043DCA7 5E POP ESI ; *
0043DCB6 FF7424 04 PUSH DWORD PTR SS:[ESP+4] ; *
0043DCBA C2 0800 RETN 8 ; *
功能:
给各个寄存器赋值EBPSTACK中的数据,EBPSTACK中的最后一个数据是跳转地址
0043EC7D命名:
VM_MOVdw_EFLreg_EBPSTACK
代码:
0043EC80 FF75 00 PUSH DWORD PTR SS:[EBP] ; *
0043EC83 8F4424 08 POP DWORD PTR SS:[ESP+8] ; *
0043EC8E FF7424 28 PUSH DWORD PTR SS:[ESP+28] ; *
0043EC92 9D POPFD ; *
功能:
给EFLAGE寄存器赋值EBPSTACK栈顶数据
在F7跟踪加壳记事本的过程中,并不是所有的伪指令都使用到了,以下是没有被执行到的伪指令:
00405A14 . 70D74300 DD 0043D770
00405A18 . E0EA4300 DD 0043EAE0
00405A48 . 99E14300 DD 0043E199
00405A58 . 00DB4300 DD 0043DB00
00405A5C . 2ED84300 DD 0043D82E
00405A68 . 1FED4300 DD 0043ED1F
00405A6C . F6EC4300 DD 0043ECF6
00405A70 . 70E74300 DD 0043E770
00405A74 . 53D24300 DD 0043D253
00405A78 . C9CD4300 DD 0043CDC9
00405A94 . F5D24300 DD 0043D2F5
00405AA4 . E7D64300 DD 0043D6E7
00405AA8 . ACDB4300 DD 0043DBAC
00405AB8 . 6BE04300 DD 0043E06B
00405ABC . CDE84300 DD 0043E8CD
00405ACC . 3CE14300 DD 0043E13C
由于没有实际的走过这些伪指令,静态分析后觉得,有个别伪指令的代码怕提取错了。把这些指令写成简介模式:
0043D770
EBPSTACK的byte逻辑右移指令
0043EAE0
VM_JMP跳转指令,重新给ESI赋值EBPSTACK栈顶数据
0043E199
复制EBPSTACK栈顶1个word的数据
0043DB00
把EBPSTACK栈顶数据作为地址,读取其中1个word的数据压入EBPSTACK
0043D82E
VM_DIV除法指令
0043ED1F
CPUID指令,结果压入EBPSTACK。
0043ECF6
把EBPSTACK数据1个byte移动到栈顶内存地址内
0043E770
给EBP寄存器的低word位赋值栈顶数据
0043D253
把SS段寄存器压入EBPSTACK栈顶
0043CDC9
另一种方式的word版NAND,不过这个是在EBPSTACK堆栈内完成运算过程
0043D2F5
EBPSTACK的byte逻辑左移指令
0043D6E7
EBPSTACK的word逻辑左移指令
0043DBAC
EBPSTACK的word逻辑右移指令
0043E06B
EBPSTACK的word加法
0043E8CD
把EAX和EDX压入EBPSTACK
0043E13C
把EBPSTACK数据1个word移动到栈顶内存地址内
到这里,所有的伪指令都罗列完毕,真的是体力活呀!
2.综合运用
2.1.常见伪指令组合
在VMP的伪指令的执行中有一些常见的组合套路,熟悉它们能让我们在跟踪VMP时更加的得心应手。这些组合与操作数的长度是无关的,下面的伪指令将去掉b w dw等标记。在例子部分,我将使用dword操作数来举例,直观明了。
2.1.1.
VM_PUSH_EBP ;复制EBP指针到EBPSTACK栈顶
VM_COPY_EBPSTACK ;把EBPSTACK栈顶数据作为堆栈地址,从中读取一个数据压入EBPSTACK
这两条指令是VMP中结合的极其紧密的组合,它们几乎总是一起出现的,用于把EBPSTACK堆栈中的数据复制起来到EBPSTACK。而很多情况下它们复制的就是原来的栈顶数据。在使用NAND来完成NOT(A)的运算中,它们是必备的前奏。凡是需要把操作数一个变两个的地方都有它们的身影。
例:
EBP 0013F9AC
0013F9AC 00000000 ....
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSH_EBP
EBP 0013F9A8
0013F9A8 0013F9AC .
0013F9AC 00000000 ....
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_COPY_EBPSTACK
EBP 0013F9A8
0013F9A8 00000000 ....
0013F9AC 00000000 ....
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
2.1.2.
VM_NAND|VM_ADD_EBPSTACK|VM_SHLD_EBPSTACK|VM_SHR_EBPSTACK等等
VM_MOV_EDISTACK_EBPSTACK ;把1个数据从EBPSTAK栈顶移动到EDISTACK,使用EAX作为偏移量
在VMP所有的运算伪指令中都是统一的模式,运算后的EFLAGS寄存器值位于EBPSTACK栈顶,运算结果位于紧接栈顶的[EBP+4]。在运算结束后,跟上一条VM_MOV_EDISTACK_EBPSTACK把运算后的标志位移动到EDISTACK,在很多时候,这都是一条废指令操作,纯粹是为去掉栈顶数据,以便继续操作运算结果。
如果接下来VM进行检测标志位的相关操作,这条指令就变得异常重要。例如:在对系统函数的CC码int3断点检测中,取出系统函数开头的第一个byte数据XX,把它与CC相减,再跟上一个ZF标志位检测+跳转。在这个时候反过来,运算结果完全无用,而我们一定要在移动指令的EAX偏移量哪里下好断点,观察好EFLAGS寄存器值的走向与来源。
2.1.3.
在进行跳转时,围绕VM_JMP的前后,有大量无价值的数据移动操作。假设现在我们刚进行了一次条件判断,VM刚刚把要跳转的地址确定并解密出来:
EBP 0013F9A8
0013F9A8 00000202 .. ;最后一次解密运算得到的EFLAGS
0013F9AC 0043651A eC. ;跳转地址
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
EBP 0013F9B0
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
EBP 0013F980
0013F980 8021D2F0 !
0013F984 0013F9C0 .
0013F988 00000246 F..
0013F98C 00000020 ...
0013F990 000359F4 Y.
0013F994 0013F9CC .
0013F998 00400000 ..@. ; OFFSET NOTEPAD
0013F99C 00000000 ....
0013F9A0 004253CD SB. ; RETURN from NOTEPAD.004255DB to NOTEPAD.004253CD
0013F9A4 000359F4 Y.
0013F9A8 00400000 ..@. ; 该带着走的数据都要在EBPSTACK里面带着走,到这里还没有完毕的。
0013F9AC 0043651A eC. ;还有其他的数据要放入,8021D2F0要隐藏一下
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F97C 7FDE2D10 -
0013F980 8021D2F0 !
0013F984 0013F9C0
VM_ADDdw_EBPSTACK
0013F97C 00000247 G..
0013F980 00000000 ....
0013F984 0013F9C0
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F980 00000000 ....
0013F984 0013F9C0
VM_PUSHdw_EDISTACKdw
VM_PUSHdw_EDISTACKdw
0013F978 0043651A eC.
0013F97C 00000000 ....
0013F980 00000000 ....
0013F984 0013F9C0 .
0013F988 00000246 F..
0013F98C 00000020 ...
0013F990 000359F4 Y.
0013F994 0013F9CC .
0013F998 00400000 ..@. ; OFFSET NOTEPAD
0013F99C 00000000 ....
0013F9A0 004253CD SB. ; RETURN from NOTEPAD.004255DB to NOTEPAD.004253CD
0013F9A4 000359F4 Y.
0013F9A8 00400000 ..@. ; OFFSET NOTEPAD.B
0013F9AC 0043651A eC.
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_JMP ;带着14个数据,VM终于跳转,除了栈顶0043651A放入ESI,其他13个数据要重新保存
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F980 00000000 ....
0013F984 0013F9C0 .
VM_PUSHdw_IMMEDIATEdw
0013F97C 8021D2F0 !
0013F980 00000000 ....
0013F984 0013F9C0 .
VM_ADDdw_EBPSTACK
0013F97C 00000286 ..
0013F980 8021D2F0 !
0013F984 0013F9C0 . ;重新恢复出来
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw ;到这里停一下,搞个小运算,原EDX=000359F4 XOR 4DFD2FC2
0013F990 000359F4 Y.
0013F994 0013F9CC .
0013F998 00400000 ..@. ; OFFSET NOTEPAD.B
0013F99C 00000000 ....
0013F9A0 004253CD SB. ; RETURN from NOTEPAD.004255DB to NOTEPAD.004253CD
0013F9A4 000359F4 Y.
0013F9A8 00400000 ..@. ; OFFSET NOTEPAD.B
0013F9AC 0043651A eC.
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_EBP
0013F98C 0013F990 .
0013F990 000359F4 Y.
0013F994 0013F9CC .
VM_COPYdw_EBPSTACK
0013F98C 000359F4 Y.
0013F990 000359F4 Y.
0013F994 0013F9CC .
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F990 000359F4 Y.
0013F994 0013F9CC .
VM_PUSHdw_EBP
0013F98C 0013F990 .
0013F990 000359F4 Y.
0013F994 0013F9CC .
VM_COPYdw_EBPSTACK
0013F98C 000359F4 Y.
0013F990 000359F4 Y.
0013F994 0013F9CC .
VM_NANDdw
0013F98C 00000282 ..
0013F990 FFFCA60B
0013F994 0013F9CC .
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F990 FFFCA60B
0013F994 0013F9CC .
VM_PUSHdw_IMMEDIATEdw
0013F98C B202D03D =
0013F990 FFFCA60B
0013F994 0013F9CC .
VM_NANDdw
0013F98C 00000206 ..
0013F990 000109C0 ..
0013F994 0013F9CC .
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F990 000109C0 ..
0013F994 0013F9CC .
VM_PUSHdw_IMMEDIATEdw
0013F98C 4DFD2FC2 /M
0013F990 000109C0 ..
0013F994 0013F9CC .
VM_PUSHdw_EDISTACKdw
0013F988 000359F4 Y.
0013F98C 4DFD2FC2 /M
0013F990 000109C0 ..
0013F994 0013F9CC .
VM_NANDdw
0013F988 00000286 ..
0013F98C B2008009 ..
0013F990 000109C0 ..
0013F994 0013F9CC .
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F98C B2008009 ..
0013F990 000109C0 ..
0013F994 0013F9CC .
VM_NANDdw
0013F98C 00000206 ..
0013F990 4DFE7636 6vM
0013F994 0013F9CC .
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
VM_MOVdw_EDISTACKdw_EBPSTACKdw
EBP 0013F9B0
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
每一次VM_JMP跳转,都要带着14个数据转,而其中呢VM还要搞上一点暗码转移。以后看到是VM_JMP跳转的状况,就是看着EBP指针,哗哗哗的让它执行,完毕了才停下来。中间的操作完全可以无视,我也是为了完整的表达才把它粘贴出了代码,实际看的时候,不用管。整个过程:要带着走的数据移到EBPSTACK-->VM_JMP跳转-->重新把数据保存到EDISTACK。关于其中000359F4 XOR 4DFD2FC2的过程,请参考下一节2.2.NAND。
由于其他的组合都和NAND或标志位检测+跳转相关,放在下两节中。这一节中的3个组合熟悉后,已经可以无视掉一部分VM的操作。
本文导航
- 第1页: 首页
- 第2页: VMProtect虚拟机简介
- 第3页: VMProtect2.04伪指令汇总
- 第4页: .NAND(与非门)
- 第5页: NOTEPAD全程跟踪
- 第6页: VMP外壳函数获取
- 第7页: 虚拟执行环境与调试器检测