• 关于徒手脱壳的几种方法


    首先我们先来说说壳的原理吧,简单说下就好,带壳程序运行以后,都会做哪些事情呢? (想要了解更多的朋友们就去读看雪大哥的《加密与解密(第三版)》吧)

    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  012FFF0
    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

    以上就是我掌握的徒手脱壳的方法了,不是每个方法都试用所有的壳,怎么说呢,挨个试试吧。

    当然也可以脱壳机或者用一堆脚本,不过这篇文章的主题是‘徒手’脱壳,就列出这几种方法,如果有什么不对的,还请指正

  • 相关阅读:
    typescript泛型
    安卓手机IPhone抓包Https
    js里面for循环的++i与i++
    http请求头
    从浏览器地址栏输入url到页面呈现
    docker部署nodejs应用
    mac下使用MongoDB
    使用vue-cli3&vue ui图形化界面创建项目
    javascript中的call, apply(转载)
    跨域请求
  • 原文地址:https://www.cnblogs.com/hijacklinux/p/7052423.html
Copyright © 2020-2023  润新知