虛擬機器保護已經是現代保護殼不可缺少的一環,雖然逆向方也發展出各種外掛幫助分析,但只針對特定某款,通用性的方法卻不多見。我總在想,既然虛擬機器的結構是固定的,如果有一款工具能夠記錄指令流,那麼按圖索驥,也許能發展出一套通用的分析方法來。其實OD(OllyDbg)就有記錄指令流的功能,叫跟蹤(trace),也許是效果不好或者操作不便,用的人甚至知道的人不多。先介紹下怎麼用。
OD的跟蹤功能原理很簡單,就是每一步都自動下單步斷點,然後記錄斷下來的指令資訊。這項功能涉及到幾項設定,第一項是快取大小,不難想象,跟蹤得到的這一些列的指令記錄是需要佔地方儲存的,佔多大可以設定,位置在除錯選項(Debugging options)->跟蹤(Trace),如圖1。
圖1
第一項就是快取的大小,記憶體允許的話,自然是多多益善,畢竟快取越大,允許記錄的資訊越多。第二項是記錄的內容,跟蹤會自動記錄地址模組等資訊,此外可以選擇是否記錄指令、ESP和標誌位的資訊。設定位置緊接著快取大小,見圖2,可以按需勾選,本文只需要記錄指令即可。最後一項是在除錯(DEBUG)選單中開啟Trace。
圖2
現在Trace已經設定完畢了,按下Ctrl+F12,檢視Trace視窗,應該已經開始記錄執行過的指令。否則請檢查前述設定和操作是否正確。
那麼,虛擬機器保護要怎麼入手分析呢?前面我提到,虛擬機器是有固定結構的,既然要分析,那對應的找到這些結構應該就可以了。傳統保護虛擬機器的結構其實很簡單,大致可以看成一隻章魚,有三個部分,分別是init(頭),Dispatch(身)和Handle(觸鬚),如圖3:
圖3
Init主要完成虛擬機器初始化工作,例如申請記憶體填寫初始值之類,每次進入虛擬機器,這個“頭部”通常只執行一次。Dispatch是虛擬機器的主體,可以看成一個主迴圈,它是每一條虛擬機器指令的開始之處,也是結束之處,負責讀取虛擬機器指令,進入具體handle解釋等工作。Handle就是虛擬機器的“指令”了,實際完成各項虛擬機器指令的功能。 我曾寫過一篇《基於虛擬機器的軟體保護技術》較為詳細的介紹過虛擬機器保護技術,對基本結構還不太熟的同學,此文會對上述概念有更詳細的說明。
現在,我們就要在具體的軟體中找這隻“章魚”了。以一個CrackMe為例,首先清理所有斷點,開啟Trace,Ctrl+F12跟蹤步過執行,看到程式跑起來了,F12暫停,看Trace的視窗如下(圖4):
圖4
記錄是從下往上看的,可以看出,在程式空間的最後一條支流,是00401534的一個call,呼叫了DialogBoxInDirectParamA,這是一個調出系統對話方塊的API,其中有一個引數DlgProc用來指明訊息回撥函式的位置,我們直接在反彙編視窗檢視這個API,發現回撥函式是0x401572(圖5):
圖5
0x401572處程式碼不長,有好幾條Call,但大部分都是系統Call,只有一處呼叫了程式空間的函式,這個函式就是虛擬機器的入口。到這裡,我們對虛擬機器的分析的工作才剛剛開始。
首先對虛擬機器的入口下斷,然後重新執行程式。目的是保證能夠正確找到init。現在應該端在虛擬機器的入口處,如下圖:
圖6
這是個非常簡單的虛擬機器,有經驗的同學也許可以一眼就看出來圖6包含了Init和Dispatch分別在哪裡。當然也可以用Trace快速找出虛擬機器的各個結構。現在去掉斷點,開啟Trace,Ctrl+F12跟蹤步過,這時程式會跑起來,多點選幾下按鈕,目的是讓主要分支得到更充分的執行(即增加獲得執行的次數),然後F12暫停。回到Trace視窗,對著任意一行程式空間的指令點選右鍵,選擇模組統計,結果如下圖:
圖7
統計是以程式碼段來劃分的,第一欄顯示的是這段程式碼在剛才的跟蹤執行中執行的次數,第二欄顯示了某個程式碼段的首地址。我們先找執行了一次的指令首地址。可以找到第5行的地址就是虛擬機器的入口地址,點選在反彙編視窗跟隨,可以看到這段程式碼是從0x00401060到0x004010B9,這就是init:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
00401060 $ 55 push ebp 00401061 . 8BEC mov ebp, esp 00401063 . 81C4 D0FEFFFF add esp, -0x130 00401069 . C745 E4 00000>mov dword ptr [ebp-0x1C], 0x0 00401070 . C745 E8 00000>mov dword ptr [ebp-0x18], 0x0 00401077 . C745 F1 00000>mov dword ptr [ebp-0xF], 0x0 0040107E . C645 FD 00 mov byte ptr [ebp-0x3], 0x0 00401082 . C645 FE 00 mov byte ptr [ebp-0x2], 0x0 00401086 . C745 F5 00000>mov dword ptr [ebp-0xB], 0x0 0040108D . 8D85 D0FEFFFF lea eax, dword ptr [ebp-0x130] 00401093 . 8945 F1 mov dword ptr [ebp-0xF], eax 00401096 . 8B45 14 mov eax, dword ptr [ebp+0x14] 00401099 . 8945 E0 mov dword ptr [ebp-0x20], eax 0040109C . 8B45 08 mov eax, dword ptr [ebp+0x8] 0040109F . 8945 D0 mov dword ptr [ebp-0x30], eax 004010A2 . 8B45 0C mov eax, dword ptr [ebp+0xC] 004010A5 . 8945 D8 mov dword ptr [ebp-0x28], eax 004010A8 . C745 DC 00000>mov dword ptr [ebp-0x24], 0x0 004010AF . C745 D4 00000>mov dword ptr [ebp-0x2C], 0x0 004010B6 . 8B45 10 mov eax, dword ptr [ebp+0x10] 004010B9 . 8945 EC mov dword ptr [ebp-0x14], eax |
接著找Dispatch,剛才說過,它既是虛擬機器指令的開始,又是結束,它得到的執行次數一定也最多。可以看到第三行的0x004010B9,這個地址在虛擬機器入口地址之後,執行次數最多,同樣的辦法可以看到這段程式碼的終止位置是0x004010D9:
1 2 3 4 5 6 7 8 9 |
004010BC > /FF45 EC inc dword ptr [ebp-0x14] 004010BF . |8B45 EC mov eax, dword ptr [ebp-0x14] 004010C2 . |8A00 mov al, byte ptr [eax] 004010C4 . |8845 F0 mov byte ptr [ebp-0x10], al 004010C7 . |B8 00204000 mov eax, 00402000 004010CC . |0FB65D F0 movzx ebx, byte ptr [ebp-0x10] 004010D0 . |C1E3 02 shl ebx, 0x2 004010D3 . |03C3 add eax, ebx 004010D5 . |FF20 jmp dword ptr [eax] |
最後是找這次執行虛擬機器用到的handle。這個不難,虛擬機器入口地址之後的程式碼段除了init和dispatch,其它都是handle,所有執行過的handle都會在裡面出現。當然了,某條handle的具體作用,以及沒有執行過的handle,就只能靠人肉分析了。還有就是,就分析虛擬機器保護來說,瞭解執行了哪些handle,以及哪些handle更常用,這些資訊都是十分有用的。