• 标 题:如何进行asprotect 1.2?的手工脱壳 (7千字)
  • 作 者:slangmgh
  • 时 间:2003-08-19 12:35:16
  • 链 接:http://bbs.pediy.com


这是很早写的,可能对于asprotect 1.23不适用。
手工Asprotect 1.2?脱壳

0.  特征
装载程序,在GetProcAddress上设置断点,中断后,第一次看到程序进行Import的处理(但实际不是)。
处理完成后,会执行到以下代码:

00E6B5C1    61              POPAD
00E6B5C2    75 08           JNZ     SHORT 00E6B5CC
00E6B5C4    B8 01000000     MOV     EAX, 1
00E6B5C9    C2 0C00         RETN    0C
00E6B5CC    68 00000000     PUSH    0
00E6B5D1    C3              RETN

从这段代码返回后,到达如下代码:
00E64574    55              PUSH    EBP                               ElAnyCal.005193E1
00E64575    8BEC            MOV     EBPESP
00E64577    83C4 B4         ADD     ESP, -4C
00E6457A    B8 5C44E600     MOV     EAX, 0E6445C
00E6457F    E8 940AFFFF     CALL    00E55018   Next 
00E64584    E8 87EDFEFF     CALL    00E53310   Step进去
00E64589    8D40 00         LEA     EAXDWORD PTR [EAX]

看到上面有连续的两个Call,第一个可以用Next走过,第二个用Step进去,里面会有27个SEH,在不同的
SEH中会处理代码的解密,IAT的获取和加密。执行代码,到每个SEH处理程序如下:
77FA0338 >  8B4C24 04       MOV     ECXDWORD PTR [ESP+4]
77FA033C    8B1C24          MOV     EBXDWORD PTR [ESP]
77FA033F    51              PUSH    ECX
77FA0340    53              PUSH    EBX
77FA0341    E8 4BF0FEFF     CALL    77F8F391
77FA0346    0AC0            OR      ALAL
77FA0348    74 0C           JE      SHORT 77FA0356
77FA034A    5B              POP     EBX
77FA034B    59              POP     ECX
77FA034C    6A 00           PUSH    0
77FA034E    51              PUSH    ECX
77FA034F    E8 1E25FEFF     CALL    ZwContinue ; 可设断点
77FA0354    EB 0B           JMP     SHORT 77FA0361
77FA0356    5B              POP     EBX
77FA0357    59              POP     ECX
77FA0358    6A 00           PUSH    0
77FA035A    51              PUSH    ECX
77FA035B    53              PUSH    EBX
77FA035C    E8 F8F2FEFF     CALL    ZwRaiseException
77FA0361    83C4 EC         ADD     ESP, -14
77FA0364    890424          MOV     DWORD PTR [ESP], EAX
77FA0367    C74424 04 01000>MOV     DWORD PTR [ESP+4], 1
77FA036F    895C24 08       MOV     DWORD PTR [ESP+8], EBX
77FA0373    C74424 10 00000>MOV     DWORD PTR [ESP+10], 0
77FA037B    54              PUSH    ESP
77FA037C    E8 970B0100     CALL    RtlRaiseException
77FA0381    C2 0800         RETN    8
这段代码将被执行27次。可以在Call ZwContinue一行上设置断点,以便观察。到最后一次,用Ollydbg可以
在CPU窗口中的寄存器窗口看到浮点寄存器ST7右边变红值为32.000000000000000000。在每次SEH处理的时候,
程序会把Dr0-Dr3清掉,所以设置硬中断是没有用的,只能设置软中断,除非用SuperBPM。

注:在程序执行到 77FA034F ZwContinue的时候,这时在Ring3状态下不能进行继续往下跟踪,但是可以查看
当前的堆栈,从当前的ESP往下数,第五个长整数(即[ESP+14H])就是产生例外的EIP,可以在该地址的后一
条指令下断点。

1.  OEP
使用loader.exe、Getoep查找EOP。对Delphi程序,可以Dump,找“runtime”,往回找“55 8B EC”即可。

2.  在OEP上设置断点,并Dump
在NT下,由于无法使用SuperBPM,所以只能在最后一次SEH的时候,在OEP上设置软中断,程序能中断。中断
后,使用LordPe或者ProcDump导出程序的映像。

