也玩一把VMP爆破


看了PEDIY天易LOVE的《深入淺出VMP爆破》,https://bbs.pediy.com/thread-127020.htm,受益匪淺,之前一直搞不懂萬用閘的概念。不過天易LOVE的演示程序是VMP1.2,我用VMP2.08對文章中的示例加殼以進行一次實戰。如果你對VMP沒有概念,建議先看看我在看雪的一篇文章,https://bbs.pediy.com/thread-224204.htm。


我簡單觀察了一下VMP2.X版本虛擬流程(無變異)后的代碼,目測代碼混淆只使用了數據流的插入死碼,以及控制流的間接轉移,算是吃了一顆定心丸。


原版未加密的程序很簡單。

00453F18  |.  E8 87FFFFFF   call VMP原版.00453EA4
00453F1D  |.  84C0          test al,al
00453F1F  |.  74 0C         je short VMP原版.00453F2D
00453F21  |.  B8 743F4500   mov eax,VMP原版.00453F74                   ;  Good job!
00453F26  |.  E8 458AFDFF   call VMP原版.0042C970
00453F2B  |.  EB 0A         jmp short VMP原版.00453F37
00453F2D  |>  B8 883F4500   mov eax,VMP原版.00453F88                   ;  Try again!
00453F32  |.  E8 398AFDFF   call VMP原版.0042C970
00453F37  |>  33C0          xor eax,eax
00453EA4  /$  83F8 7B       cmp eax,0x7B
00453EA7  |.  74 06         je short VMP原版.00453EAF
00453EA9  |.  B8 00000000   mov eax,0x0
00453EAE  |.  C3            retn
00453EAF  |>  B8 01000000   mov eax,0x1
00453EB4  \.  C3            retn

VMP是如何實現減法指令的?
可以看看PEDIY海風月影前輩的帖子https://bbs.pediy.com/thread-82618.htm,我簡單說下我的理解。
水平有限,如有錯誤,還望指正。
VMP的邏輯運算指令只有一條,記為VM_NOR。
VM_NOR的一般結構如下:

 

POP EDX
NOT EDX //NOTa
NOT DWORD PTR SS:[ESP] //NOTa
AND DWORD PTR SS:[ESP],EDX // NOT(a) and NOT(a)= not(a)
PUSHFW

 

NOT指令不影響標志位,可是AND指令影響標志位,我們一般爆破找JZ/JNZ指令,這些指令測試的是ZF標志位。
怎么測試EFLAGS里的ZF標志位?VM_ NOR (NOT(EFLAGS),NOT(0x40)),其結果就是把EFLAGS里面除ZF位以外的標志位全部清0了。ZF標志位在第6位,然后把結果再右移(shr)6位,就可以把ZF位移動到最低位,結果就變成了0和1,然后,再使用VMP里面的唯一一條跳轉指令,VM_JUMP根據結果計算出轉移地址。ZF為1時,EFLAGS的值是0x40。如果不能理解,找一張EFLAGS的布局圖,把0x40轉換為二進制,低位對低位的填上去看看即可。這里稍微注意一下,天易LOVE前輩說取出的ZF標志位是反的,可是我個人覺得取出的就是一樣的,差別就在於傳VM_NOR參數時EFLAGS是否取反。(如有錯誤,望指正)
所以,JZ跳轉成立的條件可以這樣表達:
VM_NOR(NOT(EFLAGS), NOT(0x40)) == 0x40
SHR(VM_NOR(EFLAGS, NOT(0x40)), 6) == 0x1
至於上文提到的VM_JUMP,一般形式為。

 

mov e?x,[ebp]
add ebp,4
mov esi, e?x

 

可是EFLAGS是怎么來的?這個就涉及到VMP模擬減法的知識了。我看了海風月影前輩的帖子,可惜百度空間現在已經被百度下架了,找不到關於減法的內容,僥幸在天易LOVE前輩的帖子里面撿到寶了。
NOT(a) = -a - 1
a - b = NOT(NOT(a) + b)
推到過程如下:
a -  b = NOT(a - b) + 1 = NOT(a - b - 1) = NOT(NOT(a) + 1 - 1 + b) = NOT(NOT(a) + b)
VM_NOR是怎么模擬NOT的呢?
NOT(a) = VM_NOR(a,a)
那么,剛剛推導出的NOT模擬減法公式可以用VM_NOR表達。
a - b = VM_NOR(VM_NOR(a,a) + b, VM_NOR(a,a) + b)
至於EFLAGS是由EFLAGS1和EFLAGS2運算產生的。
在運算VM_NOR(a,a) + b時,得到EFLAG1。運算完VM_NOR(VM_NOR(a,a) + b, VM_NOR(a,a) + b)的最外層的那條VM_NOR時,得到EFLAG2。
EFLAGS = VM_ NOR(NOT(EFLAG1),NOT(0x815)) + VM_NOR(NOT(EFLAG2),0x815)
0x815,轉換成二進制,對應的是AF、OF、CF、PF標志位為1,也就是說,把SF、ZF標志位清0了。大家考慮一下,運算a - 1 + b時得到的EFLAG1,與最終結果的EFLAGS有哪些差別呢?首先ZF肯定不同,SF因情況而有異數。
接着計算EFLAG2,此時得出了ZF、SF位。最后得出最終結果EFLAGS。
=========================原理分析完,開始實戰搞一波==============================
首先,關鍵點在於我們能不能找到VM_NOR這條Handler,找到了的話基本可以說是萬事OK了。
這里的思路參考了半斤八兩前輩的一篇文章,http://www.cnblogs.com/BjblCracked/p/4152193.html。(.......原來我的傻逼方法是找到VM Dispatcher,記錄下每一個調用的Handler,記錄后有上千個,去重復后有幾十個,一個一個找…………)
首先,搜索字符串引用,在0x00453EA4這個地方下一個斷點(這樣做是為了少記錄一點無用代碼)。
點擊菜單,查看——RUN跟蹤。打開RUN TRACE窗口,右鍵,記錄到文件,選擇你的記錄保存在哪里。然后一直跟蹤到VM Dispatch,也就是,0045E670    8B1485 A3DE4500 mov edx,dword ptr ds:[eax*4+0x45DEA3](強迫症)
然后點擊,調試——跟蹤步入,或者按CTRL+F11。等有提示框彈出的時候,打開你保存的記錄文件,進行搜索工作。(有點卡哦,我測試應該有1s左右的延遲)
打開記錄文件,搜索EAX=00000040(應該也有可能是EDX=00000040)

 

若干次查找后,來到此處。

這里找到了VM_NOR閘。
接着我們試試看直接查找0045E88D 主       not eax                                   ; EAX=00000040
若干次后,來到此處。

 

結果是0,EDX是EFLAGS,EAX是0x40。在OllyDbg里面下一個條件斷點試試看。
在0045E8A0 >  9C              pushfd
下一個條件斷點,EAX==0。
若干次中斷后,發現有EDX==286的情況。
把EAX修改為0x40。
把斷點刪除,F9運行。

 

剛剛ESI的值是49375B,可以改進一下這個硬件斷點。
條件改為ESI == 0x49375B
中斷時把EAX修改為0x40即可。

要打補丁很簡單,用VEH機制即可。可能需要注意的地方就是KiUserExceptionDispatcher有沒有被HOOK拿來反調試。

試煉程序下載:

鏈接: https://pan.baidu.com/s/1dG019Bj 密碼: a9my

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM