ACProtect Professional 1.3C 主程序脱壳(2)
By
softworm
4. dump
根据脱US UnpackMe的经验,不能在false OEP处dump,此时BSS section中许多数据已经初始化了。最好在第一句(push ebp)就dump。可是那个push ebp离false OEP很远L。
Packer EP的初始化环境:



先看看发出第1个call的壳代码:

可以看到(跟一下可以证实),在call入原程序空间前,最后的异常是div 0。用现在的OllyDbg脚本(跟到737B63),停下后修改异常拦截选项:

试试能否拦截异常找到合适的dump位置。当前OllyScript脚本停下时的环境:

栈中为pushad的结果。
第7次div 0异常时:


注意ebp的值已经入栈了。Pushad的结果,对应寄存器值为:
esp = 12FFC0
(原来为12FFC4)
ebp =
12FFC0 = esp (原来为12FFF0)
所以,在这里已经执行过了:
Push
ebp Mov
ebp,esp
看看BSS section:


全为0,就在这里dump。如果需要仔细跟stolen code,也可以从这里入手(可以从脚本停下的第6次div 0异常处理后开始)。
先用4D9DE4为OEP。

用LoadPE查看节表:

先把CODE的Vsize和Rsize加大,以避免修复stolen codes时空间不够。

重新跟到false OEP(4D9E37)。重建输入表。

用了Add new section,会不会有问题(好象有篇脱文里提到过这个)? 先这样处理。
用IDA编译dumped_.exe,结果不错。
现在剩下stolen code和replaced code。
5. 修复stolen code
既然不打算仔细跟,就只有猜了。对执行第1个call以前的stolen code进行猜测,应该是安全的。后面的4次call需要仔细看看。
1) call 406EDC
在IDA中看dumped_.exe的结果:

先执行脚本,停下后只勾选div 0异常。直到73A9AF。经过一番遮遮掩掩的代码:

在call前的寄存器:

堆栈:

call完后,下面的pushad为分界线,标识stolen code的结束。所以到这里的stolen code为(对于不确定的,可以在Packer EP修改寄存器值,到这里核对):
Push
ebp Mov
ebp,esp Push
edx Push
edi Push
ebx Push
eax Mov
eax,4D9BCC mov
eax,dword ptr ds:[503B94]
call 406EDC
Call完后,pushad前的环境:


2) call 46261C


到这里的stolen code:
Push
ebp Mov
ebp,esp Push
edx Push
edi Push
ebx Push
eax Mov
eax,4D9BCC mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] call
46261C push
edi mov
ecx,dword ptr ds:[503938]
call 406EDC
从这里开始,不能图省事了。要追出全部的stolen code,必须跟(必须确保上一次pushad和下一次popad时的环境一致,才不会丢代码)。先试直接拦截div 0异常,注意后面是否有popad。如果两次的环境不一致,则必须单步跟L。
另外,追stolen code不是一次完成的,所以有些图中寄存器和堆栈中的数据对不上,不必管它。
3) 0073BC47

Push
ebp Mov
ebp,esp Push
edx Push
edi Push
ebx Push
eax Mov
eax,4D9BCC mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] call
46261C //这里去掉前面的push edi mov
ecx,dword ptr ds:[503938] mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] push
eax push
ecx mov
ecx,47FCD8 //多余,前面push ecx,后面还会pop的吧? mov
eax,47FCD8
call 406EDC
4) 0073C558

Push
ebp Mov
ebp,esp Push
edx Push
edi Push
ebx Push
eax Mov
eax,4D9BCC mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] call
46261C mov
ecx,dword ptr ds:[503938] mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] mov
edx,dword ptr ds:[47FCD8] push
edx mov
edx, 462634 //注意这是下一次call的地址
call 406EDC
5) 第1次call 462634

这个函数有2个参数L。

Push
ebp Mov
ebp,esp Push
edx Push
edi Push
ebx Push
eax Mov
eax,4D9BCC mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] call
46261C mov
ecx,dword ptr ds:[503938] mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] mov
edx,dword ptr ds:[47FCD8] call
462634 push
eax push
esi mov
eax, 50385C
call 406EDC
6) 第2次call 462634

Push
ebp Mov
ebp,esp Push
edx Push
edi Push
ebx Push
eax Mov
eax,4D9BCC mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] call
46261C mov
ecx,dword ptr ds:[503938] mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] mov
edx,dword ptr ds:[47FCD8] call
462634 mov
ecx,dword ptr ds:[50385C] mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] mov
edx,dword ptr ds:[47F948] call
462634 mov eax,dword
ptr ds:[503B94] mov
eax,dword ptr ds:[eax]
call 406EDC
这是最后一次后面存在popad的div 0异常。看看是如何回到原程序的。
在73D872忽略所有异常,对code section下内存访问断点。中间在kernel32中有一次内存访问异常。

最后一次div ebx在73DBE1。单步跟到这里:

73DE38破坏前面代码。

至此修复所有stolen codes,将前面的6部分和fasle OEP的2个call合在一起。
Push
ebp Mov
ebp,esp Push
edx Push
edi Push
ebx Push
eax Mov
eax,4D9BCC mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] call
46261C mov
ecx,dword ptr ds:[503938] mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] mov
edx,dword ptr ds:[47FCD8] call
462634 mov
ecx,dword ptr ds:[50385C] mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] mov
edx,dword ptr ds:[47F948] call
462634 mov
eax,dword ptr ds:[503B94] mov
eax,dword ptr ds:[eax] call 4626B4 call
4049D8 lea
eax, [eax+0]
call 406EDC