寫給新手 - 淺談脫殼方法

看雪資料發表於2004-12-18

【標  題】寫給新手 - 淺談脫殼方法
【作  者】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 RETCALL 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外掛, Dump出程式, 啟動ImportREC, 填入OEP的RVA值
先點IATAutoSearch, 再點GetImport, 找到的API全部有效, 點Fix Dump, 修復IAT指標

執行程式, 提示系統資料錯誤, 這是因為程式有自校驗, 發現自己被脫殼後拒絕執行, 去除程式的自校驗我就不說了...用爆破
的方法就能搞定...

【總    結】

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

相關文章