發新話題

[轉貼] 寫給新手 - 淺談脫殼方法

[轉貼] 寫給新手 - 淺談脫殼方法

原作者:xIkUg
原文出處 訪客無法瀏覽此圖片或連結,請先 註冊登入會員

【標  題】寫給新手 - 淺談脫殼方法
【作  者】xIkUg[CCG][BCG][DFCG][DCM][CZG][D.4s]
【日  期】2004-12-18
【使用工具】OllyDBG, ImportREC
【主  頁】http://bbs.xp-program.com
【平  台】WinXP sp1
【目標程序】輕輕鬆鬆學開車 6.6
【下載地址】http://www.sharebank.com.cn/soft/soft_view.php?id=10506
【作者聲明】只是感興趣,沒有其他目的。失誤之處敬請諸位大俠賜教!
【正 文】
我很少寫破文, 最近看到一些新手常問查殼工具查不出是什麼殼,末知殼如何脫...我這裏就簡單的用一個範例

程式談談脫殼

方法. 算是給新手打打氣.

一般這種沒有公開的殼不會太猛(除了hying, vcasm, jingulong等這些圈內人的殼哈), 大多是自己土製的殼,

有 的還偽裝一下, 致使查殼工具查不出或查出什麼Aspack, UPX的殼. 碰 到這種殼不用太怕, 認真跟蹤分析一

下一般都沒多大問題.

一般加殼後的程式運行時大多是用各種方法 AntiDebugger, 其中夾雜很多花指令和垃圾指令, 然後解碼, 又是

一堆AntiDebugger, 可能又解碼, 恢復IAT, 又是一堆 AntiDebugger...最後再跳到程式真正的OEP處, 對付這

種殼 一般的方法是先找到OEP, 再修復IAT

在殼跳到OEP的時候一般是個跨段跳轉, 跳轉方法有很 多,

如JMP XXXXXXXX, JMP [XXXXXXXX], JE [XXXXXXXX],
PUSH XXXXXXXX RET, CALL XXXXXXXXX, CALL [XXXXXXXX],
CALL EAX...

還有的是在SEH中直接指定EIP等等, 找到這個跳轉位址,然後在相應位址上設上中斷點, F9運行, 就到達OEP處

了, 此 時就是最好的Dump時機

修復IAT一般用ImportREC, 填寫好OEP的RVA, 先點 IATAutoSearch, 再點GetImport, 如果找到的API全部有效

就可以點Fix Dump了, 如果有無效的就需要修復, 先點Show Invalid, 再對這些無效的API點右鍵, 共有3個修

復級別, 分別是Disasm, HOOK, Trap Flag, 可以一個一個試, 全部API有效或只有一兩個API無效時就可以點

Fix Dump了, 如果修復不了就只有用手工修復了.

手工修復IAT的方法是, 一般殼在運行的時候會恢復IAT或破壞IAT, 你可以在恢復IAT後或破壞IAT之前DUMP出完

好的IAT, 再用16進位編輯器, 把完整的IAT複製到Dump出來的檔中, 多做做練習就不難了

還是看看目的程式"輕輕鬆松學開車 6.6", 用PEiD查一下, ASPack 2.12 -> Alexey Solodovnikov
用OD載入, 提示入口點在代碼外部(以後別再問如何判斷程序是否加殼了, 用OD載入如果有這個提示就表示程式

是加了殼的), 點確定, 問是否分析, 點否, 來到入口處

006AF001 D> 60 pushad ; 殼代碼入口
006AF002 E8 03000000 call DrvStudy.006AF00A ; 有花指令, F7 跟進
006AF007 - E9 EB045D45 jmp 45C7F4F7
006AF00C 55 push ebp
006AF00D C3 retn
006AF00E E8 01000000 call DrvStudy.006AF014
006AF013 EB 5D jmp short DrvStudy.006AF072

在006AF002處call DrvStudy.006AF00A, 代碼中沒有 006AF00A, 是因為花指令讓OD錯誤的分析了代碼, 用F7跟

進就能來到006AF00A處, 像這種近距離的Call, 其實是變形的Jmp, 都要用F7跟進, 如果F8跳過的話可以會碰到

裏面的陷阱

006AF00A 5D pop ebp ; DrvStudy.006AF007, 跳到這裏
006AF00B 45 inc ebp ; ebp + 1
006AF00C 55 push ebp ; 變形跳轉到ebp的位址處
006AF00D C3 retn