3.  IAT
如果不对Import进行处理,在第二步的时候用ImportREC重建IAT,程序有时可用,但总会出错。因为Asprotect
对Import进行了另外的处理。不过可以手工进行调整一下。方法是,在第一次到达SEH的时候,设置GetProcAddress
的断点(注意,需要在GetProcAddress的实际处理代码中,而不是开始的7各字节上,对其他的函数也有同样
的问题,因为Asprotect是跳过这些代码的前序代码,直接跳转到代码中间的)。等到被中断后(大致在第20
个SEH上),可以看到以下代码:
00E624C3    50              PUSH    EAX                               kernel32.GetCurrentThreadId
00E624C4    E8 47FCFFFF     CALL    00E62110   调用GetProcAddress
00E624C9    E8 7EFEFFFF     CALL    00E6234C ***   对返回值进行加密处理
00E624CE    8B17            MOV     EDXDWORD PTR [EDI]              ElAnyCal.004E117C
00E624D0    8902            MOV     DWORD PTR [EDX], EAX              kernel32.GetCurrentThreadId
00E624D2    EB 7E           JMP     SHORT 00E62552
00E624D4    83FB 01         CMP     EBX, 1
00E624D7    74 05           JE      SHORT 00E624DE
00E624D9    83FB 04         CMP     EBX, 4
00E624DC    75 37           JNZ     SHORT 00E62515
00E624DE    8A06            MOV     ALBYTE PTR [ESI]
00E624E0    8845 FF         MOV     BYTE PTR [EBP-1], AL
00E624E3    46              INC     ESI
00E624E4    33C9            XOR     ECXECX
00E624E6    8A4D FF         MOV     CLBYTE PTR [EBP-1]
00E624E9    8D85 FFFEFFFF   LEA     EAXDWORD PTR [EBP-101]
00E624EF    8BD6            MOV     EDXESI
00E624F1    E8 DA2CFFFF     CALL    00E551D0
00E624F6    6A 0A           PUSH    0A
00E624F8    B9 446DE600     MOV     ECX, 0E66D44
00E624FD    33D2            XOR     EDXEDX
00E624FF    8A55 FF         MOV     DLBYTE PTR [EBP-1]
00E62502    8D85 FFFEFFFF   LEA     EAXDWORD PTR [EBP-101]
00E62508    E8 13E8FFFF     CALL    00E60D20
00E6250D    8DB5 FFFEFFFF   LEA     ESIDWORD PTR [EBP-101]
00E62513    EB 02           JMP     SHORT 00E62517
00E62515    8B36            MOV     ESIDWORD PTR [ESI]
00E62517    83FB 04         CMP     EBX, 4
00E6251A    75 1D           JNZ     SHORT 00E62539
00E6251C    56              PUSH    ESI
00E6251D    8B45 0C         MOV     EAXDWORD PTR [EBP+C]
00E62520    50              PUSH    EAX                               kernel32.GetCurrentThreadId
00E62521    E8 EAFBFFFF     CALL    00E62110   调用GetProcAddress
00E62526    8B15 3855E600   MOV     EDXDWORD PTR [E65538]   ***************************
00E6252C    8902            MOV     DWORD PTR [EDX], EAX              kernel32.GetCurrentThreadId
00E6252E    B8 E80DE600     MOV     EAX, 0E60DE8
00E62533    8B17            MOV     EDXDWORD PTR [EDI]              ElAnyCal.004E117C
00E62535    8902            MOV     DWORD PTR [EDX], EAX              kernel32.GetCurrentThreadId
00E62537    EB 19           JMP     SHORT 00E62552

代码有可能停在E62526或者E624C9上。其中Call E6234C这条指令将对返回的地址进行额外的处理,因此在此将
该指令全部换成NOP。EDX指的是IAT的地址。代码返回后,到达:
00E62728    0FB60E          MOVZX   ECXBYTE PTR [ESI]
00E6272B    41              INC     ECX
00E6272C    EB 05           JMP     SHORT 00E62733
00E6272E    B9 04000000     MOV     ECX, 4
00E62733    01CE            ADD     ESIECX
00E62735    E8 0EFDFFFF     CALL    00E62448   从这里调用上述指令
00E6273A    5B              POP     EBX
00E6273B  ^ EB CE           JMP     SHORT 00E6270B
00E6273D    61              POPAD

可以在POPAD上设断点,中断后,就可以使用ImportREC重建IAT,其中会有一个函数ImportREC不能识别, 
是GetProcAddress。确定所有输入函数后,用Fix Dump即可。

4.  重建IAT后,程序一般还是不能用,因为重建后的程序将需要调用另外的一些代码(我不知Asprotect是如何实现)。
一般会有两段代码,第一段一般长度为1D000,第二段为8000或者C000。行简单,在第二步的时候,顺便使用LordPE
中的Dump Region导出这两段代码。然后使用LordPE将这两段贴到重建后的执行文件中(首先新建一个Section填补
到新代码的虚拟地址空间,然后使用Load Section From Disk,如果发现Section个数太多而不能添加新的Section,
可以查看前面有没有实际长度为0的段,可以并到该段的上面一个段,只需将前一个段的虚长度增加这个短的虚长度
即可)。
5.  如果在第三步重建IAT后的程序运行还有问题,可以在第三步使用ImportREC的Save Tree目录首先将IAT导出到一个
文件,然后重现运行程序,再运用ImportREC将IAT并入到可执行文件中即可。
6.  Asprotect用到的若干函数:
GetVersion 在Win2000上返回 0x08390005
GetCurrentProcess 返回0xFFFFFFFF
GetCurrentThread 返回0xFFFFFFFE
LockResource 返回自己
FreeResource 去除一个调用参数,功能返回版本号
GetCommandLineA 返回可执行文件名
GetVersionEx 用MOVSD将结果移动
GetCurrentProcessId
GetCurrentThreadId
GetModuleHandleA
GetProcAddress
DialogBoxIndirectParamA