2.2.NAND(與非門)
本文的兩節(jié)重頭戲來了,NAND(與非門)與EFLAGS標志位檢測+跳轉,理解完了這兩節(jié)后,對于VM就可以無視了,一切偽指令在你眼里都是正常的指令。跟蹤VMP就和跟蹤普通程序一樣,想看API獲取就看API獲取,想看看程序的anti方式就看anti方式。一切都回到了正常,你可以看穿VM(虛擬機)這個嚇人的外衣。
2.2.1.NAND起源
NAND(與非門)和NOR(或非門)來源于de Morgan's Laws(德·摩根定律),運用于邏輯、數(shù)字電路等方面,本節(jié)專注于它與and or xor not 之間的聯(lián)系。
德·摩根定律是屬于邏輯學的定律。 德·摩根定律(或稱德·摩根定理)是形式邏輯中有關否定所描述的系統(tǒng)方式中的邏輯運算符對偶對的一系列法則。由此引出的關系也就被稱為“德·摩根二重性”。
奧古斯都·德·摩根首先發(fā)現(xiàn)了在命題邏輯中存在著下面這些關系:
非(P 且 Q)=(非 P)或(非 Q)
非(P 或 Q)=(非 P)且(非 Q)
德·摩根的發(fā)現(xiàn)影響了喬治·布爾從事的邏輯問題代數(shù)解法的研究,這鞏固了德·摩根作為該規(guī)律的發(fā)現(xiàn)者的地位,盡管亞里士多德也曾注意到類似現(xiàn)象、且這也為古希臘與中世紀的邏輯學家熟知(引自Bocheński《形式邏輯歷史》)。(引自維基百科,關鍵字:德·摩根定律)
我們再來看它在數(shù)學邏輯中的表示:
1.jpg下載此附件需要消耗2Kx,下載中會自動扣除。
(引自:MathWorld,關鍵字:de Morgan's Laws)
由于不是用我們熟悉的計算機方式來表達,上面的兩段解說比較抽象,請看2.2.2.
2.2.2.NAND與邏輯運算
在加殼記事本中使用的是NAND,下面部分將專注于NAND。對于NOR,理論都是一樣的,只是不用NAND來實現(xiàn)。
NAND(A,B):
NOT(A)
NOT(B)
ADN(A,B)
這就是NAND的操作方式。NAND的價值在于:使用NAND可以實現(xiàn)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數(shù)據(jù)是直接傳遞它的相反數(shù)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數(shù)據(jù)4DBE4AD8,XOR運算后得到ESI跳轉地址0043651A。
在VMP中,減法是采用迂回的方式實現(xiàn)的:
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.判斷兩個數(shù)是否相同
舉例數(shù)據(jù):
把立即數(shù)0000和內存00427D51中的1個word數(shù)據(jù)比較,檢測是否為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 ;立即數(shù)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 ;內存數(shù)MEM0000。很明顯,我們看到兩個數(shù)是相同的
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 ;復制內存數(shù)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 ;
第一階段結束。
兩個操作數(shù)都是0000,很明顯這次判斷將是兩個數(shù)相同,減法后的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 傳遞相反數(shù),隱藏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 傳遞相反數(shù),隱藏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 暫存結果
第二階段結束
現(xiàn)在VMP已經(jīng)把兩個標志位合并成了一個,關于標志位合并的解析結束后再來看。繼續(xù)看第三階段:檢測ZF+跳轉,注意看好堆棧數(shù)據(jù)的構造,堆棧虛擬機的跳轉判斷有他獨特的地方!同時它巧妙的運用了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 傳遞相反數(shù),隱藏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,兩個數(shù)相等;想想如果不相等,結果是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數(shù)據(jù)(詳見2.2.3.XOR舉例),然后是VM_JMP指令調用的組合(詳見2.1.3.舉例),全過程結束。
兩個操作數(shù)都是0000,1個來自內存空間,一個來自ESI的編譯數(shù)據(jù),同時這段代碼是在VM剛剛啟動就進行的了,都是定量。但是VM還要進行檢測,說明兩個數(shù)據(jù)是不確定的,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
我們現(xiàn)在就可以看到,VM要保留的是 OF AF PF CF 位。那么,SF和ZF位為什么不在這里保留呢?我們要想到,由于這里并不是A-B的最后結果,SF 和 ZF位要等到最后的運算完成才能知道。在標志位A中,PF位為1,PF位被保留。
第一個AND數(shù)00000815與第二個AND數(shù)FFFFF7EA之間是有內在聯(lián)系的。00000815+FFFFF7EA=FFFFFFFF,也就是說,這兩個這兩個AND操作時可以把所有的標志位都保留下來的,不會出現(xiàn)遺漏。而把它分開的話,是由于變換了減法的運算方式不進行保留對應的保留。
最后的NAND(A,A):
NOT A ;第一個操作數(shù)
NOT A ;第二個操作數(shù)
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被保留。
兩個標志位相加后,最終合并成為兩個操作數(shù)SUB指令后的標志位00000246
下面我們來看第三階段:
構建跳轉結構:
0013F9A0 0004
0013F9A4 0013F9A8 .
0013F9A8 4DBE49D5 IM
0013F9AC 4DBE4AD8 JM
把兩個跳轉地址4DBE49D5與4DBE4AD8壓入堆棧。0013F9A8成為跳轉地址指針,指向第一個跳轉地址。如果0013F9A8指針+4,它就會指向第二個指針。最后還有1個0004,它并不是用于給指針+4的操作數(shù),它要參與到巧妙判斷ZF位的運算中。
接下來,VM用NAND執(zhí)行一次AND操作,操作數(shù)是:標志位00000246與00000040 (在NAND操作中,VM不意外的隱藏了一次NAND(B,B)操作,直接傳遞00000040的相反數(shù)FFFFFFBF)
0013F998 FFBF
0013F99C FDB9FFFF
0013F9A0 FFFF .
0013F9A0 0004 ;為了清晰變現(xiàn),把它分開顯示
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的數(shù)據(jù)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在后面可以接上任意標志位的檢測。