來到
006AF008 /EB 04 jmp short DrvStudy.006AF00E ; 來到這裏
006AF00A |5D pop ebp
006AF00B |45 inc ebp ; ebp + 1
006AF00C |55 push ebp ; 變形跳轉到ebp的地址處
006AF00D |C3 retn
006AF00E \E8 01000000 call DrvStudy.006AF014 ; 變形跳轉, F7跟進

006AF014 5D pop ebp
006AF015 BB EDFFFFFF mov ebx,-13
006AF01A 03DD add ebx,ebp
006AF01C 81EB 00F02A00 sub ebx,2AF000 ; 代碼重定位
006AF022 83BD 22040000 00 cmp dword ptr ss:[ebp+422],0 ; 變數ebp+422是存放的基址
006AF029 899D 22040000 mov dword ptr ss:[ebp+422],ebx
006AF02F /0F85 65030000 jnz DrvStudy.006AF39A ; 這裏不跳
006AF035 |8D85 2E040000 lea eax,dword ptr ss:[ebp+42E] ; "kernel32.dll"
006AF03B |50 push eax ; 參數入疊
006AF03C |FF95 4D0F0000 call dword ptr ss:[ebp+F4D] ; kernel32.GetModuleHandleA
006AF042 |8985 26040000 mov dword ptr ss:[ebp+426],eax ; kernel32.dll的控制碼存入ebp+426處
006AF048 8BF8 mov edi,eax ; kernel32.77E40000
006AF04A 8D5D 5E lea ebx,dword ptr ss:[ebp+5E] ; "VirtualAlloc"
006AF04D 53 push ebx
006AF04E 50 push eax ; Kernel32.dll控制碼
006AF04F FF95 490F0000 call dword ptr ss:[ebp+F49] ; GetProcAddress
006AF055 8985 4D050000 mov dword ptr ss:[ebp+54D],eax ; 把VirtualAlloc的函數位址存入ebp+54D處
006AF05B 8D5D 6B lea ebx,dword ptr ss:[ebp+6B] ; "VirtualFree"
006AF05E 53 push ebx
006AF05F 57 push edi ; Kernel32.dll控制碼入疊
006AF060 FF95 490F0000 call dword ptr ss:[ebp+F49] ; GetProcAddress
006AF066 8985 51050000 mov dword ptr ss:[ebp+551],eax ; 把VirtualFree的函數位址存入ebp+511處
006AF06C 8D45 77 lea eax,dword ptr ss:[ebp+77] ; 跳轉地址載入eax
006AF06F FFE0 jmp eax ; 跳到eax指向的地方

006AF08A 8B9D 31050000 mov ebx,dword ptr ss:[ebp+531] ; 來到這裏, ebx=0
006AF090 0BDB or ebx,ebx
006AF092 /74 0A je short DrvStudy.006AF09E ; ebx==0,跳
006AF094 |8B03 mov eax,dword ptr ds:[ebx]
006AF096 |8785 35050000 xchg dword ptr ss:[ebp+535],eax
006AF09C |8903 mov dword ptr ds:[ebx],eax
006AF09E \8DB5 69050000 lea esi,dword ptr ss:[ebp+569] ; 跳到這裏, esi指向一標誌
006AF0A4 833E 00 cmp dword ptr ds:[esi],0
006AF0A7 /0F84 21010000 je DrvStudy.006AF1CE ; 不為0, 不跳
006AF0AD 6A 04 push 4
006AF0AF 68 00100000 push 1000
006AF0B4 68 00180000 push 1800
006AF0B9 6A 00 push 0
006AF0BB FF95 4D050000 call dword ptr ss:[ebp+54D] ; 分配記憶體

