關於徒手脫殼的幾種方法


首先我們先來說說殼的原理吧,簡單說下就好,帶殼程序運行以后,都會做哪些事情呢? (想要了解更多的朋友們就去讀看雪大哥的《加密與解密(第三版)》吧)

1、保存現場(pushad/popad,pushfd/popfd)>>2、獲取殼自己需要的API地址>>3、解密原程序各個區塊>>4、IAT的初始化>>5、重定位>>6、Hook-API>>7、跳到 OEP

 

首先我們先用DIE來查一下帶殼程序是什么語言編寫的,然后再用OD載入。

在脫殼之前呢,我們一定要知道各個語言的OEP特征是什么,免得到時候就算到了OEP,自己都不知道,那可就悲劇了,這里列出各個語言的OEP(請忽略地址):

 

VC++

1
2
3
4
5
6
7
8
9
00496EB8 >/$  55            PUSH EBP                        
00496EB9  |.  8BEC          MOV EBP,ESP
00496EBB  |.  6A FF         PUSH -1
00496EBD  |.  68 40375600   PUSH Screensh.00563740
00496EC2  |.  68 8CC74900   PUSH Screensh.0049C78C              
00496EC7  |.  64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
00496ECD  |.  50            PUSH EAX
00496ECE  |.  64:8925 00000>MOV DWORD PTR FS:[0],ESP
00496ED5  |.  83EC 58       SUB ESP,58

