此文章是我學習VMProtect2.04加殼程序的心得。在調試的過程中,尤其是初期,參考和查閱了N多的網絡資料,在此一并表示感謝。在計算機書中,最著名的兩類書名當屬:XXXX從入門到精通和21天學會XXXX,我在仔細思考,最終覺得還是前者比較有吸引力。OD的UDD文件的使用,在D:盤下新建文件夾Unpack,把加殼記事本放入文件夾內,把UDD文件放入OD的UDD文件夾內。OD打開加殼記事本后,UDD文件會自動生效。
聲明:本文在非盈利性用途可自由傳播。
軟件:正版VMProtect2.04加密的Win98記事本
加密選項:除了 編譯--調試模式與水印 以外,全部打鉤;虛擬計算機--數量為默認值1;編譯類型:超級(變異+虛擬)
調試器:官網下載的OllyDbg2.0
加殼程序下載:NOTEPAD.rar下載此附件需要消耗2Kx,下載中會自動扣除。
UDD文件:NOTEPAD-udd.rar下載此附件需要消耗2Kx,下載中會自動扣除。(這是是整理了偽指令的UDD文件,后續(xù)還會更新)
本文的結構:
序言
文章簡介
1.基礎知識
1.1.VMProtect虛擬機簡介
1.2.VM堆棧
1.3.偽指令匯總
2.綜合運用
2.1.常見偽指令組合
2.2.NAND(與非門)
2.3.EFLAGS標志位檢測+跳轉
3.NOTEPAD全程跟蹤
3.1.TLS
3.2.VMP外殼函數獲取
3.3.虛擬執(zhí)行環(huán)境與調試器檢測
3.4.HASH值分塊檢測與API獲取
3.5.重點解密循環(huán)
4.實驗室(暫定)
尾聲
1.基礎知識
1.1.VMProtect虛擬機簡介
虛擬機加密,是指像VMP這樣的保護程序,它會把源程序的X86指令變成自定義的偽指令,等到執(zhí)行的時候,VMP內置在保護程序中的VM就會啟動,讀取偽指令,然后解析執(zhí)行。
VMP是一個堆棧虛擬機,它的一切操作都是基于堆棧傳遞的。在VMP中,每一個偽指令就是一個handler,VM中有一個核心的Dispatch部分,它通過讀取程序的bytecode,然后在DispatchiTable里面定位到不同的handler中執(zhí)行。絕大多數情況下,在一個handler中執(zhí)行完成后,程序將回到Dispatch部分,然后到next handler中執(zhí)行。
http_imgload.jpg下載此附件需要消耗2Kx,下載中會自動扣除。
在上面的框架中,核心的部件就是Dispatch部分,下面并列的部件就是handlers。
經過VMP加密的X86指令,一條簡單的指令被分解成數條VMP的偽指令,它按照自己的偽指令排列去實現原指令的功能,在加上其他的花指令混亂等等,你將完全看不到源程序指令。VMP自帶的各種機制都不再是以X86指令的形式去實現,而是用自己的偽指令去測試。
在VMP的VM運行過程中,各個寄存器的基本用途是:EBP和EDI是VM堆棧指針(不是常規(guī)的堆棧);ESI是偽指令指針(相當于常規(guī)的EIP);EAX是VM解密數據的主運算寄存器;EBX是VM解密數據的輔運算寄存器;ECX是常規(guī)的循環(huán)計數器;ESP是常規(guī)的堆棧棧頂指針。EDX是讀取偽指令表數據;
EDI、EBP分別指向VM堆棧的上下限位置,EBP指向堆棧的下限并向上發(fā)展,EDI指向堆棧的上限并使用[EDI+EAX]的方式向下發(fā)展;ESI指向的內存塊里包括要執(zhí)行的偽指令序列,而不同的是,當VM要是使用到立即數時,也是從ESI讀取。可見ESI內存塊里面是精心構建的數據塊,只有VM自身執(zhí)行過程中,才能知道下一個數據是代表偽指令還是立即數;在VM運算中EAX寄存器很多時候通常只有AL參與運算然后在存取時再以AX或EAX得方式存;EBX在很多加密數據運算中,都會參與到EAX值的計算中,協助運算中正確的值。而每次EAX的值運算結束后,反過來會計算好下一次運算中EBX的值。所以EBX的數據一旦出錯,下一個數據解密必然錯誤;在VM運行中,通常一切操作都是在VM堆棧內完成的,所以絕大多數情況下對ESP的操作都是花指令或junk code。在一些虛擬與現實(比如說調用系統(tǒng)函數)交接的地方,系統(tǒng)并不知道VM堆棧的存在,這就需要把數據(比如系統(tǒng)函數的調用參數)移動到常規(guī)ESP棧頂。EDX是一個較少使用的寄存器,只在一些解密循環(huán)里面參與運算。而它的一個主要的運用是在DISPATCH部件里,根據ESI的值來獲取DispatchTable的數據,讓VM執(zhí)行下一條偽指令。
1.2.VM堆棧
VMP的VM是基于堆棧的虛擬家,理解好VM的堆?臻g劃分和操作,是理解整個VM運行的基礎。
VMProtect2.04加殼程序是從TLS開始運行的,我們首先點擊OD的options菜單,修改Startup and exit選項,讓OD中斷在TLS callback里。加殼程序運行后,VMP初始化VM,并進入DISPATCH部分。這里我們就以初始化后的堆棧為例。
VM的堆棧一共使用61個DWORD,上下分別有2個堆棧指針,下面為EBP指針,上面為EDI指針。下面是VM初始化時,給EDI和EBP指針賦值后的堆棧。
EDI=0013F8BC
EBP=0013F9B0
CPU Stack
Locked Value ASCII Comments
0013F8BC 009539E8 9. ;這里是EDI指向
0013F8C0 00950000 ...
0013F8C4 00150000 ...
0013F8C8 00000080 ...
0013F8CC 019314D6
0013F8D0 0013F8A8 .
0013F8D4 7C92E920 |
0013F8D8 00000000 ....
0013F8DC 00000000 ....
0013F8E0 00000000 ....
0013F8E4 FFFFFFFF
0013F8E8 7C98FEFF |
0013F8EC 7C00ADE7 .|
0013F8F0 00000000 ....
0013F8F4 00150000 ...
0013F8F8 0013F6F0 .
0013F8FC 0013F940 @.
0013F900 0013F944 D.
0013F904 7C92E920 |
0013F908 7C9301E0 |
0013F90C FFFFFFFF
0013F910 7C9301DB |
0013F914 7C9314D6 |
0013F918 7C931514 |
0013F91C 7C99E120 |
0013F920 7C9314EA |
0013F924 5ADF1158 XZ
0013F928 00000001 ...
0013F92C 00000000 ....
0013F930 7FFDA000 .
0013F934 7FFDF000 .
0013F938 00158070 p.
0013F93C 0013F890 .
0013F940 00000000 ....
0013F944 0043D759 YC.
0013F948 0000E9ED ..
0013F94C 409B0002 .@
0013F950 00000020 ...
0013F954 0013F9CC .
0013F958 0013F96C l.
0013F95C 0043E9ED C.
0013F960 000359F4 Y.
0013F964 00000020 ...
0013F968 004253CD SB.
0013F96C 409B0000 ..@
0013F970 00000020 ...
0013F974 0013F9CC .
0013F978 0013F98C .
0013F97C 00000000 .... ;這里是EBP指向
0013F980 00000000 .... ;這里是VM初始化保存的各個寄存器
0013F984 00000246 F..
0013F988 000359F4 Y.
0013F98C 00000020 ...
0013F990 00000000 ....
0013F994 0013F9CC .
0013F998 004253CD SB.
0013F99C 000359F4 Y.
0013F9A0 00400000 ..@.
0013F9A4 0013F9C0 .
0013F9A8 C456C619 V ;這里是VMP的2個加密數據
0013F9AC 2EF6420A .B.
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;這里是TLS進來時的棧頂
關于2個加密數據和初始化的過程我們后續(xù)來說,這里我們主要關注VM的堆棧劃分。
我把上面的EDI指向的堆棧稱為EDISTACK,把EBP指向的堆棧稱為EBPSTACK。在VM中,EBPSTACK是運算區(qū),各類數據的運算操作在這里完成;EDISTACK是存儲區(qū)包括長期存儲數據和臨時存儲EBPSTACK的運算數。
下面我們來看一條數據移動偽指令:
命名:
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 ; *
功能:
把1個dword的數據從EBPSTAK棧頂移動到EDISTACK,使用EAX作為偏移量
在EDISTACK的數據移動中,使用[EDI+EAX]的方式來存儲與獲取各個值。通過計算不同的EAX的值,可以到達EDISTACK中不同位置。在計算EAX值時,實際真正計算的是AL的值,我們來考慮一下AL的最小值和最大值,AL=00時[EDI+EAX]=[0013F8BC+00000000]=0013F8BC,AL=FF時[EDI+EAX]=[0013F8BC+000000FF]=0013F9BB,這是使用[EDI+EAX]可以讀取的上下限。但是,在VM的AL值計算過程中,最后有一條AND AL,0x3C指令,0x3C=00111100bit由于這條指令的限制,無論AL為任何值(從00000000bit到11111111bit),通過AND操作,只能有1111bit的活動空間大小,1111bit相當于16,所以EDISTACK最多可以讀取16個dword;由于00111100bit最后兩個00位的限制,任何數字與它AND后,后兩位都必然為0,變成與4對齊的值,說明VM都是按照0013F8BC 0013F8C0 0013F8C4 0013F8C8這樣的4對齊來讀取。在讀取時,VM可以讀取byte word dword,但是VM將不會去讀取0013F8BE。
由于EDISTACK堆棧向下發(fā)展,EBPSTACK堆棧向上發(fā)展,EDISTACK有0x3C控制著范圍,而EBPSTACK是操作區(qū),沒有硬性的范圍控制。為了預防兩個空間相撞,在每次往EBPSTACK移動數據后,VM都有相應的邊界檢測指令如下:
0043CE5A |. 8D47 50 LEA EAX,[EDI+50] ; *
0043EE5D |. 39C5 CMP EBP,EAX ; *
0043F08C |.^\0F87 29F6FFFF JA 0043E6BB ; *
比較結果 大于 ,這個正常的情況,在這個VM跟蹤過程中,我沒有發(fā)現一次小于的情況。如果小于,也就是EBPSTACK棧頂已經到達[EDI+50]位置,VM將會重新分配堆?臻g。0x50的偏移量比0x3C的偏移量多5個dword的緩沖區(qū)。我們來手動修改EBP指針,看看VM的對于兩個堆?臻g即將相撞的處理情況:
CPU Disasm
Address Hex dump Command Comments
0043F092 |. 52 PUSH EDX ;
0043D6C1 |. 8D5424 08 LEA EDX,[ARG.2] ; *EDX獲得的是原來EDI指針地址0013F8BC
0043DF38 |. 8D4F 40 LEA ECX,[EDI+40] ; *0x40的偏移量是0x3C的偏移量數據1個dword結束后的位置
0043DF46 |. 29D1 SUB ECX,EDX ; *減法計算出數據存儲量
0043DF4B |. 8D45 80 LEA EAX,[EBP-80] ; *增加0x80的空間
0043DF5C |. 24 FC AND AL,FC ; *按4對齊
0043DF68 |. 29C8 SUB EAX,ECX ; *在增加原來數據大小的堆棧空間
0043DF6E |. 89C4 MOV ESP,EAX ; *
0043DF7E |. 56 PUSH ESI ; |Arg1 = NOTEPAD.425748, *
0043DF87 |. 89D6 MOV ESI,EDX ; |*
0043DB3A /$ 8D7C01 C0 LEA EDI,[EAX+ECX-40] ; *
0043EC1E . 89C7 MOV EDI,EAX ; *
0043EEED |. F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ ; *移動原來EDISTACK中存儲的數據
0043EEF7 |. 8B7C24 10 MOV EDI,DWORD PTR SS:[ESP+10] ; *
0043EEFF |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10] ; *
這里我們可以看到,每次發(fā)現兩個堆?臻g即將相撞,VM都重新給EBP分配堆棧,并把原來EDISTACK存儲的數據移動到新的空間內。
下面是使用OD跟蹤VM堆棧的幾個小技巧:
在OD中跟蹤VM數據移動時,雙擊0013F8BC地址,OD將會以0013F8BC為基址,顯示上下各個地址與它的偏移量,如圖:
CPU Stack
Locked Value ASCII Comments
$-C 759D0000 ..u
$-8 00000001 ...
$-4 0013F8FC .
$ ==> 009539E8 9. ;這里是0013F8BC,雙擊后的效果
$+4 00950000 ...
$+8 00150000 ...
$+C 00000080 ...
$+10 019314D6
在跟蹤VM時,在數據移動偽指令中的AND AL,0x3C的下一條指令下斷點,這樣每次進行數據移動,你都可以在這個斷點看到,數據的去向和來源,這是極其有用的。在很多復雜的運算地方,你需要在草稿紙上記下,EDISTACK中一些空間的數據時來自于什么時候?比如標志位ZF檢測+跳轉是VM的一個重要操作,而EFLAGS標志數都是相差不多或類似的00000286 00000246等等,如果你不能準確知道[EDI+EAX]存儲或讀取的位置,你將無法理解VM的操作。這非常的重要,請牢記!必要時連OD得數據窗口也一起配合顯示VM堆棧
把OD里的堆棧窗口拉高,讓它竟可能多的顯示數據,在高分辨率的電腦上,最好是能夠顯示出整個VM的堆棧。默認情況下,堆棧窗口是隨著ESP指針的變化而自動顯示的,這對于我們要時刻盯著VM堆棧的需求不相符,在堆棧窗口-->右鍵-->Lock address 打鉤,這樣OD就會鎖定堆棧窗口。
到這里,關于堆?臻g的介紹就結束了。對堆棧的理解是本文的根基。
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的執(zhí)行偽指令,基本等于半自動識別VMP。一名對VMP經驗豐富的人,只要看著VM執(zhí)行的偽指令盯著EBPSTACK堆棧,就可以理解VM的情況,F在,由于多個ESI值指向相同的偽指令,還有動態(tài)EBX解碼,將會艱難的多。
我們在OD中尋找一個空間,寫一段循環(huán)代碼,把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添加循環(huán)代碼:
CPU Disasm
Address Hex dump Command Comments
00405000 60 PUSHAD
00405001 BE DBE14300 MOV ESI,0043E1DB ; DispatchTable地址
00405006 BF 00514000 MOV EDI,00405100 ; 解密循環(huán)地址
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
循環(huán)結束后,在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的匯編代碼下面繼續(xù):
CPU Disasm
Address Hex dump Command Comments
00405000 60 PUSHAD
00405001 BE DBE14300 MOV ESI,0043E1DB ; DispatchTable地址
00405006 BF 00514000 MOV EDI,00405100 ; 解密循環(huán)地址
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中。
執(zhí)行完這些代碼后,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條偽指令,在本節(jié)中我將一一列出這52條偽指令。每個分析VMP的人都有自己對偽指令的命名方式。
移動到EBPSTACK的數據使用PUSH指令,移動到EDISTACK的數據使用MOV指令。在VM中,對數據的操作包括byte和dword,而存儲的方式是word和dword,當遇到byte和word交織在一起的時候,可以就把數據作為word操作來看。
下面我以:VM_PUSHw_MEMORYb為例說明我的命名規(guī)則:
偽指令的命名統(tǒng)一使用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雙精度右移指令,執(zhí)行完畢后,結果和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雙精度左移指令,執(zhí)行完畢后,結果和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中最復雜的偽指令,用于系統(tǒng)API調用和程序過程調用。ESI數據中取得參數的個數,EAX循環(huán)取得參數,壓入ESP指針指向的常規(guī)堆棧。大量使用[ESP+X]的方式調用,摻雜著廢壓棧操作,靜態(tài)看代碼難以看出。返回地址是常量壓入的0043EEB7。這條偽指令涉及內容眾多,分支龐大,系統(tǒng)API調用和程序過程調用的走向都是不同的,在后面章節(jié)詳述。我這里列舉的是一次只有1個參數的系統(tǒng)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跟蹤加殼記事本的過程中,并不是所有的偽指令都使用到了,以下是沒有被執(zhí)行到的偽指令:
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
由于沒有實際的走過這些偽指令,靜態(tài)分析后覺得,有個別偽指令的代碼怕提取錯了。把這些指令寫成簡介模式:
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的偽指令的執(zhí)行中有一些常見的組合套路,熟悉它們能讓我們在跟蹤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所有的運算偽指令中都是統(tǒng)一的模式,運算后的EFLAGS寄存器值位于EBPSTACK棧頂,運算結果位于緊接棧頂的[EBP+4]。在運算結束后,跟上一條VM_MOV_EDISTACK_EBPSTACK把運算后的標志位移動到EDISTACK,在很多時候,這都是一條廢指令操作,純粹是為去掉棧頂數據,以便繼續(xù)操作運算結果。
如果接下來VM進行檢測標志位的相關操作,這條指令就變得異常重要。例如:在對系統(tǒng)函數的CC碼int3斷點檢測中,取出系統(tǒng)函數開頭的第一個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指針,嘩嘩嘩的讓它執(zhí)行,完畢了才停下來。中間的操作完全可以無視,我也是為了完整的表達才把它粘貼出了代碼,實際看的時候,不用管。整個過程:要帶著走的數據移到EBPSTACK-->VM_JMP跳轉-->重新把數據保存到EDISTACK。關于其中000359F4 XOR 4DFD2FC2的過程,請參考下一節(jié)2.2.NAND。
由于其他的組合都和NAND或標志位檢測+跳轉相關,放在下兩節(jié)中。這一節(jié)中的3個組合熟悉后,已經可以無視掉一部分VM的操作。
2.2.NAND(與非門)
本文的兩節(jié)重頭戲來了,NAND(與非門)與EFLAGS標志位檢測+跳轉,理解完了這兩節(jié)后,對于VM就可以無視了,一切偽指令在你眼里都是正常的指令。跟蹤VMP就和跟蹤普通程序一樣,想看API獲取就看API獲取,想看看程序的anti方式就看anti方式。一切都回到了正常,你可以看穿VM(虛擬機)這個嚇人的外衣。
2.2.1.NAND起源
NAND(與非門)和NOR(或非門)來源于de Morgan's Laws(德·摩根定律),運用于邏輯、數字電路等方面,本節(jié)專注于它與and or xor not 之間的聯系。
德·摩根定律是屬于邏輯學的定律。 德·摩根定律(或稱德·摩根定理)是形式邏輯中有關否定所描述的系統(tǒng)方式中的邏輯運算符對偶對的一系列法則。由此引出的關系也就被稱為“德·摩根二重性”。
奧古斯都·德·摩根首先發(fā)現了在命題邏輯中存在著下面這些關系:
非(P 且 Q)=(非 P)或(非 Q)
非(P 或 Q)=(非 P)且(非 Q)
德·摩根的發(fā)現影響了喬治·布爾從事的邏輯問題代數解法的研究,這鞏固了德·摩根作為該規(guī)律的發(fā)現者的地位,盡管亞里士多德也曾注意到類似現象、且這也為古希臘與中世紀的邏輯學家熟知(引自Bocheński《形式邏輯歷史》)。(引自維基百科,關鍵字:德·摩根定律)
我們再來看它在數學邏輯中的表示:
1.jpg下載此附件需要消耗2Kx,下載中會自動扣除。
(引自:MathWorld,關鍵字:de Morgan's Laws)
由于不是用我們熟悉的計算機方式來表達,上面的兩段解說比較抽象,請看2.2.2.
2.2.2.NAND與邏輯運算
在加殼記事本中使用的是NAND,下面部分將專注于NAND。對于NOR,理論都是一樣的,只是不用NAND來實現。
NAND(A,B):
NOT(A)
NOT(B)
ADN(A,B)
這就是NAND的操作方式。NAND的價值在于:使用NAND可以實現NOT AND OR XOR這4個邏輯運算。
NOT(A):
NAND(A,A)
AND(A,B):
NAND(NAND(A,A),NAND(B,B))
OR(A,B):
NAND(NAND(A,B),NAND(A,B))
XOR(A,B):
NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
2.2.3.VMP偽指令執(zhí)行過程
NOT(4DBE4AD8):
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_EBP
0013F9A8 0013F9AC .
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_COPYdw_EBPSTACK
0013F9A8 4DBE4AD8 JM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDdw
0013F9A8 00000286 ..
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(A,A)
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
NOT(4DBE4AD8)=B241B527
AND(4DBE4AD8,4DFD2FC2):
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_EBP
0013F9A8 0013F9AC .
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_COPYdw_EBPSTACK
0013F9A8 4DBE4AD8 JM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDdw
0013F9A8 00000286 ..
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(A,A),NAND(B,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F9A8 B202D03D =
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(A,A),NAND(B,B))**B202D03D=NAND(B,B)**
VM_NANDdw
0013F9A8 00000206 ..
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(A,A),NAND(B,B))VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VMP的B數據是直接傳遞它的相反數B202D03D給VM,相當于隱藏了一次NAND(B,B)的過程。AND(4DBE4AD8,4DFD2FC2)=4DBC0AC0
OR (00000293,00000100):
0013F780 00000293 ..
0013F784 00000100 ...
VM_NANDdw ;NAND(NAND(A,B),NAND(A,B))
0013F784 FFFFFC6C l
VM_PUSHdw_EBP
VM_COPYdw_EBPSTACK ;復制結果,就相當于NAND(NAND(A,B),NAND(A,B))
0013F780 FFFFFC6C l
0013F784 FFFFFC6C l
VM_NANDdw ;NAND(NAND(A,B),NAND(A,B))
0013F784 00000393 ..
OR (00000293,00000100)=00000393
XOR(4DBE4AD8,4DFD2FC2):
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_EBP
0013F9A8 0013F9AC .
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_COPYdw_EBPSTACK
0013F9A8 4DBE4AD8 JM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDdw
0013F9A8 00000286 ..
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F9A8 B202D03D =
0013F9AC B241B527 'A
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))**B202D03D=NAND(B,B)**
VM_NANDdw
0013F9A8 00000206 ..
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_EDISTACKdw
0013F9A8 4DBE4AD8 JM
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEdw
0013F9A4 4DFD2FC2 /M
0013F9A8 4DBE4AD8 JM
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDdw
0013F9A4 00000282 ..
0013F9A8 B2009025 %.
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9A8 B2009025 %.
0013F9AC 4DBC0AC0 .M
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDdw
0013F9A8 00000202 ..
0013F9AC 0043651A eC.
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(NAND(A,A),NAND(B,B)),NAND(A,B))
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 0043651A eC.
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
上面這條XOR指令,就是VM在確定跳轉地址后的解密指令。加密地址是A數據4DBE4AD8,XOR運算后得到ESI跳轉地址0043651A。
在VMP中,減法是采用迂回的方式實現的:
A-B:
NOT(A)
A=A+B
NOT(A)
而NOT運算又要使用NAND來完成
A-B:
NAND(A,A)
A=A+B
NAND(A,A)
2.3.EFLAGS標志位檢測+跳轉
這一節(jié)看完后,就可以暢通無阻的瀏覽VMP的偽指令了。
2.3.1.判斷兩個數是否相同
舉例數據:
把立即數0000和內存00427D51中的1個word數據比較,檢測是否為0。
整個過程分為兩個階段:
第一階段:執(zhí)行減法運算
A-B:
NAND(A,A) ;這里的標志位是無用的
A=A+B ;獲得標志位A
NAND(A,A) ;獲得標志位B
第二階段:合并兩個標志位
A=AND(A,00000815)
B=AND(B,FFFFF7EA)
A=A+B
第三階段:檢測ZF位+跳轉
構建跳轉地址結構
檢測ZF位
獲得加密跳轉地址
解密跳轉地址
調用VM_JMP
在開始這個部分前,把所有VM_MOV_EDISTACK_EBPSTACK偽指令中的AND AL,3C指令的下一條指令處下好斷點,我們要記錄下各個標志位的走向!000000286-->14(表示EFL存儲到偏移量14的[EDI+EAX]位置)
第一階段:
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHw_IMMEDIATEb
0013F9AC 0000
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;立即數IMM0000
VM_PUSHdw_IMMEDIATEdw
0013F9A8 7D51
0013F9AC 00000042 B...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHw_MEMORYb
0013F9AC 00000000
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;內存數MEM0000。很明顯,我們看到兩個數是相同的
VM_PUSHdw_EBP
0013F9A8 0013F9AC .
0013F9AC 00000000 ....
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_COPYw_EBPSTACK
0013F9A8 0000
0013F9AC 00000000 ....
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;復制內存數MEM0000
VM_NANDw
0013F9A8 00000286 ..
0013F9AC 000000FF ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NOT(MEM0000)=MEM00FF
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 000000FF ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;000000286-->14(表示EFL存儲到偏移量14的[EDI+EAX]位置)
VM_ADDb_EBPSTACK
0013F9A8 0286
0013F9AC 00FF0000 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;00FF=IMM0000+MEM00FF
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 00FF
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;標志位A 000000286-->04
VM_PUSHdw_EBP
0013F9A8 F9AE
0013F9AC 00FF0013 ..
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_COPYw_EBPSTACK
0013F9AC 00FF00FF ..
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDw
0013F9A8 0246
0013F9AC 00000000 ....
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NOT(00FF)
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 0000
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;標志位B 00000246-->3C
VM_MOVw_EDISTACKb_EBPSTACKw
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;
第一階段結束。
兩個操作數都是0000,很明顯這次判斷將是兩個數相同,減法后的ZF位置1。
運算的結果都是無用的,關鍵在于它的標志位,繼續(xù)看標志位ZF的檢測+跳轉
第二階段:
VM_PUSHdw_EDISTACKdw
0013F9AC 00000286 ..
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;標志位A 000000286<--04
VM_PUSHdw_EDISTACKdw
0013F9A8 00000286 ..
0013F9AC 00000286 ..
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;再來一次標志位A
VM_NANDdw
0013F9A8 00000282 ..
0013F9AC FFFFFD79 y
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(A,A)=NOT(A)=FFFFFD79
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC FFFFFD79 y
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEw
0013F9A8 FFFFF7EA
0013F9AC FFFFFD79 y
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(X,X)=NOT(00000815)=FFFFF7EA 傳遞相反數,隱藏NOT(00000815)
VM_NANDdw
0013F9A8 00000202 ..
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(A,A),NAND(X,X))=標志位A 00000286 AND 00000815
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_EDISTACKdw
0013F9A8 00000246 F..
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;標志位B 00000246<--3C
VM_PUSHdw_EDISTACKdw
0013F9A4 00000246 F..
0013F9A8 00000246 F..
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;再來一次標志位B
VM_NANDdw
0013F9A4 00000282 ..
0013F9A8 FFFFFDB9
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(B,B)=NOT(B)=FFFFFDB9
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9A8 FFFFFDB9
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEw
0013F9A4 00000815 ..
0013F9A8 FFFFFDB9
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(Y,Y)=NOT(FFFFF7EA)=00000815 傳遞相反數,隱藏NOT(FFFFF7EA)
VM_NANDdw
0013F9A4 00000206 ..
0013F9A8 00000242 B..
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(B,B),NAND(Y,Y))=標志位B 00000246 AND FFFFF7EA
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9A8 00000242 B..
0013F9AC 00000004 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_ADDdw_EBPSTACK
0013F9A8 00000202 ..
0013F9AC 00000246 F..
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;兩個AND后的標志位相加
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 00000246 F..
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;00000246-->00 暫存結果
第二階段結束
現在VMP已經把兩個標志位合并成了一個,關于標志位合并的解析結束后再來看。繼續(xù)看第三階段:檢測ZF+跳轉,注意看好堆棧數據的構造,堆棧虛擬機的跳轉判斷有他獨特的地方!同時它巧妙的運用了ZF位在EFLAGS中的位置。
第三階段:
VM_PUSHdw_IMMEDIATEdw
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳轉地址1
VM_PUSHdw_IMMEDIATEdw
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳轉地址2
VM_PUSHdw_EBP
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳轉地址指針
VM_PUSHw_IMMEDIATEb
0013F9A0 0004
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;傳遞4,看好堆棧的構造,下面的幾個操作是獨立的
VM_PUSHdw_EDISTACKdw
0013F99C 0246
0013F9A0 00040000 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;第二階段結果00000246<--00
VM_PUSHdw_EBP
0013F998 F99E
0013F99C 02460013 .F
0013F9A0 00040000 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_COPYdw_EBPSTACK
0013F998 0246
0013F99C 02460000 ..F
0013F9A0 00040000 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;復制標志位
VM_NANDdw
0013F998 0282
0013F99C FDB90000 ..
0013F9A0 0004FFFF .
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(A,A)=NOT(A)=NOT(00000246)=FFFFFDB9
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F99C FDB9
0013F9A0 0004FFFF .
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEb
0013F998 FFBF
0013F99C FDB9FFFF
0013F9A0 0004FFFF .
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(B,B)=NOT(00000040)=FFFFFFBF 傳遞相反數,隱藏NOT(000000040)
VM_NANDdw
0013F998 0202
0013F99C 00400000 ..@. ; OFFSET NOTEPAD.B
0013F9A0 00040000 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;NAND(NAND(B,B),NAND(B,B))=標志位 00000246 AND 00000040
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F99C 0040
0013F9A0 00040000 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;AND結果是00000040,說明ZF位是1,兩個數相等;想想如果不相等,結果是00000000
VM_SHRdw_EBPSTACKb
0013F99C 00000202 ..
0013F9A0 00000004 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;右移4位剛好把00000040移動成00000004;如果不相等,右移后是00000000
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9A0 00000004 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_ADDdw_EBPSTACK
0013F9A0 00000206 ..
0013F9A4 0013F9AC .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;00000004+0013F9A8=0013F9AC;如果不相等,00000000+0013F9A8=0013F9A8
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9A4 0013F9AC .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳轉地址指針指向的就是判斷后的跳轉地址
VM_COPYdw_EBPSTACK
0013F9A4 4DBE4AD8 JM
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;跳轉地址指針指向的跳轉地址復制出來
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;把最終的跳轉地址暫存到EDISTACK,4DBE4AD8-->18
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;掃尾工作,釋放EBPSTACK
VM_MOVdw_EDISTACKdw_EBPSTACKdw
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;掃尾工作,釋放EBPSTACK
VM_PUSHdw_EDISTACKdw
0013F9AC 4DBE4AD8 JM
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A ;壓入判斷的跳轉地址4DBE4AD8<--18
第三階段結束
接下來VM將使用一次XOR運算解密4DBE4AD8數據(詳見2.2.3.XOR舉例),然后是VM_JMP指令調用的組合(詳見2.1.3.舉例),全過程結束。
兩個操作數都是0000,1個來自內存空間,一個來自ESI的編譯數據,同時這段代碼是在VM剛剛啟動就進行的了,都是定量。但是VM還要進行檢測,說明兩個數據是不確定的,VM在運行過程中要知道它是不是0,可以把它猜測為VMP內部的一個信號。VM一開始就要知道到底應該走向哪個分支。到后面我們會進行測試,如果這個信號比較結果不為0,VM的走向是怎樣的。
下面我們來詳解上面的操作過程,從第二階段合并標志位來看
第一階段:執(zhí)行減法運算
IMM0000-MEM0000:
NAND(IMM0000,IMM0000) ;這里的標志位是無用的
00FF=IMM00FF+MEM0000 ;獲得標志位A 000000286
NAND(00FF,00FF) ;獲得標志位B 000000246
第二階段:合并兩個標志位
00000004=AND(00000286,00000815)
00000242=AND(00000246,FFFFF7EA)
00000246=00000004+00000242
把兩個標志位分別AND后相加,AND操作時用于保留想要的標志位,加法把它合并起來。
關于EFLAGS標志位,Intel的資料顯示:
3.jpg下載此附件需要消耗2Kx,下載中會自動扣除。
各個標志位的詳細說明,請查閱Intel 64 and IA-32 Architectures Software Developer's Mannual(Intel 64位與IA-32體系結構軟件開發(fā)者指南)中卷1的3.4.3 EFLAGS Register
關于ADD指令,Intel的資料顯示:
ADD—Add
Operation
DEST DEST SRC;
Flags Affected
The OF, SF, ZF, AF, CF, and PF flags are set according to the result.
把00000286 AND 00000815使用二進制表示:
0000 0000 0000 0000 0000 0010 1000 0110
AND 0000 0000 0000 0000 0000 1000 0001 0101
我們現在就可以看到,VM要保留的是 OF AF PF CF 位。那么,SF和ZF位為什么不在這里保留呢?我們要想到,由于這里并不是A-B的最后結果,SF 和 ZF位要等到最后的運算完成才能知道。在標志位A中,PF位為1,PF位被保留。
第一個AND數00000815與第二個AND數FFFFF7EA之間是有內在聯系的。00000815+FFFFF7EA=FFFFFFFF,也就是說,這兩個這兩個AND操作時可以把所有的標志位都保留下來的,不會出現遺漏。而把它分開的話,是由于變換了減法的運算方式不進行保留對應的保留。
最后的NAND(A,A):
NOT A ;第一個操作數
NOT A ;第二個操作數
AND A,A ;最終標志位B 00000246是來自這里
關于AND邏輯運算,Intel的資料顯示:
AND—Logical AND
Operation
DEST DEST AND SRC;
Flags Affected
The OF and CF flags are cleared; the SF, ZF, and PF flags are set according to the
result. The state of the AF flag is undefined.
把00000246 AND FFFFF7EA使用二進制表示:
0000 0000 0000 0000 0000 0010 0100 0110
AND 1111 1111 1111 1111 1111 0111 1110 1010
VM要把除了上面00000815保留了的 OF AF PF CF 以外的標志位都保留了下來。在標志位B中,IF ZF PF 和第二位是Intel的保留位默認為1 這4個標志位為1,所以IF ZF PF被保留。
兩個標志位相加后,最終合并成為兩個操作數SUB指令后的標志位00000246
下面我們來看第三階段:
構建跳轉結構:
0013F9A0 0004
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
把兩個跳轉地址4DBE49D5與4DBE4AD8壓入堆棧。0013F9A8成為跳轉地址指針,指向第一個跳轉地址。如果0013F9A8指針+4,它就會指向第二個指針。最后還有1個0004,它并不是用于給指針+4的操作數,它要參與到巧妙判斷ZF位的運算中。
接下來,VM用NAND執(zhí)行一次AND操作,操作數是:標志位00000246與00000040 (在NAND操作中,VM不意外的隱藏了一次NAND(B,B)操作,直接傳遞00000040的相反數FFFFFFBF)
0013F998 FFBF
0013F99C FDB9FFFF
0013F9A0 FFFF .
0013F9A0 0004 ;為了清晰變現,把它分開顯示
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
VM_NANDdw
0013F99C 0040 ;運算結果為00000040
0013F9A0 00040000 ...
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
NAND操作是:
NOT(A)
NOT(B)
AND(A,B)
所以,在這條偽指令的內部,00000246 AND 0000040
把00000246 AND 00000040使用二進制表示:
0000 0000 0000 0000 0000 0010 0100 0110
AND 0000 0000 0000 0000 0000 0000 0100 0000
唯一的檢測ZF位,如果ZF位為1,那么結果將是00000040,否則是00000000。由于ZF位剛好是在byte的4的位置,把它和前面的跳轉地址指針相加,0013F9A8+0則是不變,指向第一個地址,+4就指向第二個地址,所以剛好可以讓AND后的結果與指針0013F9A8進行1次加法運算,如果ZF位是1,0013F9A8+4將指向4DBE4AD8完成判斷跳轉。由于ZF位的前面還有1個byte的數據0,就是00000040中最后的1個byte0,如果直接和0013F9A8相加,就變成+40,所以要先進行1次4個bit的右移,00000040變?yōu)?0000004,這樣才正確。
ZF位為1,AND 00000040后: ZF位為0,AND 00000040后:
00000040 00000000
SHR(4) 00000004 SHR(4) 00000000
ADD 0013F9A8 ADD 0012F9A8
結果 0013F9AC 結果 0013F9A8
0013F9A4 0013F9AC . 0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM 0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM 0013F9AC 4DBE4AD8 JM
ZF位的不同帶來跳轉地址的不同,把相應的跳轉地址解密后,使用VM_JMP給VM的指令指針ESI賦值,全程結束。
進行ZF位比較的話,只需要比較最后的標志位B就可以了,而且可以進行直接的比較,不需要這樣截取+拼接,那么為什么VMP還需要在整個過程中截取了所有的標志位呢?我想,可以這么來考慮,在VMP中標志位的截取+拼接在代碼中是屬于一個模塊,不管VMP要檢測哪個標志位,它都是先調用這個模塊然后再進行標志位檢測。雖然在單純的ZF位檢測中,有了很多的不必要的操作,但是它增加了通用性,只要調用了這個模塊,VMP在后面可以接上任意標志位的檢測。
3.NOTEPAD全程跟蹤
在這一章里,我們將全過程瀏覽NOTEPAD.EXE文件。本章顯示的代碼,外殼的花指令將全部跳過,VM的偽指令則部份跳過。
3.1.TLS
3.1.1.到達Dispatch部份
VMProtect2.04加殼程序是從TLS開始運行的,我們首先點擊OD的options菜單,修改Startup and exit選項,讓OD中斷在TLS callback里。載入NOTOPAD.EXE后,程序停在這里:
004253CD $ 68 9AA597B7 PUSH B797A59A ; TLS callback function
當前的寄存器值:
EAX 004253CD NOTEPAD.004253CD
ECX 00000020
EDX 000359F4
EBX 00000000
ESP 0013F9B0
EBP 0013F9CC
ESI 0013F9C0
EDI 00400000 NOTEPAD.
進入VM之前,VMP要保存當前的各個寄存器值,VM堆棧要分配,同時給偽指令指針寄存器ESI賦值等等,初始化結束后,進入Dispatch部份VM開始運行。F7單步下去:
0043BD02 . C74424 40 0A4 MOV DWORD PTR SS:[ESP+40],2EF6420A ; |*
00429088 |> \C74424 44 19C MOV DWORD PTR SS:[ARG.17],C456C619 ; *
;壓入VM的兩個定量。
0043DCD2 |. 893424 MOV DWORD PTR SS:[ESP],ESI ; *
0043CF0D . 57 PUSH EDI ; *
0043CF17 . 891424 MOV DWORD PTR SS:[ESP],EDX ; *
0043CF1D . 50 PUSH EAX ; *
0043E17A |. 896C24 04 MOV DWORD PTR SS:[ARG.1],EBP ; *
0043D741 |> /871C24 XCHG DWORD PTR SS:[ESP],EBX ; *
0043D746 |. 894C24 20 MOV DWORD PTR SS:[ESP+20],ECX ; *
;7個寄存器保存完畢
0043D750 |. 875424 40 XCHG DWORD PTR SS:[ESP+40],EDX ; |Arg17, *
0043E62E /$ 9C PUSHFD ; *
0043E62F |. 8F4424 40 POP DWORD PTR SS:[ESP+40] ; *
0043E636 |. FF35 89D24300 PUSH DWORD PTR DS:[43D289] ; *
0043E63C |. 8F4424 3C POP DWORD PTR SS:[ESP+3C] ; *
0043E646 |. C74424 38 000 MOV DWORD PTR SS:[ESP+38],0 ; *
;多保存1個寄存器,由于ESP是動態(tài)的,這個位置相當于是ESP寄存器的位置;EFLAGS;內存地址[43D289];常量0;一共保存13個數據
0013F97C 00000000 .... ;常量0 20
0013F980 00000000 .... ;[43D289] 24 8121D2F0相加
0013F984 00000246 F.. ;EFLAGS 0C
0013F988 000359F4 Y. ;EDX(給ESP寄存器的位置) 00
0013F98C 00000020 ... ;ECX 08
0013F990 00000000 .... ;EBX 1C
0013F994 0013F9CC . ;EBP 28
0013F998 004253CD SB. ;EAX 10
0013F99C 000359F4 Y. ;EDX 2C
0013F9A0 00400000 ..@. ;EDI 30
0013F9A4 0013F9C0 . ;ESI 38
0013F9A8 C456C619 V ;常量B 3C
0013F9AC 2EF6420A .B. ;常量A 18
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
0043E65B |. 8B7424 68 MOV ESI,DWORD PTR SS:[ESP+68] ; *
;這里讀取的是常量A=2EF6420A,以下是解密2EF6420A的過程:
0043E665 |. 0FCE BSWAP ESI ; *
0043E66E |. 4E DEC ESI ; *
0043E67D |. 81F6 63A1000A XOR ESI,0A00A163 ; *
;結束,ESI=0042574E,這里就是VM的指令序列
0043E68C |. 8D6C24 34 LEA EBP,[ESP+34]
0043E692 |. 81EC 8C000000 SUB ESP,8C ; *
0043E69C |. 89E7 MOV EDI,ESP ; *
;第一條指令EBP取得的地址是0013F97C的位置,也就是13個保存數據的結束地址,要在這個的基礎上分配VM堆棧
;保存的13個dword=34byte,ESP在執(zhí)行減法前,位于0013F9FC的34個byte位置,減法分配8Cbyte;34+34+8C=F4byte=61dword
;VM堆棧的詳細分析請查閱1.2.VM堆棧
0043E6A9 |. 89F3 MOV EBX,ESI ; *
;EBX是VM解密數據的輔運算寄存器,初始化為VM的指令序列地址0042574E
0043E6B8 |. 0375 00 ADD ESI,DWORD PTR SS:[EBP] ; *
;ESI的地址還要和常量0相加,相加會改變VM指令序列的地址,決定VM第一次運行后要執(zhí)行的指令
;常量0可以看作是VM內部的一個修正量,在NOTEPAD里它是0
到這里,一切初始化完成,程序已經到達Dispatch(調遣)部份。整個過程概括為:保存數據、分配VM堆棧、給ESI賦值。下一條指令就是所有偽指令執(zhí)行完成后的返回地址0043E6BB。
0043E6BB |> >66:0FA5FA SHLD DX,DI,CL ; Dispatch 花指令,沒有什么意義
3.1.2.Dispatch部份解析
下面是第1條偽指令的獲取過程:
0043E6BF |. 8A46 FF MOV AL,BYTE PTR DS:[ESI-1] ; *
;開始讀取偽指令序列號,以下是解密偽指令序列號C0的過程:
0043E6C4 |. 30D8 XOR AL,BL
0043E6CE |. F6D0 NOT AL ; *
0043E6D6 |. FEC8 DEC AL ; *
0043E6DA |. C0C8 07 ROR AL,7 ; *
;結束,AL=E0,它將用于在DispatchTable(調遣表)中定位出偽指令地址。
0043E6E1 |. 83EE 01 SUB ESI,1 ; *
0043E6ED |. 30C3 XOR BL,AL ; *
;指令序列減1,計算好下一次BL的值
0043D02F |. 0FB6C0 MOVZX EAX,AL ; *
0043F124 |. 8B1485 DBE143 MOV EDX,DWORD PTR DS:[EAX*4+43E1DB] ; *
;取出偽指令地址49C4C29F,以下是解密49C4C29F的過程:
;DispatchTable的詳細分析請查閱1.3.偽指令匯總
0043E100 |> /81C2 6B197FB6 ADD EDX,B67F196B ; *
;只有1條解密指令,加上常量B67F196B,EDX=0043DC0A
0043E10A |. 895424 3C MOV DWORD PTR SS:[ESP+3C],EDX ; *
0043E11B |. FF7424 4C PUSH DWORD PTR SS:[ESP+4C] ; *
0043E11F |. C2 5000 RETN 50 ; Enter
;由于是使用RET指令來跳轉,需要使用到真實堆棧指針ESP,在暫存EDX地址時,VM使用的空間是EDISTACK的上一個位置:
0013F8B8 0043DC0A .C. ; RETURN from NOTEPAD.0043D5C7 to NOTEPAD.0043DC0A
0013F8BC 00953F38 8?. ;這里是EDISTACK的上限
到這里,VM將進入執(zhí)行第一條偽指令。整個過程:初始化、從ESI指針獲得偽指令序列號、從DispatchTable獲得偽指令地址、跳轉執(zhí)行偽指令。Dispatch部份是VM中將會不斷重復重復再重復的執(zhí)行,所有的偽指令完畢后,都是返回到這里獲得下一條偽指令。
3.1.3.anti方式初現
通過前面章節(jié)的介紹,在這一節(jié)里,我將對NOTEPAD從TLS回調函數到TLS退出進行一次概論。這個過程將不再出現任何的x86指令代碼,前面介紹的偽指令組合和相關內容將會被縮短和鏈接后跳過。
1.初始化(請查閱3.1.初始化)
NOTEPAD在TLS回調函數中斷后,經過初始化過程后,開始執(zhí)行偽指令。VM將會把所有EBPSTACK中帶過來的13個初始化保留數據暫存至EDISTACK。
2.ESI數據0000與[00427D51]=0000進行比較+跳轉(請查閱2.3.1.判斷相同)
3.VMP將根據PE文件結構讀取出程序入口的第一個字節(jié)進行CC碼檢測。VM會去到另外的堆?臻g操作整個過程,把ESP指針從0013F994-40=0013F954,在開頭構建2個0013F954進行NAND(A,A)中,和前面稍有不同,這里不再詳述。過程:
0013F994-40=0013F954
MOV EBP,0013F954
0013F988 0013F994 .
0013F98C 0013F994 .
0013F990 00000040 @...
0013F994 0013F9C0 . ;計算前的EBP指針
VM_NANDdw
0013F98C FFEC066B k
0013F990 00000040 @...
0013F994 0013F9C0 .
VM_ADDdw_EBPSTACK
0013F990 FFEC06AB
0013F994 0013F9C0 .
VM_PUSHdw_EBP
VM_COPYw_EBPSTACK
0013F98C FFEC06AB
0013F990 FFEC06AB
0013F994 0013F9C0 .
VM_NANDdw
0013F990 0013F954 T.
0013F994 0013F9C0 .
VM_MOVdw_EBPreg_EBPSTACK
EBP 0013F954
堆棧移動到0013F954后,
VM_PUSHdw_IMMEDIATEdw ;壓入00427D51
開始從程序的入口地址根據PE文件格式定位:
VM_PUSHdw_IMMEDIATEdw ;壓入00400000,NOTEPAD程序的文件頭地址,OD數據窗口跟蹤
00400000 4D 5A 90 00|03 00 00 00|04 00 00 00|FF FF 00 00| MZ.........
00400010 B8 00 00 00|00 00 00 00|40 00 00 00|00 00 00 00| .......@.......
00400020 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400030 00 00 00 00|00 00 00 00|00 00 00 00|80 00 00 00| ...............
00400040 0E 1F BA 0E|00 B4 09 CD|21 B8 01 4C|CD 21 54 68| ..!L!Th
00400050 69 73 20 70|72 6F 67 72|61 6D 20 63|61 6E 6E 6F| is program canno
00400060 74 20 62 65|20 72 75 6E|20 69 6E 20|44 4F 53 20| t be run in DOS
00400070 6D 6F 64 65|2E 0D 0D 0A|24 00 00 00|00 00 00 00| mode....$.......
00400080 50 45 00 00|4C 01 09 00|65 91 46 35|00 00 00 00| PE..L..eF5....
00400090 00 00 00 00|E0 00 0F 01|0B 01 03 0A|00 F0 03 00| .....
004000A0 00 74 00 00|00 00 00 00|17 78 03 00|00 10 00 00| .t......x....
004000B0 00 50 00 00|00 00 40 00|00 10 00 00|00 10 00 00| .P....@.......
004000C0 04 00 00 00|00 00 00 00|04 00 00 00|00 00 00 00| ..............
004000D0 00 50 04 00|00 04 00 00|CE 59 03 00|02 00 00 00| .P....Y....
004000E0 00 00 10 00|00 10 00 00|00 00 10 00|00 10 00 00| ............
004000F0 00 00 00 00|10 00 00 00|FC 1D 02 00|50 0C 00 00| ........P...
00400100 18 66 03 00|A0 00 00 00|00 00 04 00|00 50 00 00| f........P..
00400110 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400120 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400130 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400140 F4 59 03 00|20 00 00 00|00 00 00 00|00 00 00 00| Y. ...........
00400150 00 00 00 00|00 00 00 00|B0 7D 03 00|4C 00 00 00| ........}.L...
00400160 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400170 00 00 00 00|00 00 00 00|2E 74 65 78|74 00 00 00| .........text...
00400180 9C 3E 00 00|00 10 00 00|00 00 00 00|00 00 00 00| >.............
00400190 00 00 00 00|00 00 00 00|00 00 00 00|20 00 00 60| ............ ..`
004001A0 2E 64 61 74|61 00 00 00|4C 08 00 00|00 50 00 00| .data...L...P..
004001B0 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
004001C0 00 00 00 00|40 00 00 C0|2E 69 64 61|74 61 00 00| ....@...idata..
004001D0 E8 0D 00 00|00 60 00 00|00 00 00 00|00 00 00 00| ....`..........
004001E0 00 00 00 00|00 00 00 00|00 00 00 00|40 00 00 40| ............@..@
004001F0 2E 76 6D 70|31 00 00 00|B8 4F 00 00|00 70 00 00| .vmp1...O...p..
00400200 00 50 00 00|00 10 00 00|00 00 00 00|00 00 00 00| .P.............
00400210 00 00 00 00|60 00 00 60|2E 76 6D 70|30 00 00 00| ....`..`.vmp0...
00400220 9C 0A 00 00|00 C0 00 00|00 00 00 00|00 00 00 00| ..............
00400230 00 00 00 00|00 00 00 00|00 00 00 00|60 00 00 60| ............`..`
00400240 2E 76 6D 70|32 00 00 00|A0 FD 00 00|00 D0 00 00| .vmp2........
00400250 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400260 00 00 00 00|20 00 00 20|2E 74 6C 73|00 00 00 00| .... .. .tls....
00400270 18 00 00 00|00 D0 01 00|00 10 00 00|00 60 00 00| .........`..
00400280 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 C0| ...............
00400290 2E 76 6D 70|33 00 00 00|85 11 02 00|00 E0 01 00| .vmp3......
004002A0 00 20 02 00|00 70 00 00|00 00 00 00|00 00 00 00| . ..p..........
004002B0 00 00 00 00|20 00 00 E2|2E 72 73 72|63 00 00 00| .... ...rsrc...
004002C0 B0 4F 00 00|00 00 04 00|00 50 00 00|00 90 02 00| O......P....
004002D0 00 00 00 00|00 00 00 00|00 00 00 00|40 00 00 40| ............@..@
004002E0 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
004002F0 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400300 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400310 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400320 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400330 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
00400340 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| ................
..........................................................................
0013F94C 0000003C <...
0013F950 00400000 ..@. ; OFFSET NOTEPAD. ;DOS_header_addr
VM_ADDdw_EBPSTACK
0013F950 0040003C <.@. ;e_magic
VM_PUSHdw_MEMORYdw
0013F950 00000080 ...
取得e_magic的數值,獲取PE文件頭位置
0013F94C 00000080 ...
0013F950 00400000 ..@. ; OFFSET NOTEPAD
VM_ADDdw_EBPSTACK
0013F950 00400080 .@. ; ASCII "PE" ;IMAGE_NT_HEADERS
0013F94C 00400080 .@. ; ASCII "PE"
0013F950 00000028 (...
VM_ADDdw_EBPSTACK
0013F950 004000A8 .@. ;AddressOfEntryPoint
VM_PUSHdw_MEMORYdw
0013F950 00037817 x.
PE文件頭28偏移量的位置是屬于IMAGE_OPTIONAL_HEADER32結構的AddressOfEntryPoint字段,程序執(zhí)行入口RVA00037817
0013F94C 00037817 x.
0013F950 00400000 ..@. ; OFFSET NOTEPAD
VM_ADDdw_EBPSTACK
0013F950 00437817 xC. ; NOTEPAD.
現在已經獲得NOTEPAD的程序執(zhí)行入口地址
VM_PUSHw_IMMEDIATEb
0013F950 00CC
0013F94C 7817
0013F950 00CC0043 C..
VM_PUSHw_MEMORYb
0013F950 00CC0068 h..
從執(zhí)行入口地址讀取字節(jié)和CC進行比較,我沒有在入口地址下INT3斷點,取得的字節(jié)是68,接下來VMP進行減法和標志位ZF檢測,這里不再復述,在VM_JMP組合執(zhí)行完畢后,恢復EBPSTACK重新回到0013F994。
4.IF標志位置1
VM使用NAND進行1次AND操作,AND操作數的一個是700,截取的是DF,IF,TF位,另一個操作數是246,這樣保留的就是Interrupt Enable Flag(IF)位。最后通過偽指令VM_MOVdw_EFLreg_EBPSTACK把結果壓入EFlags寄存器。
0013F9A8 00000246 F..
0013F9AC 00000246 F..
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDdw
0013F9AC FFFFFDB9
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_PUSHdw_IMMEDIATEw
0013F9A8 000008FF ..
0013F9AC FFFFFDB9
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_NANDdw
0013F9AC 00000200 ...
0013F9B0 7C92118A | ; RETURN to ntdll.7C92118A
VM_MOVdw_EFLreg_EBPSTACK
3.1.4.TLS退出
0013F990 8021D2F0 !
0013F994 8021D2F0 !
0013F998 00000246 F..
0013F99C F6F93A39 9:
0013F9A0 00000020 ...
0013F9A4 00000000 ....
0013F9A8 0013F994 .
0013F9AC 004253CD SB. ; RETURN from NOTEPAD.004255DB to NOTEPAD.004253CD
0013F9B0 000359F4 Y.
0013F9B4 00400000 ..@. ; OFFSET NOTEPAD.B
0013F9B8 0013F9C0 .
0013F9BC 7C92118A | ; RETURN to ntdll.7C92118A
VM_EXIT
程序返回進入7C92118A,程序要從TLS中返回,我們去開始地址00437817地址下斷點,在程序執(zhí)行地址攔截下程序。
7C92118A 8BE6 MOV ESP,ESI
7C92118C 5B POP EBX
7C92118D 5F POP EDI
7C92118E 5E POP ESI
7C92118F 5D POP EBP
7C921190 C2 1000 RETN 10
3.2.VMP外殼函數獲取
00437817 . 68 B59DF9FC PUSH FCF99DB5
在00437817把程序攔截下來后,和TLS相似,VMP開始初始化VM等等操作,這里不再復述。VM運行后還是先進行0000與[00427D51]中的0000進行比較+跳轉。接下來把VM堆棧重新分配和TLS里的相似:
0013FF8C 0013FF98
0013FF90 0013FF98
VM_NANDdw
0013FF90 FFEC0067 g.
0013FF94 00000800 ...
VM_ADDdw_EBPSTACK
0013FF94 FFEC0867 g
VM_PUSHdw_EBP
0013FF90 0013FF94 .
0013FF94 FFEC0867 g
VM_COPYdw_EBPSTACK
0013FF90 FFEC0867 g
0013FF94 FFEC0867 g
VM_NANDdw
0013FF94 0013F798 . ; UNICODE "er"
VM_MOVdw_EBPreg_EBPSTACK
偽指令進行了一次減法操作0013FF98-800=0013F798,最終把0013F798賦值給EBPSTACK,在這條指令里進行的邊界檢查中,將觸發(fā)這個VM堆棧的重新分配(詳情請查閱1.2.VM堆棧)
來到新的VM堆棧0013F798中,EF標志位也將再次的置1,一切都和TLS里相同。
3.2.1.動態(tài)鏈接庫
接下來就要進入大規(guī)模的anti檢測了,在進入之前還有一個準備工作,在接下來的使用中,需要使用到一個buffer(緩沖區(qū))用于存儲API函數的返回值,動態(tài)鏈接庫名字等等內容,VM采用的方式是0013FF98-10=0013FF88,這樣就在0013FF88----0013FF98之間分配好了buffer,4個dword:
0013FF88 00000212 ..
0013FF8C 00000282 ..
0013FF90 00000202 ..
0013FF94 0013F798 . ; UNICODE "er"
0013FF98
在后面的anti檢測中,很多都會使用到這個buffer空間。接下來就開始往buffer內寫入常量數據:
0013F790 0013FF88 .
0013F794 6E72656B kern
VM_MOVdw_MEMORYdw_EBPSTACKdw ;寫入kern
VM_PUSHdw_IMMEDIATEdw
0013F78C 0013FF88 .
0013F790 00000004 ...
0013F794 32336C65 el32
VM_ADDdw_EBPSTACK ;buffer存儲地址+4
0013F790 0013FF8C .
0013F794 32336C65 el32
VM_MOVdw_MEMORYdw_EBPSTACKdw ;寫入el32
VM_PUSHdw_IMMEDIATEdw
0013F78C 00000008 ...
0013F790 0013FF88 .
0013F794 6C6C642E .dll
VM_ADDdw_EBPSTACK ;buffer存儲地址+8
0013F790 0013FF90 .
0013F794 6C6C642E .dll
VM_MOVdw_MEMORYdw_EBPSTACKdw ;寫入.dll
VM_PUSHdw_IMMEDIATEdw
0013F78C 0000000C ....
0013F790 0013FF88 .
0013F794 00000000 ....
VM_ADDdw_EBPSTACK ;buffer存儲地址+C
0013F790 0013FF94 .
0013F794 00000000 ....
VM_MOVdw_MEMORYdw_EBPSTACKdw ;寫入00000000
現在我們來看看buffer中整體寫入的數據:
0013FF88 6E72656B kern
0013FF8C 32336C65 el32
0013FF90 6C6C642E .dll
0013FF94 00000000 ....
kernel32.dll鏈接庫,多么熟悉的字眼呀,VM寫入這個數據后,會開始進行一次VM_JMP的相關操作,準備跳轉到新的地方繼續(xù),下面是VM_JMP偽指令的數據:
0013F75C 0042816C lB.
0013F760 00000000 ....
0013F764 7FF224A8 $
0013F768 7C92E514 | ; ntdll.KiFastSystemCallRet
0013F76C 7FFD8000 .
0013F770 00000202 ..
0013F774 00000000 ....
0013F778 0013FFB0 .
0013F77C 00000202 ..
0013F780 0013FF98 .
0013F784 0013FF88 . ; ASCII "kernel32.dll"
0013F788 7FF224A8 $
0013F78C 0013FF88 . ; ASCII "kernel32.dll"
0013F790 00000282 ..
0013F794 004389FB C. ; Entry point of procedure
VM_JMP
最終VM終于來到ANTI檢測和VM后期將要不斷調用的偽指令VM_EBPSTACK_CALL,用于API函數和程序自身過程調用,兩者的操作都是離開虛擬機環(huán)境的。API函數調用將進入系統(tǒng)空間,必然需要離開虛擬機環(huán)境,而這里的過程調用也是離開虛擬機環(huán)境的。離開虛擬機環(huán)境其實很簡單,只需要堆棧上變換一下,本來都是在VM堆棧的數據參數需要移動到ESP指針的堆?臻g內,正常的程序執(zhí)行它是使用ESP指針的,不懂得到VM堆棧內取數據。
0013F78C 00428275 uB. ; RETURN from NOTEPAD.00436E08 to NOTEPAD.00428275
0013F790 0013FF88 . ; ASCII "kernel32.dll"
0013F794 004389FB C. ; Entry point of procedure
VM_EBPSTACK_CALL
關于這條偽指令,我再來詳細解說一下,首先它通過VM的ESI數據獲得這次函數調用的參數個數。這次它的參數是1個,然后把1給ECX,下面是這條偽指令內部獲取參數的指令:
0043E0C6 87448D 00 XCHG DWORD PTR SS:[ECX*4+EBP],EAX ; *
0043E0CD 894424 24 MOV DWORD PTR SS:[ESP+24],EAX ; *
EBP=0013F78C,在循環(huán)里面通過[ECX*4+EBP]的方式,ECX的值就決定了要取出多少個參數,取出的參數壓入ESP指向的堆棧,在這里顯示的是[ESP+24],24的偏移是因為有花指令,不用考慮。一切準備好后,就跳轉到00428275。最后的跳轉指令數據截。
$ ==> 00428275 uB. ; RETURN from NOTEPAD.00436E08 to NOTEPAD.00428275
$+4 9AF17581 u
$+8 14415549 IUA
$+C 0013F6D8 .
$+10 00428137 7B.
$+14 0013F78C .
$+18 0013F6C0 .
$+1C 80A6D7DB 爪
$+20 0043EF77 wC.
$+24 00000000 ....
$+28 00428275 uB. ; RETURN from NOTEPAD.00436E08 to NOTEPAD.00428275
$+2C 0043EED7 C.
$+30 00000246 F..
$+34 0043EED7 C.
$+38 00428275 uB. ; RETURN from NOTEPAD.00436E08 to NOTEPAD.00428275
$+3C 0043EEB7 C. ; RETURN from NOTEPAD.0043D111 to NOTEPAD.0043EEB7 ;執(zhí)行這條指令,去除38垃圾數據后跳轉數據
$+40 0013FF88 . ; ASCII "kernel32.dll"
0043DE10 C2 3800 RETN 38 ; Call Enter
既然是跳轉到00428275,我們來看看00428275處是什么樣的代碼,那里只有一條跳轉指令:
00428275 -/FF25 E47D4300 JMP DWORD PTR DS:[<&KERNEL32.LoadLibrary
現在就很清晰了,這次call是使用LoadLibrary函數來獲取Kernel32.dll鏈接庫句柄。我們在call return address處0043EEB7處下斷,然后就可以F9運行,直接攔截下系統(tǒng)調用結束后返回的結果EAX=7C800000。現在又要回到虛擬機中了,反過來就需要把數據放回到VM堆棧中,按照VM運行方式讓它在VM堆棧中操作。
在返回VM的過程中,首先是把原來EBPSTACK中的參數占據的位置釋放:
0043ED41 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] ; *這里是保存的本次call的參數個數
0043ED46 8D6C8D 00 LEA EBP,[ECX*4+EBP] ; *釋放掉對應的空間
把EAX中的結果保存到EBPSTACK
EAX=7C800000
0043ED50 8945 00 MOV DWORD PTR SS:[EBP],EAX ; *
0013F790 7C800000 ..|
返回VM后,進行一次VM_JMP調用
3.2.2.循環(huán)
分配新的buffer空間,0013FF88-10=0013FF78再分配4個dword的空間出來,使用上面的方式往新的buffer空間寫入數據。4個dword寫入完成后buffer空間的數據如下:
0013FF78 8D7E029C ~
0013FF7C 8F81160C .
0013FF80 048DFF7E ~
0013FF84 00C78D05 .
0013FF88 6E72656B kern
0013FF8C 32336C65 el32
0013FF90 6C6C642E .dll
0013FF94 00000000 ....
調用VM_JMP跳轉到新的地方,接下來毫無疑問的是繼續(xù)進行call調用
0013F788 00421C48 HB. ; Entry point of procedure
0013F78C 7C800000 ..|
0013F790 0013FF78 x.
0013F794 00427C45 E|B.
VM_EBPSTACK_CALL
這次是帶著kernel32.dll的句柄7C800000和0013FF78兩個參數調用00421C48,這次是一個過程調用,我們下面來看00421C48的代碼:
00421C4E 55 PUSH EBP ; *
00421C58 8D6C24 04 LEA EBP,[ESP+4] ; *下面要使用EBP來讀取參數,先保存后定位
00421C76 56 PUSH ESI ; *
00421C80 893C24 MOV DWORD PTR SS:[ESP],EDI ; *
00421C85 53 PUSH EBX ; *
00421C8C 52 PUSH EDX ; *
該保存的保存起來
00421C92 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; *讀取第一個參數kernel32.dll句柄7C800000
接下來的部分和TLS中的PE文件頭定位相似,不過TLS是使用偽指令實現的,這里用常規(guī)指令實現。
00436B42 8B70 3C MOV ESI,DWORD PTR DS:[EAX+3C] ; *
00436B4E 01C6 ADD ESI,EAX ; *
00436B5C 8B56 78 MOV EDX,DWORD PTR DS:[ESI+78] ; * 78偏移是導出表結構位置,kernel32.dll的導出表RVA
0043A773 01C2 ADD EDX,EAX ; * 獲得導出表開始位置7C80262C
0043A77D 8B4E 7C MOV ECX,DWORD PTR DS:[ESI+7C] ; * 78偏移是導出表結構位置,kernel32.dll的導出表大小
00435CB9 01D1 ADD ECX,EDX ; * 獲得導出表結束位置7C809345
00435CBE 894D F0 MOV DWORD PTR SS:[EBP-10],ECX ; * 保存起來
00435CCB 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+0C] ; * 這里獲取的是第二個參數0013FF78
0041EE80 8B7A 24 MOV EDI,DWORD PTR DS:[EDX+24] ; * 導出表24偏移的是AddressOfNamesOrdinals 指向輸入序列號數組
0041EE8B 01C7 ADD EDI,EAX ; * 輸入序列號數組地址7C804424
0041EE8F 8B5A 20 MOV EBX,DWORD PTR DS:[EDX+20] ; * 導出表20偏移的是AddressOfNames 函數名字的指針的地址
0041EE9B 01C3 ADD EBX,EAX ; * 函數名字的指針地址7C80353C
0041EEB1 8B4A 18 MOV ECX,DWORD PTR DS:[EDX+18] ; * 導出表18偏移的是NumberOfNames AddressOfNames數組的項數
一切準備就緒開始讀取導出表函數比較
0041EED5 83E9 01 SUB ECX,1 ; * 計數器第1個word減去
0041EEDF 894D FC MOV DWORD PTR SS:[EBP-4],ECX ; *
0041EEF0 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-8] ; * 整個程序的載入偏移量
00428D66 034D FC ADD ECX,DWORD PTR SS:[EBP-4] ; *
00428D6B D1E9 SHR ECX,1 ; * 計數器除以2,按照word方式
00428D72 8B3C8B MOV EDI,DWORD PTR DS:[ECX*4+EBX] ; *
00428514 /01C7 ADD EDI,EAX ; *
EDI 7C806FB2 ASCII "GetVDMCurrentDirectories",到這里就得到API函數名了
0042851E 8B75 0C MOV ESI,DWORD PTR SS:[EBP+0C] ; * 0013FF78
0043A156 AC LODS BYTE PTR DS:[ESI] ; *
0043A159 F6D0 NOT AL ; *
0043A165 F6D8 NEG AL ; *
00435255 /FEC0 INC AL ; *
0043525C 34 37 XOR AL,37 ; *
0043552A FEC0 INC AL ; *
00435530 D0C0 ROL AL,1 ; *
0041E634 FEC0 INC AL ; *
接下來就是比較
004388E1 3A07 CMP AL,BYTE PTR DS:[EDI] ; *
004388E7 ^\0F8C 35EAFFFF JL 00437322 ; *
004388ED 8D7F 01 LEA EDI,[EDI+1] ; *
004219F2 /0F87 9A6B0100 JA 00438592 ; *
00425FB3 3B4D FC CMP ECX,DWORD PTR SS:[EBP-4] ; *
00425FC4 83C1 01 ADD ECX,1 ; *
00425FCD 894D F8 MOV DWORD PTR SS:[EBP-8],ECX ; *
注意好它的跳轉方式是通過JL和JA方式一起實現,上面的是比較失敗后的循環(huán)。如果相同的話,JL和JA這里都不能跳轉,我們去JA的下一條指令攔截比較相同的情況:
004219F2 /0F87 9A6B0100 JA 00438592 ; *
004219F8 |E8 2B480000 CALL 00426228 ;F4這里
現在我們就找到第一個字母比較相同的函數是VirtualAlloc
0042968E 807F FF 00 CMP BYTE PTR DS:[EDI-1],0 ; *
0043712D ^\0F85 F213FFFF JNE 00428525 ; *
出來到外循環(huán)看是不是已經比較到尾部,還沒有就回去比較第2個字節(jié),我們直接跳出循環(huán)看結果
0043712D ^\0F85 F213FFFF JNE 00428525 ; *
00437133 0FBAEF 16 BTS EDI,16 ;F4這里,所有字節(jié)都相同的函數是VirtualProtect
到這里比較完畢,要找的函數是VirtualProtect
0043C9C1 8B7A 24 MOV EDI,DWORD PTR DS:[EDX+24] ; * AddressOfNamesOrdinals
0043C9C7 01C7 ADD EDI,EAX ; *
0043C9CC 0FB70C4F MOVZX ECX,WORD PTR DS:[ECX*2+EDI] ; *
0043C9D6 29CF SUB EDI,ECX ; *
0043C9DB 8B7A 1C MOV EDI,DWORD PTR DS:[EDX+1C] ; * AddressOfFunctions
0043C9E3 01C7 ADD EDI,EAX ; *
0042862B /8B3C8F MOV EDI,DWORD PTR DS:[ECX*4+EDI] ; *
00438A9F \01F8 ADD EAX,EDI ; *
解說不過來了,大家不明白的去復習PE文件格式的導出表部分吧,上面的指令結束后,得到了VirtualProtect的系統(tǒng)地址7C801AD4
0043EEB7 89D1 MOV ECX,EDX ; call return address
回到call返回地址,數據壓入EBPSTACK這里等于0013F790
0013F790 7C801AD4 | ; kernel32.VirtualProtect
0013F794 00427C45 E|B.
我們在平時的代碼中也經?梢钥吹剑琧all調用完畢后檢測一下是不是0,VMP也是一樣的,只是用偽指令來實現
0013F78C 7C801AD4 | ; kernel32.VirtualProtect
0013F790 7C801AD4 | ; kernel32.VirtualProtect
0013F794 00427C45 E|B.
VM_NANDdw
0013F790 837FE52B +
0013F794 00427C45 E|B.
VM_PUSHdw_EBP
VM_COPYdw_EBPSTACK
0013F78C 837FE52B +
0013F790 837FE52B +
0013F794 00427C45 E|B.
VM_NANDdw
0013F790 7C801AD4 | ; kernel32.VirtualProtect
0013F794 00427C45 E|B.
兩個NOT指令為的就是要它的標志位來進行ZF位檢測+跳轉VM_JMP指令
接下來VM將會進行一次CC碼檢測,看看VirtualProtect函數開始地址有沒有下斷點,此部分不再復述,記錄關鍵數據:
0013F78C 1AD4
0013F790 00CC7C80 |.
0013F794 00427C45 E|B.
讀取出7C801AD4函數的首字節(jié),與CC碼進行一次減法操作,獲取其中的標志位,進行ZF位檢測+跳轉
在前面的EBPSTACK中一直附帶著00427C45這個數據而沒有動靜,現在在確定一切安全沒有問題,VM再次調用VM_JMP控制VM跳轉到00427C45位置,下面我們就來看看00427C45是干什么操作的
0013F790 C9058E9B
0013F794 7C801AD4 | ; kernel32.VirtualProtect
VM_ADDdw_EBPSTACK
0013F794 4585A96F oE
0013F78C 0013F798
0013F790 00000020
0013F794 4585A96F oE
VM_ADDdw_EBPSTACK
0013F790 0013F7B8 .
0013F794 4585A96F oE
VM_MOVb_MEMORYb_EBPSTACKb
現在我們看清楚,00427C45這個VM子程序過程是把得到的系統(tǒng)地址加密起來存放,不讓它按照明碼的方式存儲。到這里完成了一個系統(tǒng)函數獲取到存儲的全過程,接下來程序回到 3.2.2.循環(huán) 的開頭,開始新的函數獲取,從往0013FF78壓入4個dword開始,接著就退出00427C45這個VM子程序過程。當kernel32.dll結束后又進入下一個DLL文件,程序回到 3.2.1.動態(tài)鏈接庫 這一部分不再復述,接下來直接粘貼獲取的函數匯總
kernel32.dll:
0013F780 7C801AD4 | ; kernel32.VirtualProtect ;加密后0013F7B8 4585A96F oE
0013F790 7C809AF1 | ; kernel32.VirtualAlloc ;加密后0013F7C0 7ED1C93F ?~
0013F790 7C801A28 (| ; kernel32.CreateFileA ;加密后0013F7DC 45E78F5A ZE
0013F778 7C809BE7 鐩| ; kernel32.CloseHandle ;加密后0013F7D8 877DBA31 1}
0013F790 7C810B17 | ; kernel32.GetFileSize ;加密后0013F7E4 05F84F8C O
0013F790 7C80950A .| ; kernel32.CreateFileMappingA ;加密后0013F7F4 8B5A496C lIZ
0013F790 7C80B9A5 | ; kernel32.MapViewOfFile ;加密后0013F7C4 C2DC4B94 K
0013F790 7C80BA14 | ; kernel32.UnmapViewOfFile ;加密后0013F798 230A53C4 S.#
0013F790 7C80B741 A| ; kernel32.GetModuleHandleA ;加密后0013F7CC 058C4D40 @M
0013F794 7C813133 31| ; kernel32.IsDebuggerPresent ;加密后0013F7EC 9C056A3F ?j
0013F794 7C85AAF2 | ; kernel32.CheckRemoteDebuggerPresent ;加密后0013F7F8 77ED7C33 3|w
0013F790 7C863FCA ?| ; kernel32.UnhandledExceptionFilter ;加密后0013F7D0 35B5E8D3 5
ntdll.dll:
0013F794 7C92D7FE 讙| ; ntdll.ZwQueryInformationProcess ;加密后0013F7B0 D324C5FE $
0013F794 7C92DCAE 軖| ; ntdll.NtSetInformationThread ;加密后0013F7A8 E42D06B3 -
0013F794 7C92D92E .賿| ; ntdll.NtQuerySystemInformation ;明碼存儲00425E60 7C92D92E到這里所有的外殼函數獲取結束,VM執(zhí)行VM_JMP跳轉走,由于最后的1條系統(tǒng)函數地址是存儲在內存中,00425E60進行加法操作,不讓他以明碼出現,同時VM作為堆棧虛擬機,還是喜歡堆棧的存儲方式,所以再找一個堆?臻g0013F7E8,把暗碼地址放進去:
0013F790 03DDEA1E
0013F794 00425E64 d^B.
VM_ADDdw_EBPSTACK
0013F794 04204882 H
0013F78C 00000050 P...
0013F790 0013F798 .
0013F794 04204882 H
VM_ADDdw_EBPSTACK
0013F790 0013F7E8 .
0013F794 04204882 H
VM_MOVdw_MEMORYdw_EBPSTACKdw
3.3.虛擬執(zhí)行環(huán)境與調試器檢測
在前面所有的節(jié)里面的內容全部都是貫穿的,沒有一個地方遺漏下來的在解析,但是這一節(jié)和上一節(jié)的結尾并沒有連接在一起,不是我想藏著捏著搞流水賬脫文出來,實在是沒有精力一條一條的去說。我已經想吐了,沒有心情繼續(xù)寫下去了,越是想著全盤托出,有些地方老是更有壓力害怕遺漏了東西。還有一個是想趕快結稿的心理也有一些。再說,VMP的ANTI檢測是很有趣的一個部分,當你知道下面有一節(jié)很有趣,而一直繞在上面的基礎地方,實在也有點心急?傊绻惆l(fā)現我有遺漏了沒有解說的地方就自己去看好了。下面我們就直接來到ANTI部分:
3.3.1.VMware
0013F78C 00000000 ....
0013F790 0043B7B2 C. ; RETURN from NOTEPAD.00435E6A to NOTEPAD.0043B7B2
0013F794 0013FF98 .
VM_FS:[EBPSTACK] ;讀取FS:[0]的數據
0013F78C |0013FFE0 .
0013F790 \0043B7B2 C. ; RETURN from NOTEPAD.00435E6A to NOTEPAD.0043B7B2
0013F794 0013FF98 .
0013F784 00000000 ....
0013F788 0013F78C .
0013F78C 0013FFE0 .
0013F790 0043B7B2 C. ; RETURN from NOTEPAD.00435E6A to NOTEPAD.0043B7B2
0013F794 0013FF98 .
VM_SEH
0013F78C |0013FFE0 . ; Pointer to next SEH record
0013F790 \0043B7B2 C. ; SE handler
0013F794 0013FF98 .
這是VM構建新的SEH。讀取FS:[0]的原來的SEH地址,然后放入新的進去VM_SEH偽指令里實現的,不明白的自己去看SEH相關資料。構建好SEH后程序調用VM_EXIT,
0013F770 00000000 ....
0013F774 0013FF98 .
0013F778 564D5868 hXMV
0013F77C 00005658 XV..
0013F780 [0042536C lSB. ; RETURN from NOTEPAD.00426E8B to NOTEPAD.0042536C
0013F784 31921C56 V1
0013F788 0042536C lSB. ; RETURN from NOTEPAD.00426E8B to NOTEPAD.0042536C
0013F78C 0013FFE0 . ; Pointer to next SEH record
0013F790 0043B7B2 C. ; SE handler
0013F794 0013FF98 .
VM_EXIT
接下來程序在
0042536C . ED IN EAX,DX ; I/O command
這條指令這里就卡死了。為什么呢?因為VM_EXIT中有著給各個寄存器賦值的操作,其中這里他就構建了一個VMware的后門指令檢測。在這里算是VMP的第一個正規(guī)的ANTI來了。執(zhí)行這條指令時候的CPU狀態(tài)和VMware后門檢測的源碼:
CPU - main thread, module NOTEPAD
EAX 564D5868
ECX 0000000A
EDX 00005658
EBX 00000000
ESP 0013F78C
EBP 0013FF98
ESI 0013FF8C ASCII "ntdll.dll"
EDI 0013FF70
EIP 0042536C NOTEPAD.0042536C
VMware的后門指令檢測。這個么不用問為什么。這個就是后門
mov eax, 564D5868h
mov ebx, 00000000h
mov ecx, 0000000Ah
mov edx, 00005658h
in eax, dx
這個檢測很普通,由于只有在VMware下這條指令才有返回值,否則就是一次異常。而我們是在真實環(huán)境下的。這個太普通了。我根本沒有用什么虛擬機。想深入了解的自己google資料看。
我是沒有在VMware調試VMP,所以它必定異常出錯,現在就可以看到剛才構建的SEH的作用了。去SEH地址下斷,然后忽略異常繼續(xù)調試。
0043B7B2 .^\E9 7DD2FFFF JMP 00438A34 ; VMware SEH
我們來到了這里繼續(xù)調試,在SEH中,將保存當前的結構,重新初始化一個VM,并在這個VM里面修改掉context結構中EIP指針。要看懂修改的偽指令,還是復習一下SEH回調函數:
首先看好SEH回調函數的參數,它一共有4個參數:
SEH_Handler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext
對應VMP里的SEH地址看看具體數據
0013F3C4 [7C9232A8 2| ; RETURN to ntdll.7C9232A8
0013F3C8 0013F4AC . ;第一個參數:ExceptionRecord指針
0013F3CC 0013F78C . ;第二個參數:SEH指針
0013F3D0 0013F4C0 . ;第三個參數:Context指針
0013F3D4 0013F480 . ;第四個參數:DispatcherContext指針
附CONTEXT結構環(huán)境:
代碼:typedefstruct_CONTEXT{
/*000*/DWORD ContextFlags;
/*004*/DWORD Dr0;
/*008*/DWORD Dr1;
/*00C*/DWORD Dr2;
/*010*/DWORD Dr3;
/*014*/DWORD Dr6;
/*018*/DWORD Dr7;
/*01C*/FLOATING_SAVE_AREAFloatSave;
/*08C*/DWORD SegGs;
/*090*/DWORD SegFs;
/*094*/DWORD SegEs;
/*098*/DWORD SegDs;
/*09C*/DWORD Edi;
/*0A0*/DWORD Esi;
/*0A4*/DWORD Ebx;
/*0A8*/DWORD Edx;
/*0AC*/DWORD Ecx;
/*0B0*/DWORD Eax;
/*0B4*/DWORD Ebp;
/*0B8*/DWORD Eip; ;B8的偏移量位置是Eip
/*0BC*/DWORD SegCs;
/*0C0*/DWORD EFlags;
/*0C4*/DWORD Esp;
/*0C8*/DWORD SegSs;
/*0CC*/ BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
/*2CC*/}CONTEXT;
先用理論來說一下:
1)第三個參數得到context結構地址0013F4C0。
2)context結構基地址+B8得到Eip存儲地址。
3)把安全的返回地址放入Eip位置。
4)SEH異常返回,程序從新的Eip地址開始執(zhí)行。
下面來看偽指令的操作過程:
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
VM_PUSHdw_IMMEDIATEb
0013F3C0 0000000C ....
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
VM_PUSHdw_IMMEDIATEb
0013F3BC 00000008 ...
0013F3C0 0000000C ....
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
VM_PUSHdw_EBP
0013F3B8 0013F3BC .
0013F3BC 00000008 ...
0013F3C0 0000000C ....
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
VM_ADDdw_EBPSTACK
0013F3BC 0013F3C4 .
0013F3C0 0000000C ....
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
VM_ADDdw_EBPSTACK
0013F3C0 0013F3D0 .
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
這是1)階段,加8獲得參數的位置,加C獲得第三個參數:
0013F3C0 0013F3D0 .
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
0013F3C8 0013F4AC .
0013F3CC 0013F78C .
0013F3D0 0013F4C0 .
0013F3D4 0013F480 .
VM_COPYdw_EBPSTACK
0013F3C0 0013F4C0 .
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
這是1)階段,獲得context結構地址0013F4C0
0013F3B8 0013F4C0 .
0013F3BC 000000B8 ...
0013F3C0 00436C7D }lC.
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
VM_ADDdw_EBPSTACK
0013F3BC 0013F578 x. ; ASCII "lSB"
0013F3C0 00436C7D }lC.
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
這是2)階段,context結構B8偏移量位置是Eip位置
VM_MOVdw_MEMORYdw_EBPSTACKdw
這是3)階段,本來Eip位置是發(fā)生異常的指令地址:
0013F578 0042536C lSB. ; RETURN from NOTEPAD.00426E8B to NOTEPAD.0042536C
經過修改后,放入了返回后的執(zhí)行地址:
0013F578 00436C7D }lC.
0013F398 F32B430A .C+
0013F39C 00000000 ....
0013F3A0 00000246 F..
0013F3A4 7C9232BC 2|
0013F3A8 0013F4C0 .
0013F3AC 00000000 ....
0013F3B0 0013F3E4 .
0013F3B4 00000000 ....
0013F3B8 7C9232BC 2|
0013F3BC 00000000 ....
0013F3C0 00000000 ....
0013F3C4 7C9232A8 2| ; RETURN to ntdll.7C9232A8
VM_EXIT
這是4)階段。最后調用VM_EXIT返回,里面給寄存器賦值,讓系統(tǒng)從SEH返回,而返回后eip指針被修改,我們就需要從新放入的地址00436C7D攔截程序跟蹤。接下來重新初始化VM。我們繼續(xù)走起了。現在前面放置的SEH已經無用了,釋放掉恢復原來的SEH,直接放偽指令的實現過程了,不明白的查SEH資料:
0013F788 00000000 ....
VM_FS:[EBPSTACK] ;FS:[O]的值取出來,得到當前的SEH結構存儲地址
0013F788 0013F78C .
VM_MOVdw_EBPreg_EBPSTACK ;移動EBP指針下去,到達SEH結構存執(zhí)地址
0013F788 00000000 ....
0013F78C 0013FFE0 . ; Pointer to next SEH record
0013F790 0043B7B2 C. ; SE handler
VM_SEH ;恢復原來的SEH,0013FFE0放入FS:[0]
3.3.2.單步模式
這個檢測方法用偽指令來實現可以說是非常非常的猥瑣。這招對于一般的人來說毫無意義,因為都是直接運行或者從來不進偽指令里面去,很容易就過了,反倒是碰到我這樣F7單步走VM的人來說,不小心就中招了。總之十分猥瑣的方法。
0013F78C 00000000 ....
0013F790 0041F070 pA. ; RETURN from NOTEPAD.00423165 to NOTEPAD.0041F070
0013F794 0013FF98 .
VM_FS:[EBPSTACK] ;讀取FS:[0]的值當前SEH結構
0013F78C |0013FFE0 .
0013F790 \0041F070 pA. ; RETURN from NOTEPAD.00423165 to NOTEPAD.0041F070
0013F794 0013FF98 .
0013F784 00000000 ....
0013F788 0013F78C .
0013F78C 0013FFE0 .
0013F790 0041F070 pA. ; RETURN from NOTEPAD.00423165 to NOTEPAD.0041F070
0013F794 0013FF98 .
VM_SEH
0013F78C |0013FFE0 . ; Pointer to next SEH record
0013F790 \0041F070 pA. ; SE handler
0013F794 0013FF98 .
構建新的SEH結構,現在的異常處理程序地址是0041F070
下面VM會進行一次OR操作,標志位00000293 OR 00000100=00000393并把結果壓入EFLAGS寄存器,偽指令過程如下:
0013F77C 0013F780 .
0013F780 00000008 ...
0013F784 00000100 ...
0013F788 00000293 ..
0013F78C 0013FFE0 . ; Pointer to next SEH record
0013F790 0041F070 pA. ; SE handler
0013F794 0013FF98 .
VM_ADDdw_EBPSTACK
0013F780 0013F788 .
0013F784 00000100 ...
0013F788 00000293 ..
VM_COPYdw_EBPSTACK
0013F780 00000293 ..
0013F784 00000100 ...
0013F788 00000293 ..
VM_NANDdw
0013F784 FFFFFC6C l
0013F788 00000293 ..
VM_PUSHdw_EBP
VM_COPYdw_EBPSTACK
0013F780 FFFFFC6C l
0013F784 FFFFFC6C l
0013F788 00000293 ..
VM_NANDdw
0013F784 00000393 ..
0013F788 00000293 ..
VM_PUSHdw_EBP
0013F780 0013F784 .
0013F784 00000393 ..
0013F788 00000293 ..
VM_PUSHdw_IMMEDIATEb
0013F77C 00000004 ...
0013F780 0013F784 .
0013F784 00000393 ..
0013F788 00000293 ..
VM_ADDdw_EBPSTACK
0013F780 0013F788 .
0013F784 00000393 ..
0013F788 00000293 ..
VM_MOVdw_MEMORYdw_EBPSTACKdw
0013F788 00000393 ..
0013F758 00000286 ..
0013F75C 0013FF8C . ; ASCII "ntdll.dll"
0013F760 00000206 ..
0013F764 00426C00 .lB.
0013F768 0000000A ....
0013F76C 00000000 ....
0013F770 0013FF98 .
0013F774 00000000 ....
0013F778 00005658 XV..
0013F77C 0013FF70 p.
0013F780 0013FF8C . ; ASCII "ntdll.dll"
0013F784 00428173 sB.
0013F788 00000393 ..
VM_EXIT
在VM_EXIT中,最后一個數據00000393是
00428173 9D POPFD ; *
被壓入了EFLAGS寄存器,現在我們可以看看00000100這個OR操作數影響的是Trap Flag(TF)位,這個過程就是把標志位的TF位置1。根據Intel資料:
TF (bit 8) Trap flag — Set to enable single-step mode for debugging;
clear to disable single-step mode.
也就是VM設置單步模式(single-step mode)。下面我們來回頭進好好看看00428173這個過程的詳細代碼:
00428173 |. 9D POPFD ; *
00428174 |. 0F31 RDTSC
00428176 |. 90 NOP
00428177 |. 9C PUSHFD
00428178 |. C70424 0A429C MOV DWORD PTR SS:[ESP],489C420A
0042817F |. 9C PUSHFD
00428180 \. E9 58030100 JMP 004384DD
這段代碼你要是一條一條的F7走下去,完全沒有問題。能夠一直走到JMP 004384DD這里,然后程序就開始初始化VM,看不到任何的問題。而事實上你已經中招了。接下來就等著看被VMP發(fā)現的提示框吧。
而如果你直接在進入這個00428173的過程前來一個F9,比如說VM_EXIT偽指令處。你就會發(fā)現程序被攔截下來了
00428173 |. 9D POPFD ; * single-step mode
00428174 |. 0F31 RDTSC
00428176 |. 90 NOP ;*************************
00428177 |. 9C PUSHFD
00428178 |. C70424 0A429C MOV DWORD PTR SS:[ESP],489C420A
0042817F |. 9C PUSHFD
00428180 \. E9 58030100 JMP 004384DD
在NOP指令這里,程序就被攔截下來了?匆幌翺D下角的提示框顯示:
Break on single-step trap set by application - Shift+Run/Step to pass exception to the program
來說說原理,由于OD這樣的Ring3調試器,F7單步靠的就是TF標志位,所以如果你單步走這段代碼。VM程序設置的TF位就會和OD調試器的TF位相同,OD以為是自己的單步調試,就不會觸發(fā)這個異常,而一旦你F9運行程序,OD才會發(fā)現:哦,原來這里調試的程序自己設置一個TF單步異常!注意看OD給的提示消息:中斷在應用程序設置的單步陷阱-Shift+Run/Step跳過程序異常