C語言描述為
VirtualAlloc(NULL, 0x1800, MEM_COMMIT, PAGE_READWRITE);
006AF0C7 8B46 04 mov eax,dword ptr ds:[esi+4]
006AF0CA 05 0E010000 add eax,10E
006AF0CF 6A 04 push 4
006AF0D1 68 00100000 push 1000
006AF0D6 50 push eax
006AF0D7 6A 00 push 0
006AF0D9 FF95 4D050000 call dword ptr ss:[ebp+54D] ; 再次分配記憶體, eax為所需記憶體大小
006AF0DF 8985 52010000 mov dword ptr ss:[ebp+152],eax ; 分配的第2塊記憶體控制碼存入 ebp+152處
006AF0E5 56 push esi
006AF0E6 8B1E mov ebx,dword ptr ds:[esi]
006AF0E8 039D 22040000 add ebx,dword ptr ss:[ebp+422]
006AF0EE FFB5 56010000 push dword ptr ss:[ebp+156] ; 0x401000
006AF0F4 FF76 04 push dword ptr ds:[esi+4]
006AF0F7 50 push eax
006AF0F8 53 push ebx
006AF0F9 E8 6E050000 call DrvStudy.006AF66C ; 解壓縮代碼
006AF0FE B3 01 mov bl,1
006AF100 80FB 00 cmp bl,0
006AF103 75 5E jnz short DrvStudy.006AF163
006AF105 FE85 EC000000 inc byte ptr ss:[ebp+EC]
006AF10B 8B3E mov edi,dword ptr ds:[esi]
006AF10D 03BD 22040000 add edi,dword ptr ss:[ebp+422]
006AF113 FF37 push dword ptr ds:[edi] ; 保護0x401000處的代碼
006AF115 C607 C3 mov byte ptr ds:[edi],0C3 ; 把0x401000處的代碼改為RET
006AF118 FFD7 call edi ; 垃圾呼叫
006AF11A 8F07 pop dword ptr ds:[edi] ; 恢復0x401000處的代碼

F8單步到下面一點點, 來到
006AF12D 0BC9 or ecx,ecx
006AF12F 74 2E je short DrvStudy.006AF15F
006AF131 78 2C js short DrvStudy.006AF15F
006AF133 AC lods byte ptr ds:[esi]
006AF134 3C E8 cmp al,0E8
006AF136 74 0A je short DrvStudy.006AF142
006AF138 EB 00 jmp short DrvStudy.006AF13A
006AF13A 3C E9 cmp al,0E9
006AF13C 74 04 je short DrvStudy.006AF142
006AF13E 43 inc ebx
006AF13F 49 dec ecx
006AF140 ^ EB EB jmp short DrvStudy.006AF12D
006AF142 8B06 mov eax,dword ptr ds:[esi] ; 在這裏設置一個中斷點

這裏找opcode為e8或e9的地方, F9運行後斷下, ebx=1FE,
eax=[esi]的內容, 緊接著會比較esi指向的地方

006AF144 /EB 00 jmp short DrvStudy.006AF146
006AF146 \803E 36 cmp byte ptr ds:[esi],36 ; 比較第2個位元組的opcode是否為36
006AF149 ^ 75 F3 jnz short DrvStudy.006AF13E ; 是就不跳, 這裏為36不會跳
006AF14B 24 00 and al,0
006AF14D C1C0 18 rol eax,18 ; 解碼
006AF150 2BC3 sub eax,ebx ; 解碼
006AF152 8906 mov dword ptr ds:[esi],eax ; 解碼後的代碼存入esi指向的地方
006AF154 83C3 05 add ebx,5
006AF157 83C6 04 add esi,4
006AF15A 83E9 05 sub ecx,5
006AF15D ^ EB CE jmp short DrvStudy.006AF12D ; 向上跳

可以判斷出上面的這段代碼是解碼的, C30000處的代碼會解
碼, 在006AF15D的下一個指令設好中斷點, F9運行, 斷下

006AF15F 5B pop ebx ; 斷下, 解碼完畢
006AF160 5E pop esi
006AF161 59 pop ecx
006AF162 58 pop eax
006AF163 EB 08 jmp short DrvStudy.006AF16D ; 跳

006AF16D 8BC8 mov ecx,eax ; 來到這裏
006AF16F 8B3E mov edi,dword ptr ds:[esi] ; 代碼段偏移
006AF171 03BD 22040000 add edi,dword ptr ss:[ebp+422] ; 基底位址+偏移, 指向代碼段
006AF177 8BB5 52010000 mov esi,dword ptr ss:[ebp+152] ; 解碼後的正確代碼位址
006AF17D C1F9 02 sar ecx,2
006AF180 F3:A5 rep movs dword ptr es:[edi],dword >; 填充正確的代碼
006AF182 8BC8 mov ecx,eax
006AF184 83E1 03 and ecx,3
006AF187 F3:A4 rep movs byte ptr es:[edi],byte pt>; 填充正確的代碼
006AF189 5E pop esi
006AF18A 68 00800000 push 8000
006AF18F 6A 00 push 0
006AF191 FFB5 52010000 push dword ptr ss:[ebp+152]
006AF197 FF95 51050000 call dword ptr ss:[ebp+551] ; VirtualFree
006AF19D 83C6 08 add esi,8
006AF1A0 833E 00 cmp dword ptr ds:[esi],0 ; 解碼完成?沒完成就跳
006AF1A3 ^ 0F85 1EFFFFFF jnz DrvStudy.006AF0C7 ; 又跳到上面

