这是很早写的,可能对于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 EBP, ESP
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 EAX, DWORD PTR [EAX]
看到上面有连续的两个Call,第一个可以用Next走过,第二个用Step进去,里面会有27个SEH,在不同的
SEH中会处理代码的解密,IAT的获取和加密。执行代码,到每个SEH处理程序如下:
77FA0338 > 8B4C24 04 MOV ECX, DWORD PTR [ESP+4]
77FA033C 8B1C24 MOV EBX, DWORD PTR [ESP]
77FA033F 51 PUSH ECX
77FA0340 53 PUSH EBX
77FA0341 E8 4BF0FEFF CALL 77F8F391
77FA0346 0AC0 OR AL, AL
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 EDX, DWORD 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 AL, BYTE PTR [ESI]
00E624E0 8845 FF MOV BYTE PTR [EBP-1], AL
00E624E3 46 INC ESI
00E624E4 33C9 XOR ECX, ECX
00E624E6 8A4D FF MOV CL, BYTE PTR [EBP-1]
00E624E9 8D85 FFFEFFFF LEA EAX, DWORD PTR [EBP-101]
00E624EF 8BD6 MOV EDX, ESI
00E624F1 E8 DA2CFFFF CALL 00E551D0
00E624F6 6A 0A PUSH 0A
00E624F8 B9 446DE600 MOV ECX, 0E66D44
00E624FD 33D2 XOR EDX, EDX
00E624FF 8A55 FF MOV DL, BYTE PTR [EBP-1]
00E62502 8D85 FFFEFFFF LEA EAX, DWORD PTR [EBP-101]
00E62508 E8 13E8FFFF CALL 00E60D20
00E6250D 8DB5 FFFEFFFF LEA ESI, DWORD PTR [EBP-101]
00E62513 EB 02 JMP SHORT 00E62517
00E62515 8B36 MOV ESI, DWORD PTR [ESI]
00E62517 83FB 04 CMP EBX, 4
00E6251A 75 1D JNZ SHORT 00E62539
00E6251C 56 PUSH ESI
00E6251D 8B45 0C MOV EAX, DWORD PTR [EBP+C]
00E62520 50 PUSH EAX kernel32.GetCurrentThreadId
00E62521 E8 EAFBFFFF CALL 00E62110 调用GetProcAddress
00E62526 8B15 3855E600 MOV EDX, DWORD PTR [E65538] ***************************
00E6252C 8902 MOV DWORD PTR [EDX], EAX kernel32.GetCurrentThreadId
00E6252E B8 E80DE600 MOV EAX, 0E60DE8
00E62533 8B17 MOV EDX, DWORD 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 ECX, BYTE PTR [ESI]
00E6272B 41 INC ECX
00E6272C EB 05 JMP SHORT 00E62733
00E6272E B9 04000000 MOV ECX, 4
00E62733 01CE ADD ESI, ECX
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