第五課 文件類型分析
拿到一個殼,第一步就是用相關工具分析一下是什麼殼,然後就可心中有數地跟蹤分析。文件分析工具有
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。,
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。等。
1.PEiD
PEiD的GUI界面操作非常方便直觀。它的原理是利用查特徵串搜索來完成識別工作的。各種開發語言都有固定的啟動代碼部分,利用這點就可識別出是何種語言編編譯的。同樣,不同的殼也有其特徵碼,利用這點就可識別是被何種殼所加密。PEiD提供了一個擴展接口文件userdb.txt ,用戶可以自定義一些特徵碼,這樣就可識別出新的文件類型。
有些外殼程序為了欺騙PEiD等文件識別軟件,會偽造啟動代碼部分,例如將入口代碼改成與Visual C++ 6.0所編程程序入口處類似代碼,即可達到欺騙目的。
所以,文件識別工具所給出的結果只是個參考,文件是否被加殼處理過,還得跟蹤分析程序代碼才可得知。 可參考這個文檔瞭解如何偽裝:
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。 。目前Hying的殼PE-Armor偽裝能力是最強的:
PEiD分析不出類型的文件就報告是「Nothing found *」,如出現這情況一般都是未知殼或新版的殼。
下面PEiD識別出這個軟件是用Asprotect 1.2x加的殼。
2.FileInfo
FileInfo(簡稱Fi)另一款不錯的文件檢測工具。Fi運行時是DOS界面,在DOS窗口中運行程序相當不便,建議採用下面的技巧:
1.用鼠標將文件拖到Fi主文件上。
2.將Fi快捷方放進Windows的SendTo文件夾裡.以後要分析某文件,只需右擊「發送到」功能就可打開Fi。
FileInfo升級慢,其識別庫不能自定義。而PEiD升級比較頻繁,用戶也可自定義特徵碼,因此PEiD用的比較普遍。
有時,FileInfo和PEID會報「PE Win GUI」,Win GUI就是Windows圖形用戶界面程序統稱,表明程序
可能沒加殼。但不排除也有加殼的可能性,下圖是一個ExeCryptor 2.2x的殼,FileInfo報「*PE Win GUI *section* ??」,其不能識別出來。識別信息中帶了個問號,表明FI對給出的結果不是太肯定。
第六課 尋找OEP
一般的壓縮殼,如Aspack等都有
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。 。而加密殼(如ASProtect,Armadillo) 一般很少有脫殼機,必須手工脫殼。手工脫殼一般情況是分三步:一是查找程序的真正入口點(OEP);二是抓取內存映像文件;三是輸入表重建。(當然現在的加密殼複雜些,要考慮更多的東西)
OEP是Original Entry Point縮寫,即程序加殼前的真正的入口點。
外殼初始化的現場環境(各寄存器值)與原程序的現場環境是相同的。加殼程序初始化時保存各寄存器的值,外殼執行完畢,會恢復各寄存器內容。其代碼形式一般如下:
PUSHFD ; 將標誌寄存器入棧保存
PUSHAD ; push eax, ecx, edx, ebx, esp, ebp, esi, edi
…… ; 外殼代碼部分
POPAD ; pop edi, esi, ebp, esp, ebx, edx, ecx, eax
POPFD ; 恢復標誌寄存器
JMP OEP ;
OEP: …… ; 解壓後的程序原代碼
為了講述方便,本節用UPX加殼的Win98記事本來演示。首先用PEid查看加殼前的記事本:
PEid顯示Notepad.exe程序是用Microsoft Visual C++ 6.0編譯的,接下來用UPX來加殼,方法是開個DOS窗口,用命令upx notepad.exe。如下圖所示:
這時再用PEid查看加殼的文件,PEid會給出如下信息:UPX 0.89.6 - 1.02 / 1.05 - 1.24 -> Markus & Laszlo
UPX的殼可以用UPX.exe自身來脫,命令是:upx -d 文件名 。一些變種的UPX殼用UPX.EXE自身脫不了,這時可以試試
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。這款工具。
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。
脫殼前建議用PE工具
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。
打開目標文件查看一下區塊,以盡可能地多瞭解一些信息,對脫殼有幫助,如下圖:
1.根據跨段指令尋找OEP
推薦用Ollydbg來調試脫殼,比SoftICE和TRW2000方便多了。運行Ollydbg,點擊菜單「選項/調試設置」,將第一次暫停設在WinMain函數上。再用Ollydbg打開實例notepad.upx.exe就可中斷在外殼的入口點處了:
上圖相關代碼如下:
0040E8C0 > 60
pushad //一開始Ollydbg就會中斷這行,這個就是外殼的入口點,注意這個pushad指令
絕大多數加殼程序在被加密的程序中加上一個或多個段,所以依據跨段的轉移指令(JMP)就可找到真正的入口點,此時就會有POPAD/POPFD指令出現。UPX 用了一次跨段的轉移指令(JMP),在跳到OEP處會看到虛擬地址的值有一個突變,此時就能確定OEP了。
UPX殼比較簡單,大家不必要跟蹤去找這個跨段的轉移指令,中斷WinMain後,只需要在Ollydbg裡往下翻屏,就會發現這個跨段轉移指令:
上圖相關代碼如下:
0040EA0E 61
popad //注意這裡的popad指令,和開始的pushad對應
0040EA0F - E9 B826FFFF jmp 004010CC
//這裡跳到OEP,將光標移到這,按F4執行到這行
這一句
0040EA0F jmp 004010CC
就是跳到OEP的指令,執行到這,UPX外殼己將程序解壓完畢,並模擬Windows加載器的將原始程序加載到內存,004010CC 就是映射到內存目標程序的入口點,此時就可抓取內存映像文件了。
2.根據堆棧平衡原理找OEP
這個堆棧平衡原理其找OEP原理這篇文檔描述的比較詳細:
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。
操作方法:多數殼在運行到OEP的時候ESP=0012FFC4,這就是說程序的第一句是對0012FFC0進行寫入操作,只要在0012FFC0下硬件寫入斷點(命令行裡鍵入HW 12FFC0),我們就能停在OEP的第二句處。
用OllyDBG重新加載實例程序notepad.upx.exe,在命令行下硬件寫斷點:
按F9執行程序,就會中斷在OEP第二行:
此時如果將光標向上移,會發現第一句代碼變亂了:
004010C7 000D 0A000055 add [5500000A], cl
004010CD 8BEC mov ebp, esp
這是因為Ollydbg將數據當彙編代碼來分析了,你可以按 Ctrl+ALT+向上光標鍵 將當前顯示的代碼向上滾動一個字節就可看到正確的彙編代碼了:
004010CC 55 push ebp
004010CD 8BEC mov ebp, esp
//中斷在這行
004010CF 83EC 44 sub esp, 44
004010D2 56 push esi
004010D3 FF15 E4634000 call [4063E4] ; kernel32.GetCommandLineA
中斷後,別忘點擊菜單「調試/硬件斷點/」打開硬件斷點面板,將剛才的硬件斷點刪除。
小知識:
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。
3.根據編譯語言特點找OEP
各類語言編譯的文件入口點都有一些規律,可以這利用這點來尋找入口點。
1)Delphi程序
執行程序,用LordPE(或Prodump)選dump(full)脫殼,存為dump.exe。接著用Hex Workshop打開dump.exe,搜索文本「runtime」,搜到後,向前查找離「runtime」最近的十六進制數字「55 8B EC」,數字所在的地址就是程序的OEP。
2)Visual C程序
可以利用Visual C啟動部分的幾個函數GetCommandLineA(W)、GetVersion、GetModuleHandleA(W)、GetStartupInfoA(W) 等來定位程序的OEP。
常見的各類編譯語言的入口彙編代碼都要熟悉,因為一些加密強殼會偷OEP處的代碼到殼裡,一般情況各編譯語言入口代碼都相同,到時只需要直接引用相關程序的入口代碼,這給我們恢復代碼帶來方便。
4.用內存斷點找OEPAuthor:Lenus
From:
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。 &
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。
E-mail︴email]GLenus_M@163.com[/email]
--------------------------------------------------
1.前言
發現論壇中很多兄弟在詢問:什麼是二次內存斷點,三次內存斷點。還有很多人對內存斷點的原理不是很明白。其實只要懂得殼是如何解壓代碼的,那麼就完全可以按自己的喜歡來下斷。
本文要解決的問題是:
1.什麼是內存斷點?
2.如何在尋找OEP時使用內存斷點。
3.內存斷點的局限性。
2.內存斷點尋找OEP的原理
i.首先,在OD中內存斷點,硬件斷點和普通斷點(F2下斷)是有本質區別的。硬件斷點等效與SoftICE命令bpm,他的中斷要用到DR0-DR7的調試寄存器,也就是說OD通過這些DR0-DR7的調試寄存器來判斷是否斷下。
普通斷點(F2下斷)等效於bpx,他是在所執行的的代碼的當前地址的一個字節修改為CC(int3)。當程序運行到int3的時候就會產生一個異常,而這個異常將交給OD處理,把這個異常的regEIP-1以後就正好停在了需要的中斷的地方(這個根據系統不同會不一樣),同時OD在把上面的int3修改回原來的代碼。
而內存斷點基本上使用的是對代碼使用的保護屬性來實現中斷。
內存斷點分為:內存訪問斷點,內存寫入斷點。
我們知道,在程序運行的時候會有3種基本的狀態產生:讀取,寫入,執行。
004AE242 A1 00104000 mov eax,dword ptr ds:[004AE24C] //004AE24C處的內存讀取
004AE247 A3 00104000 mov dword ptr ds:[004AE24C],eax //004AE24C處的內存寫入
004AE24C 83C0 01 add eax,1 //004AE24C處的內存執行
那麼我們應該如何中斷在上面的幾行呢?
1.當我們對004AE24C下內存訪問斷點的時候,可以中斷在004AE242也可以中斷在004AE247。
2.當我們對004AE24C下內存寫入斷點的時候,只能中斷在004AE247。
3.當我們對004AE24C下內存訪問斷點的時候,能中斷在004AE24C。
到這裡你可能不明白了,為什麼內存訪問斷點能中斷在004AE247這一句對004AE24C的寫入,而且還能中斷在004AE24C的執行呢?
其實很簡單,我們只要仔細體會一下「內存訪問」這四個字的含義遍可以知道,當我們對004AE24C進行讀取的時候需要「訪問」他吧,當我對004AE24C進行寫入的時候也需要「訪問」他吧!!當然我們要執行內存地址004AE24C的代碼的時候也是還是要「訪問」他的!
所以我們不難得出下面的結論:
1.內存寫入中斷的地方,一定是也可以用內存訪問中斷。
2.內存執行的地方,也可以用內存訪問中斷。
如果這時你認為,那麼內存寫入豈不是沒用了。呵呵~那我要告訴你當然不是,如果你想快速的準確的定位到004AE247這一行的時候,那麼他就大有作用了!
總結一下:內存斷點不修改改原代碼,不會像普通斷點那樣因為修改代碼被程序校驗而導致中斷失敗;對於區段的訪問只是區域大了一點,其原理和上面分析的三行代碼是一樣的。
ii.如何使用內存斷點來尋找OEP呢?
要回答這個問題首先要回答這一個問題:殼是如何解壓代碼的?
正如我們知道的,殼如果要把原來加密或壓縮的代碼運行起來就必須要解壓和解密原來的代碼。而這一個過程我們難道不能將他看做是對代碼段(code段)的寫入嗎?好了,解壓完畢了。我們要從殼代碼的區段JMP到原來的代碼段的時候,難道不正是對代碼段(code段)的執行嗎?
理清了上面的關係就好辦了,那麼如果載入OD後,我們直接對code段下內存訪問斷點的時候,一定會中斷在殼對code段的寫入的代碼的上面,就像上面的004AE247的這一行。而如果當他把code段的代碼全部解壓解密完畢了以後,JMP到OEP的時候,我們是不是還可以停在OEP的代碼上面呢?而且每按下F9都會中斷,因為這時code段在執行中哦!
相信很多人到這裡已經明白了,為什麼在教程中到達了某一個時候,某一行的時候。牛人們就叫我們對code段下內存訪問斷點了吧。
而如果你還要繼續問我為什麼一定要到那個地方才可以下斷呢?我難道不可以一開始就下斷嗎?
正入我上面所說的,如果你在前面下斷很可能殼對code段還沒解壓完畢呢,這時如果你不停的按F9,你將會看到OD的下方不斷的在提示你「對401000寫入中斷」 「對401002寫入中斷」「對401004寫入中斷」.......如果你不介意按F9到他把正個code段寫完的話,我除了同情你的「F9」以外,沒什麼其他的意見!
那麼我們就沒有別更快一點的辦法了嗎?
有的!那就是我們呼之欲出的兩次內存斷點辦法。
怎麼理解兩次內存斷點呢?
讓我來做一個假設吧,假設我是一個殼的作者。一個EXE文件的有code段,data段,rsrc段.....依次排列在你的內存空間中,那麼我會怎麼解碼呢?呵呵~我比較笨一點,我會先將code段解碼,然後再將data段解壓,接著是rsrc段......那麼聰明的你不難發現,只要你在data斷或者rsrc段下內存訪問斷點,那麼中斷的時候code段就已經解壓完畢了。這時我們再對code段下內存反問斷點,不就可以到達OEP了嗎?
這裡注意上面雖然下了兩次內存訪問斷點,但是本質是不一樣的,目的也是不一樣的。
1.對data段下內存訪問斷點而中斷是因為內存寫入中斷,目的是斷在對對data段的解壓時,這時殼要對data段寫數據,但是code段已經解壓完畢。
2.對code段下內存訪問斷點而中斷是因為內存執行中斷,目的當然就是尋找OEP了。
總結一下:如果我們知道殼在什麼地方對code段解壓完畢我們就可以使用內存斷點,找到OEP。如果不知道,那麼我們就依靠2次內存斷點去找,如果還不行就用多次內存斷點。總之明白了原理在多次的內存斷點其實都一樣。從這個過程中我們瞭解的是殼在對區段解碼的順序!
iii.實戰
說了這麼多,我想大家都越越欲試了吧。
好吧,來弄一個猛殼怎麼樣:
註:本節實例有些難度,不適合新手練習,新手可以跳過這個實例的學習,等找到合適的實例會補充上來的
訪客無法瀏覽此圖片或連結,請先 註冊 或 登入會員 。
這個殼是一個hying的舊版,我們用他來實驗一下我們內存斷點法。
OD載入以後來到這裡
0040D000 u> 56 push esi //這裡
0040D001 52 push edx
0040D002 51 push ecx
0040D003 53 push ebx
0040D004 55 push ebp
0040D005 E8 15010000 call unpackme.0040D11F
根據跟過一次的經驗我們將先設置,除int3異常以外忽略其他異常,SHIFT+F9
003725B1 64:8925 0000000> mov fs:[0], esp
003725B8 CC int3
003725B9 90 nop //到這裡
003725BA 8BCD mov ecx,ebp
然後再設置除「除零」異常外,忽略其他異常。SHIFT+F9
00372660 F7F3 div ebx //到這裡
00372662 90 nop
下面是很多的單步異常,太麻煩我們不管他,現在開始用內存斷點的方法(記得將所有異常忽略)。
對code段下內存訪問斷點,希望他已經解壓完畢。方法是按ALT+M鍵打開內存窗口,在.code段按F2設斷:
SHIFT+F9執行:
0040D19D A4 movs byte ptr es:[edi],byte ptr ds:[esi] //還沒解完呢
0040D19E B3 02 mov bl,2
對data段下內存「寫入」斷點,試試看他是不是要寫data段。
00372712 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] //斷到這裡
00372714 5E pop esi
下面再對code段下內存訪問斷點。F9
00372855 8907 mov dword ptr ds:[edi],eax ; SHELL32.DragFinish //這裡是對IAT加密
的地方了!!!
00372857 5A pop edx
00372858 0FB642 FF movzx eax,byte ptr ds:[edx-1]
0037285C 03D0 add edx,eax
0037285E 42 inc edx
0037285F 83C7 04 add edi,4
00372862 59 pop ecx
00372863 ^ E2 A9 loopd short 0037280E
00372865 ^ E9 63FFFFFF jmp 003727CD
0037286A 8BB5 93060000 mov esi,dword ptr ss:[ebp+693] //到這裡下斷F2
現在如果再對data下訪問斷點已經是沒用了。這時應該格外的小心。
我們現在就想既然這一段是對code解碼的,那麼我們就繞過他吧!
到0037286A下斷F2,然後清除內存斷點!!!!
F9以後停在這裡,繼續對code下內存訪問斷點。
看看左下角還在解碼,哎~真是麻煩!
003728E1 /EB 1D jmp short 00372900
003728E3 |25 FFFFFF7F and eax,7FFFFFFF
003728E8 |0385 83060000 add eax,dword ptr ss:[ebp+683]
003728EE |2B85 8F060000 sub eax,dword ptr ss:[ebp+68F]
003728F4 |8BDE mov ebx,esi
003728F6 |2BD8 sub ebx,eax
003728F8 |8958 FC mov dword ptr ds:[eax-4],ebx //停在這裡
003728FB |83C7 08 add edi,8
003728FE ^|EB DB jmp short 003728DB
00372900 \64:FF35 30000000 push dword ptr fs:[30] //清除內存斷點以後到這裡下斷,F9
又是一段解碼的代碼,再次使用上面的辦法手動跳出去。
現在繼續對code段下內存訪問斷點!!F9以後到達這裡。
004010CC FFD7 call edi ; unpackme.004010CE //OEP哦
004010CE 58 pop eax
004010CF 83EC 44 sub esp,44
004010D2 56 push esi
004010D3 90 nop
004010D4 E8 B518F7FF call 0037298E
004010D9 8BF0 mov esi,eax
呵呵~雖然不是我們熟悉的OEP,但是地址是沒錯了,況且根據我們的步驟,我可以很肯定的說這是code段的第一次「執行」中斷!
所以這就是OEP了。
總結一下:當我們在尋找OEP的時候,要多次對code下斷「賭」一「賭」他解壓完畢,如果不是就對別的段試試~如果程序跑飛了,那就沒辦法了,重來唄~其實說起來要賭的是:當data段,idata段,rsrc段擺在你的面前,你會好好「珍惜」那個段,不過還好上天還會給我們從來一次的機會(ctrl+F2 ^_^),那麼我們會對那個不會跑飛的段說3個字----「先斷你」如果非要在上面加一個次數,我希望是「一次內存斷點就好了」
vi.下面來討論一下內存斷點的局限性問題。
是不是什麼殼都可以用內存中斷啊?
不是每個都可以的,一些像UPX和ASPACK就不行。
為什麼?
呵呵~follew me!
情況1.
我們來看看UPX的殼
首先,他的殼代碼在UPX1段。
這裡是他要跳到OEP的地方
0040ED4F /77 11 ja short NOTEPAD_.0040ED62
0040ED51 |01C3 add ebx,eax
0040ED53 |8B03 mov eax,dword ptr ds:[ebx]
0040ED55 |86C4 xchg ah,al
0040ED57 |C1C0 10 rol eax,10 //在解碼
0040ED5A |86C4 xchg ah,al
0040ED5C |01F0 add eax,esi
0040ED5E |8903 mov dword ptr ds:[ebx],eax
0040ED60 ^|EB E2 jmp short NOTEPAD_.0040ED44
0040ED62 \24 0F and al,0F
0040ED64 C1E0 10 shl eax,10
0040ED67 66:8B07 mov ax,word ptr ds:[edi]
0040ED6A 83C7 02 add edi,2
0040ED6D ^ EB E2 jmp short NOTEPAD_.0040ED51 //回跳解碼
0040ED6F 61 popad
0040ED70 - E9 5723FFFF jmp NOTEPAD_.004010CC //跳到OEP
我們看到他在對code段解壓完畢的時候馬上就JMP到OEP去了,那麼我們根本就來不及使用內存斷點的辦法。
你可能說,我可以在
0040ED6F 61 popad //這一句下段然後使用啊
呵呵~~當然可以,不過你把花在下內存斷點的時間,多按下幾次F8不更好?!
也就是說當一個殼如果他在JMP 到OEP前的一行代碼仍在都在對code段解壓,那麼我們就不能再使用這種辦法了!
或者說我們沒必要使用內存斷點更貼切一點!
情況2.
對於一些在OEP處有stolen code的代碼
我們來看看一個OEP
0049E2F4 u> 55 push ebp //OEP
0049E2F5 8BEC mov ebp,esp
0049E2F7 83C4 F4 add esp,-0C
0049E2FA B8 BCE04900 mov eax,unpack.0049E0BC
0049E2FF E8 048CF6FF call unpack.00406F08 //這裡調用子程序
0049E304 A1 B8FE4900 mov eax,dword ptr ds:[49FEB8]
0049E309 50 push eax
0049E30A 6A 00 push 0
0049E30C 68 1F000F00 push 0F001F
0049E311 E8 E68EF6FF call //API
0049E316 A3 60194A00 mov dword ptr ds:[4A1960],eax
0049E31B 833D 60194A00 00 cmp dword ptr ds:[4A1960],0
這個軟件在被PESPIN加殼了以後這些全被偷掉了!
也就是說,殼在模擬OEP代碼的時候必然會執行
0049E2FF E8 048CF6FF call unpack.00406F08 //這一步
而這個地方是call向code段的。如果我們使用內存訪問斷點,那麼就停在這個子程序的地方
00406F08 50 push eax //會停在這裡
00406F09 6A 00 push 0
00406F0B E8 F8FEFFFF call
00406F10 BA 04F14900 mov edx,unpack.0049F104
00406F15 52 push edx
這裡既不是處理stolen code的地方,也不是FOEP的地方。這就會對我們的判斷產生誤導。
當然你可以alt+F9返回到殼處理stolen的地方,然後用內存斷點,或者按幾下F8到達FOEP處,但試問如果你拿到一個未知的殼的時候又怎麼知道應該這麼處理呢?
還有其他一些情況留給大家總結吧!
在下的磚已拋出,各位的玉不久矣。
--------------------------------------------------
3.總結
好了說了很多,大家應該對內存斷點的辦法有了全面的瞭解,如果瞭解了內存斷點的原理就不難明白他的使用方法,不難明白為什麼有寫殼不能使用內存斷點的辦法,其實任何的一種辦法都需要經驗的積累。相信如果大家在回答開篇的3個問題,已經不難了。
大家可以結合原理再好好的體會一下《手動脫殼進階第八篇Skvp1.32》這篇文章。