因此可以確定這一大段006AF0C7 - 006AF1A3都是解碼的代
碼, 在006AF1A3處的下1條指令設置一個中斷點, F9運行, 斷下

006AF1A9 68 00800000 push 8000 ; 斷下
006AF1AE 6A 00 push 0
006AF1B0 FFB5 56010000 push dword ptr ss:[ebp+156]
006AF1B6 FF95 51050000 call dword ptr ss:[ebp+551] ; VirtualFree 釋放記憶體了

006AF1BC 8B9D 31050000 mov ebx,dword ptr ss:[ebp+531] ; ebx=0
006AF1C2 0BDB or ebx,ebx
006AF1C4 74 08 je short DrvStudy.006AF1CE ; ebx==0,跳
006AF1C6 8B03 mov eax,dword ptr ds:[ebx]
006AF1C8 8785 35050000 xchg dword ptr ss:[ebp+535],eax
006AF1CE 8B95 22040000 mov edx,dword ptr ss:[ebp+422] ; 跳到這裏, ebx=0x400000, 基底位址
006AF1D4 8B85 2D050000 mov eax,dword ptr ss:[ebp+52D] ; eax=0x400000
006AF1DA 2BD0 sub edx,eax
006AF1DC 74 79 je short DrvStudy.006AF257 ; ecx==0, 跳

下面就是恢復IAT了

006AF257 8B95 22040000 mov edx,dword ptr ss:[ebp+422] ; 跳到這裏, edx=0x400000, 基底位址
006AF25D 8BB5 41050000 mov esi,dword ptr ss:[ebp+541] ; esi=0
006AF263 0BF6 or esi,esi
006AF265 74 11 je short DrvStudy.006AF278 ; esi==0, 跳
006AF267 03F2 add esi,edx
006AF269 AD lods dword ptr ds:[esi]
006AF26A 0BC0 or eax,eax
006AF26C 74 0A je short DrvStudy.006AF278
006AF26E 03C2 add eax,edx
006AF270 8BF8 mov edi,eax
006AF272 66:AD lods word ptr ds:[esi]
006AF274 66:AB stos word ptr es:[edi]
006AF276 ^ EB F1 jmp short DrvStudy.006AF269
006AF278 BE 00001E00 mov esi,1E0000 ; 跳到這裏, esi=1e0000
006AF27D 8B95 22040000 mov edx,dword ptr ss:[ebp+422] ; edx=基底位址
006AF283 03F2 add esi,edx ; esi=51e0000
006AF285 8B46 0C mov eax,dword ptr ds:[esi+C] ; 指向下一個dll檔案名稱的RVA
006AF288 85C0 test eax,eax ; 不為空就不跳
006AF28A 0F84 0A010000 je DrvStudy.006AF39A
006AF290 03C2 add eax,edx ; 得到下一個dll檔案名稱
006AF292 8BD8 mov ebx,eax
006AF294 50 push eax
006AF295 FF95 4D0F0000 call dword ptr ss:[ebp+F4D] ; 取kernel32.dll的控制碼
006AF29B 85C0 test eax,eax ; 成功, 跳
006AF29D 75 07 jnz short DrvStudy.006AF2A6
006AF29F 53 push ebx
006AF2A0 FF95 510F0000 call dword ptr ss:[ebp+F51]
006AF2A6 8985 45050000 mov dword ptr ss:[ebp+545],eax ; 跳到這裏, kernel32.dll控制碼存入ebp+545
006AF2AC C785 49050000 00000000 mov dword ptr ss:[ebp+549],0 ; [ebp+549]=0
006AF2B6 8B95 22040000 mov edx,dword ptr ss:[ebp+422] ; edx=基底位址
006AF2BC 8B06 mov eax,dword ptr ds:[esi] ; eax=0
006AF2BE 85C0 test eax,eax
006AF2C0 75 03 jnz short DrvStudy.006AF2C5 ; eax==0, 不跳
006AF2C2 8B46 10 mov eax,dword ptr ds:[esi+10] ; IAT的FirThunk基底位址
006AF2C5 03C2 add eax,edx
006AF2C7 0385 49050000 add eax,dword ptr ss:[ebp+549] ; 指向下1個API
006AF2CD 8B18 mov ebx,dword ptr ds:[eax]
006AF2CF 8B7E 10 mov edi,dword ptr ds:[esi+10]
006AF2D2 03FA add edi,edx
006AF2D4 03BD 49050000 add edi,dword ptr ss:[ebp+549]
006AF2DA 85DB test ebx,ebx
006AF2DC 0F84 A2000000 je DrvStudy.006AF384
006AF2E2 F7C3 00000080 test ebx,80000000
006AF2E8 75 04 jnz short DrvStudy.006AF2EE
006AF2EA 03DA add ebx,edx
006AF2EC 43 inc ebx ; 取函數名稱
006AF2ED 43 inc ebx ; 取函數名稱
006AF2EE 53 push ebx
006AF2EF 81E3 FFFFFF7F and ebx,7FFFFFFF
006AF2F5 53 push ebx ; 函數名稱入疊
006AF2F6 FFB5 45050000 push dword ptr ss:[ebp+545] ; kernel32.dll控制碼入疊
006AF2FC FF95 490F0000 call dword ptr ss:[ebp+F49] ; GetProcAddress
006AF302 85C0 test eax,eax
006AF304 5B pop ebx
006AF305 75 6F jnz short DrvStudy.006AF376 ; 跳