VB:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
00401166  - FF25 6C104000   JMP DWORD PTR DS:[<&MSVBVM60. #100>]      ; MSVBVM60.ThunRTMain
0040116C >  68 147C4000     PUSH PACKME.00407C14                     ;這是第一種入口格式
00401171    E8 F0FFFFFF     CALL <JMP.&MSVBVM60. #100>
00401176    0000            ADD BYTE PTR DS:[EAX],AL
00401178    0000            ADD BYTE PTR DS:[EAX],AL
0040117A    0000            ADD BYTE PTR DS:[EAX],AL
0040117C    3000            XOR BYTE PTR DS:[EAX],AL
 
00401FBC >  68 D0D44000        push dumped_.0040D4D0
00401FC1    E8 EEFFFFFF        call <jmp.&msvbvm60.ThunRTMain>        ;這是第二種入口格式
00401FC6    0000               add byte ptr ds:[eax],al
00401FC8    0000               add byte ptr ds:[eax],al
00401FCA    0000               add byte ptr ds:[eax],al
00401FCC    3000               xor byte ptr ds:[eax],al
00401FCE    0000               add byte ptr ds:[eax],al

BC++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0040163C > $  /EB  10         JMP SHORT BCLOCK.0040164E
0040163E     |66            DB 66                                    ;  CHAR  'f'
0040163F     |62            DB 62                                    ;  CHAR  'b'
00401640     |3A            DB 3A                                    ;  CHAR  ':'
00401641     |43            DB 43                                    ;  CHAR  'C'
00401642     |2B            DB 2B                                    ;  CHAR  '+'
00401643     |2B            DB 2B                                    ;  CHAR  '+'
00401644     |48            DB 48                                    ;  CHAR  'H'
00401645     |4F            DB 4F                                    ;  CHAR  'O'
00401646     |4F            DB 4F                                    ;  CHAR  'O'
00401647     |4B            DB 4B                                    ;  CHAR  'K'
00401648     |90            NOP
00401649     |E9            DB E9
0040164A   . |98E04E00      DD OFFSET BCLOCK.___CPPdebugHook
0040164E   > \A1 8BE04E00   MOV EAX,DWORD PTR DS:[4EE08B]
00401653   .  C1E0 02       SHL EAX,2
00401656   .  A3 8FE04E00   MOV DWORD PTR DS:[4EE08F],EAX
0040165B   .  52            PUSH EDX
0040165C   .  6A 00         PUSH 0                                   ;  /pModule  = NULL
0040165E   .  E8 DFBC0E00   CALL <JMP.&KERNEL32.GetModuleHandleA>    ; \GetModuleHandleA
00401663   .  8BD0          MOV EDX,EAX

Delphi:

1
2
3
4
5
6
7
8
9
10
00509CB0 > $  55            PUSH EBP
00509CB1   .  8BEC          MOV EBP,ESP
00509CB3   .  83C4 EC       ADD ESP,-14
00509CB6   .  53            PUSH EBX
00509CB7   .  56            PUSH ESI
00509CB8   .  57            PUSH EDI
00509CB9   .  33C0          XOR EAX,EAX
00509CBB   .  8945 EC       MOV DWORD PTR SS:[EBP-14],EAX
00509CBE   .  B8 20975000   MOV EAX,unpack.00509720
00509CC3   .  E8 84CCEFFF   CALL unpack.0040694C

易語言:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
格式一:
00401000 >  E8 06000000     call dump_.0040100B
00401005    50              push eax
00401006    E8 BB010000     call <jmp.&KERNEL32.ExitProcess>
0040100B    55              push ebp
0040100C    8BEC            mov ebp,esp
0040100E    81C4 F0FEFFFF   add esp,-110
00401014    E9 83000000     jmp dump_.0040109C
00401019    6B72 6E 6C      imul esi,dword ptr ds:[edx+6E],6C
0040101D    6E              outs dx,byte ptr es:[edi]
 
格式二
00403831 >/$  55            PUSH EBP
00403832  |.  8BEC          MOV EBP,ESP
00403834  |.  6A FF         PUSH -1
00403836  |.  68 F0624000   PUSH Nisy521.004062F0
0040383B  |.  68 A44C4000   PUSH Nisy521.00404CA4                   
00403840  |.  64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
00403846  |.  50            PUSH EAX
00403847  |.  64:8925 00000>MOV DWORD PTR FS:[0],ESP
 
5.0以后獨立編譯的入口:
 
0045C12D >/$  55            push ebp
0045C12E  |.  8BEC          mov ebp,esp
0045C130  |.  6A FF         push -0x1
0045C132  |.  68 087D4B00   push notebook.004B7D08
0045C137  |.  68 14E94500   push notebook.0045E914                      
0045C13C  |.  64:A1 0000000>mov eax,dword ptr fs:[0]
0045C142  |.  50            push eax
0045C143  |.  64:8925 00000>mov dword ptr fs:[0],esp
0045C14A  |.  83EC 58       sub esp,0x58
0045C14D  |.  53            push ebx
0045C14E  |.  56            push esi
0045C14F  |.  57            push edi
0045C150  |.  8965 E8       mov [ local .6],esp
0045C153  |.  FF15 A4C14700 call dword ptr ds:[<&KERNEL32.GetVersion>;  kernel32.GetVersion
0045C159  |.  33D2          xor edx,edx
0045C15B  |.  8AD4          mov dl,ah
0045C15D  |.  8915 BCB44E00 mov dword ptr ds:[0x4EB4BC],edx
0045C163  |.  8BC8          mov ecx,eax
0045C165  |.  81E1 FF000000 and ecx,0xFF
0045C16B  |.  890D B8B44E00 mov dword ptr ds:[0x4EB4B8],ecx
0045C171  |.  C1E1 08       shl ecx,0x8
0045C174  |.  03CA          add ecx,edx
0045C176  |.  890D B4B44E00 mov dword ptr ds:[0x4EB4B4],ecx
0045C17C  |.  C1E8 10       shr eax,0x10
0045C17F  |.  A3 B0B44E00   mov dword ptr ds:[0x4EB4B0],eax
0045C184  |.  6A 01         push 0x1
0045C186  |.  E8 354B0000   call notebook.00460CC0

MASM32 / TASM32入口:

1
2
3
4
5
6
7
8
9
00401258 >/$  6A 00         push 0                                   ;  /pModule  = NULL
0040125A  |.  E8 47000000   call <jmp.&kernel32.GetModuleHandleA>    ; \GetModuleHandleA
0040125F  |.  A3 00304000   mov dword ptr ds:[403000],eax
00401264  |.  6A 00         push 0                                   ;  /lParam  = NULL
00401266  |.  68 DF104000   push dump.004010DF                       ; |DlgProc = dump.004010DF
0040126B  |.  6A 00         push 0                                   ; |hOwner = NULL
0040126D  |.  6A 65         push 65                                  ; |pTemplate = 65
0040126F  |.  FF35 00304000 push dword ptr ds:[403000]               ; |hInst = NULL
00401275  |.  E8 56000000   call <jmp.&user32.DialogBoxParamA>       ; \DialogBoxParamA

VC8入口:

1
2
3
4
5
6
7
8
9
00401258 >/$  6A 00         push 0                                   ;  /pModule  = NULL
0040125A  |.  E8 47000000   call <jmp.&kernel32.GetModuleHandleA>    ; \GetModuleHandleA
0040125F  |.  A3 00304000   mov dword ptr ds:[403000],eax
00401264  |.  6A 00         push 0                                   ;  /lParam  = NULL
00401266  |.  68 DF104000   push dump.004010DF                       ; |DlgProc = dump.004010DF
0040126B  |.  6A 00         push 0                                   ; |hOwner = NULL
0040126D  |.  6A 65         push 65                                  ; |pTemplate = 65
0040126F  |.  FF35 00304000 push dword ptr ds:[403000]               ; |hInst = NULL
00401275  |.  E8 56000000   call <jmp.&user32.DialogBoxParamA>       ; \DialogBoxParamA

 

脫殼方法:

知道了入口我們就開始脫殼吧!

脫殼方法一:單步跟蹤,其實就是f8,f7配合啦

需要注意的幾點:當你遇到近Call的時候需要用f7跟進;當你遇到遠Call的時候,用f8跳過就行;當遇到循環的時候,直接用f4跳出;當遇到大的跳轉就要注意了,很快就到OEP了

我們拿《加密與解密》的RebPE.exe舉栗子,die查VC++寫的,來看看吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
00413000 >  60              pushad                                   ; 剛進來
00413001    E8 C2000000     call RebPE.004130C8                      ; 這call很近吧,我們f7進去
================================================================================================進來了
004130C8    5D              pop ebp                                  ; RebPE.00413006
004130C9    81ED 06000000   sub ebp,0x6                              ; 進來以后就f8跟吧
004130CF    8B85 C0000000   mov eax,dword ptr ss:[ebp+0xC0]          
004130D5    0BC0            or eax,eax
=================================================================================================用我單身狗多年練就的手速開始瘋狂f8
00150028    8B06            mov eax,dword ptr ds:[esi]               ; kernel32.GetProcAddress
0015002A    8907            mov dword ptr ds:[edi],eax               ; kernel32.GetProcAddress
0015002C    83C6 04         add esi,0x4
0015002F    83C7 04         add edi,0x4
00150032  ^ E2 F4           loopd short 00150028                     ; 看到這里,這是個循環,我們直接下一條選中f4跳出循環
00150034    8D85 37010000   lea eax,dword ptr ss:[ebp+0x137]
0015003A    8982 55030000   mov dword ptr ds:[edx+0x355],eax         ; kernel32.GetProcAddress
=================================================================================================
001500DE    68 00800000     push 0x8000
001500E3    6A 00           push 0x0
001500E5    56              push esi
001500E6    FF95 5D030000   call dword ptr ss:[ebp+0x35D]            ; kernel32.VirtualFree
001500EC    5B              pop ebx                                  ; ntdll.7C930460
001500ED    83C3 0C         add ebx,0xC
001500F0  ^ EB B3           jmp short 001500A5
001500F2    8B85 8D020000   mov eax,dword ptr ss:[ebp+0x28D]         ; 上面jmp又是個向上跳轉,在這條f4跳出
001500F8    0BC0            or eax,eax
=================================================================================================
00150270    8B85 89020000   mov eax,dword ptr ss:[ebp+0x289]
00150276    0385 51030000   add eax,dword ptr ss:[ebp+0x351]         ; RebPE.00400000
0015027C    0185 84020000   add dword ptr ss:[ebp+0x284],eax         ; RebPE.00401130
00150282    61              popad                                    ; 注意這里有個popad,看到他就離OEP不遠了,因為popad是還原現場用的
00150283    68 00000000     push 0x0
00150288    C3              retn
================================================================================================
00401130      55            db 55                                    ;  來到了這里,用ctrl+A分析下
00401131      8B            db 8B
00401132      EC            db EC
00401133      6A            db 6A                                    ;  CHAR  'j'
00401134      FF            db FF
00401135      68            db 68                                    ;  CHAR  'h'
================================================================================================分析好了,對比下上面的入口,正好就是OEP了
00401130  /.  55            push ebp                                 ;  來到了這里,用ctrl-A分析下
00401131  |.  8BEC          mov ebp,esp
00401133  |.  6A FF         push -0x1
00401135  |.  68 B8504000   push RebPE.004050B8
0040113A  |.  68 FC1D4000   push RebPE.00401DFC                      ;  SE 處理程序安裝
0040113F  |.  64:A1 0000000>mov eax,dword ptr fs:[0]

 

脫殼方法二:最后一次異常法

1
2
3
4
5
1、在od的調試選項把異常的那個對話框所有異常的勾選全部去掉,ctrl+f2重新載入
2、反復按 shift +f9,記錄你按下的次數n
3、重新載入,這次按n-1次 shift +f9
4、看堆棧,有個se處理程序,反匯編跟隨
5、單步跟蹤,直到有大跳轉

 

脫殼方法三:兩次斷點法(內存鏡像法):外殼會先解壓各個區段,然后再跳回代碼段執行,根據這個原理:

1
2
3
4
1、在od的調試選項把異常的那個對話框所有異常的勾選全部選中
2、alt+m查看內存,然后再在資源段下一次f2斷點,f9斷下(目的是斷下后確定殼解壓處理了text段)
3、接下來再在text段下一次f2斷點,f9斷下(此時就說明開始訪問text段了)
4、單步跟蹤,直到大跳轉

 

脫殼方法四:ESP定律:外殼在開始的時候一定要保存環境(例如pushad),結束的時候還原環境(例如popad),最重要的是堆棧一定要平衡,這樣,當執行pushad后,我們就可以在ESP下斷點:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
EAX 00000000                                              ;這是執行pushad后各個寄存器的值
ECX 0012FFB0
EDX 7C92E514 ntdll.KiFastSystemCallRet
EBX 7FFD5000
ESP 0012FFA4                                              ;我們在0012fff0處下個硬件斷點,然后運行
EBP 0012FFF0
ESI 7C9A0620 ntdll.7C9A0620
EDI 7C930460 ntdll.7C930460
EIP 00413001 RebPE.00413001
================================================================================================
00150283    68 30114000     push 0x401130                 ;斷在這里了,我們單步跟蹤
00150288    C3              retn
00150289    3011            xor byte ptr ds:[ecx],dl
0015028B    0000            add byte ptr ds:[eax],al
0015028D    0100            add dword ptr ds:[eax],eax
================================================================================================到達OEP
00401130    55              push ebp
00401131    8BEC            mov ebp,esp
00401133    6A FF           push -0x1
00401135    68 B8504000     push RebPE.004050B8
0040113A    68 FC1D4000     push RebPE.00401DFC
0040113F    64:A1 00000000  mov eax,dword ptr fs:[0]
00401145    50              push eax

脫殼方法五:直接搜索popad法

這方法原理特別簡單,既然popad是還原環境,那么直接搜索ctrl+f搜索popad,然后下f2斷點,運行斷下就好了。

當然,這種方法有很大的局限性,只適合UPX,ASPACK等少量殼。

 

脫殼方法六:模擬跟蹤法

tc的意思:Trace in till condition 跟蹤進入直到條件滿足

eip是當前指令指針

1
2
3
4
5
6
7
8
9
10
11
12
13
1、在od的調試選項把異常的那個對話框所有異常的勾選全部選中
2、alt+m查看內存,然后再在資源段下一次f2斷點,f9斷下(目的是斷下后確定殼解壓處理了text段)
3、接下來再在text段下一次f2斷點,f9斷下(此時就說明開始訪問text段了)
================================================================================================ 以上方法就是兩次斷點,不同的是下面不再單步跟蹤了,而是模擬跟蹤
alt+m去看下內存,里面在程序領空會有一個sfx 輸入表,像這樣:
00400000   00001000   RebPE                 PE 文件頭                
00401000   00004000   RebPE      .text      代碼                    
00405000   00001000   RebPE      .rdata     數據                   
00406000   00003000   RebPE      .data                           
00409000   0000A000   RebPE      .rsrc      資源                      
00413000   00007000   RebPE      .pediy     SFX,輸入表            ;我們記住這個地址00413000
================================================================================================   
之后我們在 command 命令行插件中輸入tc eip<00413000 回車,看到左上角狀態變成了‘跟蹤’,接下來就是等程序自動跳到OEP了

 

脫殼方法七:sfx法,這方法就是太慢了

1
2
3
4
5
6
7
8
9
10
1、在od的調試選項把異常的那個對話框所有異常的勾選全部選中
2、在od的調試選項在SFX的那個選項卡選中 “字節方式跟蹤真正入口處(速度非常慢)”
3、重新載入,漫長的等待過后就到達OEP了
=================================================================================================等待過后
00401130  /.  55            push ebp                                 ;  SFX 代碼真正入口點
00401131  |.  8BEC          mov ebp,esp
00401133  |.  6A FF         push -0x1
00401135  |.  68 B8504000   push RebPE.004050B8
0040113A  |.  68 FC1D4000   push RebPE.00401DFC                      ;  SE 處理程序安裝
0040113F  |.  64:A1 0000000>mov eax,dword ptr fs:[0]

 

脫殼方法八:一步到達法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
1、od載入后運行
2、我們去堆棧的最下面,開始向上找,找第一個程序領空地址的
=================================================================================================
0012FFA4  |FFFFFFFF
0012FFA8  |0012FF4C
0012FFAC  |0012FFF0
0012FFB0  |0012FFE0  指向下一個 SEH 記錄的指針
0012FFB4  |00401DFC  SE處理程序
0012FFB8  |004050B8  RebPE.004050B8                          ;就是這個,RebPE是程序名,注意左邊有個框把這段給框起來了,說明這是一個函數堆棧
0012FFBC  |00000000
0012FFC0  \0012FFF0
0012FFC4   7C816037  返回到 kernel32.7C816037
0012FFC8   7C930460  返回到 ntdll.7C930460
0012FFCC   7C9A0620  ntdll.7C9A0620
=================================================================================================我們繼續向上翻,找這個框的開頭
 
0012FF14  |00000000
0012FF18  |7FFDE000
           \
向下找,‘返回到’的,這個例子有3個
0012FF1C   /0012FFC0                                                       ;這個就是函數的頭部了
0012FF20  |0040101B  返回到 RebPE.0040101B 來自 user32.DialogBoxParamA    ;這個肯定不是,user32.DialogBoxParamA
0012FF24  |00400000  RebPE.00400000
0012FF28  |00000065
0012FF2C  |00000000
0012FF30  |00401080  RebPE.00401080
0012FF38  |004011FE  返回到 RebPE.004011FE 來自 RebPE.00401000            ;排除第一個和第三個,也只剩下這個了,我們右鍵反匯編跟隨
0012FF3C  |00400000  RebPE.00400000
0012FF40  |00000000
0012FF44  |002823C8
0012FF48  |0000000A
0012FF4C  |7C930460  返回到 ntdll.7C930460                                ;這個也肯定不是,都跑系統領空去了
0012FF50  |7C9A0620  ntdll.7C9A0620
0012FF54  |7FFDD000
=================================================================================================反匯編跟隨后,向上找斷首,就是OEP了
00401130  /.  55            push ebp
00401131  |.  8BEC          mov ebp,esp
00401133  |.  6A FF         push -0x1
00401135  |.  68 B8504000   push RebPE.004050B8
0040113A  |.  68 FC1D4000   push RebPE.00401DFC                      ;  SE 處理程序安裝
0040113F  |.  64:A1 0000000>mov eax,dword ptr fs:[0]
00401145  |.  50            push eax
00401146  |.  64:8925 00000>mov dword ptr fs:[0],esp
0040114D  |.  83EC 58       sub esp,0x58
00401150  |.  53            push eb

 

以上就是我掌握的徒手脫殼的方法了,不是每個方法都試用所有的殼,怎么說呢,挨個試試吧。

當然也可以脫殼機或者用一堆腳本,不過這篇文章的主題是‘徒手’脫殼,就列出這幾種方法,如果有什么不對的,還請指正


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM