談談vmp的爆破
0x00 前言
玩vmp的筆記吧,祝大家新年快樂。先談談爆破,這個比起其他的方面的話,應該很多人喜歡,比較直截了當。當然,也會提一些vmp其他的東西。
0x10 需要的基礎知識
平常我們爆破一個軟體,有一種有常見的方法就是通過修改jcc跳轉來達到爆破的目的。而vmp實現jcc,跳還是不跳,其實計算是eflags。
以jz為例,大家都知道jz跳不跳其實是看zf標誌位的狀態:
zf == 1 ---> 跳轉
zf == 0 ---> 不跳轉
那麼我們看看vmp是怎麼做的,先上個elflags reg圖和給幾個要用的數字以及一個handle:
數字:0x815、0x246、0x206、0x216、0x40
(如果有玩過vmp的,應該對這幾個數字有熟悉)
handle大致樣子,記為vm_p(a,b):
not anot band a,b
下面我會一一解釋。
先看看0x815的二進位:100000010101
好像看不出來什麼所以然,那麼這樣呢?
這樣就很直觀了,其實等價於OF AF PF CF的mask。
同理大家可以看看其他的幾個數字。
比如0x40,這裡就直接用維基的圖了:
明白了這幾個數字的含義那麼我們看看可以怎麼得到zf為1的情況,之前已經說到了會用到eflag的值。是不是可以這樣:
~zf = and(0x40,not(eflags)) = 0 (1)
zf = and(0x40,eflags) = 1 (2)
注意到這裡的0和1都是所對應到eflags zf的數值
那麼eflags又是怎麼得到的,這裡vmp通過加法和0x815實現
eflags = and( eflags1, 0x815) + and( eflags2, not(0x815)) (3)
而vmp中的not,and運算都可以用vm_p來實現,比如我們將式(1)轉換後,如下:
~zf = vm_p(not(0x40),eflags) = 0
至於eflags1,eflags2我們可以並不關心他怎麼運算的,因為我們只需要修改最終的eflags即可達到爆破的目的。
不過接觸過vmp的人應該明白eflags1,eflags2的數值是通過vm_p(sn,sn)+?這樣的式子得到的。
相信vmp為什麼會選擇0x40這個數字來計算zf大家也明白了(還有一點,大家注意一下af)。
0x02 實戰
明白了原理之後,我們找個東西進行一番實踐。
這裡我選擇的是EverEdit,因為官方剛好有兩個vmp版本
使用的是官方下的32位的綠色版
你可以輸入了隨意輸入sn後(不過注意,everedit的sn太短不行,233)通過暫停法然後再單步找到vmp調用handle的地方(也就是dispather),也可以通過那塊保存sn的堆空間找到(因為是malloc的),當然方法很多。
使用trace into記錄到文件中。
接下來我們只需要找到vm_p這條handle,然後在記錄的文件之中搜索(記得倒著,從下向上)就ok。
而怎麼找handle,如果你對vmp有了解的話,應該知道其實就4套handler。
這裡我用一種我的方法,先分析是如何jmp到handle的。
0052474C 8B1485 00D05B00 mov edx,dword ptr ds:[eax*4+0x5BD000] ; <== handler調用00524753 ^E9 EA9BFAFF jmp EverEdit.004CE342004CE342 4A dec edx ; EverEdit.00524F92
然後我們在看看0x5BD000這個數組:
如果有人再仔細一些會發現,這個地址剛好是vmp0的區段的開始。
對了,可以提一提,這個數組大小是0x100 * 4。但其實vmp加密的時候第一次掃了186次,第二次隨機掃剩下的,再把地址填進去。
至於為什麼是186次:
1).186個匹配規則
2).186個handle
然後我把這些地址記錄下來,修改後列印出來:
具體代碼就不帖了。
很幸運的是,od看了看第一個handle對應的就是vm_p,如下:
0041742D 66:D3C2 rol dx,cl00417430 8B45 00 mov eax,dword ptr ss:[ebp]00417433 66:89F2 mov dx,si00417436 8B55 04 mov edx,dword ptr ss:[ebp+0x4]00417439 66:0FBAE3 01 bt bx,0x10041743E F7D0 not eax00417440 66:0FBAE4 01 bt sp,0x100417445 60 pushad00417446 F7D2 not edx00417448 F8 clc00417449 66:85E4 test sp,sp0041744C F5 cmc0041744D 21D0 and eax,edx0041744F 9C pushfd00417450 890C24 mov dword ptr ss:[esp],ecx ; EverEdit.005BA174
not eaxnot edxand eax,edx
這時候根據前面的知識,我們只需要搜索trace記錄的log,比如搜索eax=40或者edx=40或者not(40)等
搜索三次後達到找到這個地方:
00417430 Main mov eax,dword ptr ss:[ebp] ; EAX=FFFFFFBF00417436 Main mov edx,dword ptr ss:[ebp+0x4] ; EDX=000002460041743E Main not eax ; EAX=0000004000417446 Main not edx ; EDX=FFFFFDB90041744D Main and eax,edx ; FL=PZ, EAX=00000000
vm_p(not(40),eflags) ------> and( 40, not(eflags))
很明顯這裡就是取0x40與eflags進行運算了,然後相信怎麼修改大家也有明確了。
比如這裡eax得到0,註冊碼不成立,那麼正確的應該是這樣vm_p(not(0x40),0x216) = 0x40,也就是eax的值是0x40成立。後面還有vm_shr之類的運算,不過那是確定zf的。
(這裡想吐槽下EverEdit的作者,把sn一個一個讀取的過程也vm了,跑了這麼多行指令也是醉了)
然後我們可以根據寄存器的情況,寫個小腳本或者做補丁了。比如這樣的:
bp 41744dstart:runcmp eax,00000040jnz startcmp edx,FFFFFDB9jnz startcmp esp,0012E4E4jnz startend:bc 41744dret
當然這裡 取esp不太好,取esi,edi之類的更好。(可以思考一下)
停下來的時候修改寄存器的數值即可達到爆破的目的。
這裡我直接修改edx為not(206)即可。然後這個軟體好像是個重啟驗證,恩,有興趣的可以下斷之後,研究研究。應該多改一次就可以了,估計不止一個jcc。
0x03 結束語
接著說下vmp3.x的爆破,其實差不多,不過vmp3.x比起之前的來說區別還是很多
比如把handle更分散,用jmp等方法連接,然後還有一些檢測。不過大家了解了vmp對於eflag的使用,那麼應該沒啥問題。
這裡給個最新版的點,以便於大家理解。trace into之後的對比前16w行即可
00423F89 8A45 0B mov al,byte ptr ss:[ebp+0xB]00423F8C 8BE5 mov esp,ebp00423F8E 5D pop ebp ; EverEdit.00423ED300423F8F C2 0800 retn 0x8
最後說下vmp。在未了解之前,就一直說聽過。在了解了之後,才發現這個東西設計得多麼細膩,從反彙編引擎到加殼的過程可以說是一個很順暢的流程。當然vmp3.x重寫了反彙編引擎(有點怪異..),然後想看怎麼加殼發現流程被vm了。
[原創]談談vmp的爆破-『軟體逆向』-看雪安全論壇歡迎關注 「看雪學院」 公眾號:ikanxue
推薦閱讀:
※攻擊TrustZone系列(Pt.2) -- 逆向高通TrustZone
※旅行的青蛙Unity遊戲逆向修改Android&iOS
※堆溢出研究二