取API函數位址成功後跳到下面的地方
006AF376 8907 mov dword ptr ds:[edi],eax ; API位址存入edi指向為址
006AF378 8385 49050000 04 add dword ptr ss:[ebp+549],4 ; 取下一個API
006AF37F ^ E9 32FFFFFF jmp DrvStudy.006AF2B6 ; 跳
006AF384 8906 mov dword ptr ds:[esi],eax
006AF386 8946 0C mov dword ptr ds:[esi+C],eax
006AF389 8946 10 mov dword ptr ds:[esi+10],eax
006AF38C 83C6 14 add esi,14
006AF38F 8B95 22040000 mov edx,dword ptr ss:[ebp+422]
006AF395 ^ E9 EBFEFFFF jmp DrvStudy.006AF285 ; 跳去恢復下個導入dll中的函數

這個大循環完成後IAT就恢復完成了...這個IAT是沒有被破壞的, 根據一般經驗, 殼在運行時都會有這麼一個大

循環來恢復IAT, 有的是破壞,

因此當在你一個迴圈中碰到 GetModuleHandleA, LoadLibraryA, GetProcAddress, 時就要小心了, 記下它, 有

可能ImportRec找不回API函數的時候, 需要手工修復IAT 時就要用到它

迴圈完後,來到這裏

006AF39A B8 08CF1C00 mov eax,1CCF08 ; OEP的RVA
006AF39F 50 push eax
006AF3A0 0385 22040000 add eax,dword ptr ss:[ebp+422] ; OEP值
006AF3A6 59 pop ecx
006AF3A7 0BC9 or ecx,ecx
006AF3A9 8985 A8030000 mov dword ptr ss:[ebp+3A8],eax ; 修改[ebp+3A8]處的代碼
006AF3AF 61 popad
006AF3B0 75 08 jnz short DrvStudy.006AF3BA ; 跳
006AF3B2 B8 01000000 mov eax,1
006AF3B7 C2 0C00 retn 0C
006AF3BA 68 08CF5C00 push DrvStudy.005CCF08 ; 變形跳轉
006AF3BF C3 retn
;  飛向光明之顛

這裏就是典型的Delphi程式的OEP處代碼

005CCF08 55 push ebp ; 停在這裏
005CCF09 8BEC mov ebp,esp
005CCF0B 83C4 F0 add esp,-10
005CCF0E 53 push ebx
005CCF0F B8 80CA5C00 mov eax,DrvStudy.005CCA80
005CCF14 E8 B3A7E3FF call DrvStudy.004076CC

現在就可在005CCF08處Dump程式了, 用OD的OllyDump插件(套件or外掛), Dump出程式, 啟動ImportREC, 填入

OEP的RVA值先點IATAutoSearch, 再點GetImport, 找到的API全部有效, 點Fix Dump, 修復IAT指標

運行程式, 提示系統資料錯誤, 這是因為程式有自校驗, 發現自己被脫殼後拒絕運行, 去除程式的自校驗我就

不說了...用暴破的方法就能搞定...

【總 結】

本文講述了脫殼的一般方法, 新手面對末知殼先不要怕, 自己試著跟蹤分析一下, 多做練習才會有進步

----------------------------------------------

繁體化 : djpvd (King of Holan)

註:花指令----> SEH干擾指令

TOP

發新話題

本站所有圖文均屬網友發表,僅代表作者的觀點與本站無關,如有侵權請通知版主會盡快刪除。