不清楚这个SDP究竟是什么版本,总之不是以前碰过的那个J。用的是修改版的OllyDbg 110,OllyScript 0.92。
1.
测试
事先已经知道是用SDP做的,想看看是否为双进程版本。直接运行,用LoadPE只看见一个进程。用OD加载,因为Single Step Event停在这里:
在IDA中的EP是5E2000,而且不象ExeCryptor,没有TLS Callback Function。修改OD设置:
停在ntdll中。再对5E2000下断,可以停下。清除该断点,不忽略所有异常,用IsDebuggerPresent隐藏OD,F9运行。
直接运行,用WinHex搜索,用什么版本保护的?文件头中的标记:
提示信息:
不会是破解版本吧,或许注册版本也有这个?
写个简单的OllyScript,记录异常次数。之所以用script而不是直接用手按,是因为SDP对运行时间的检测很严J。
可以看到,10次shift-F9后报debugger detected。
2.
debugger检测
10次shift-F9后,这里异常:
用script走到5E2EFA,这个异常的handler没有改变context中的eip,从SEH handler中返回异常地址继续执行。dump解出的代码,去掉Junk codeJ。
跟第10次异常后的代码,唯一的一次判断在这里(不跳,往下执行即显示debugger detected):
修改script,在这里jmp到5E2F7F。程序继续运行了。下面又是一次异常,
这里如果shift-F9,原来的进程结束了。新启动一个进程继续运行。
3.
进程逃逸
从上面这个异常(5E313D)的handler开始跟。有个问题:在debugger下运行有时会失败,不能启动子进程。一旦失败一次,就再也不能正常运行起来了。不知道是不是程序做了什么手脚。在我的W2K SP4和WinXP Pro下都如此。
这是创建新进程的代码:
创建进程时的参数:
父进程和子进程使用同一个EXE文件,参数也没有任何特殊之处。
执行CreateProcessA后的返回地址。
创建新进程之后,父进程立即就退出了,子进程独立运行。显然,这里并不象Armadillo的Nanaomites那样让人头疼J 。创建新进程的唯一目的就是摆脱debugger。
为什么新的进程不能运行了?注意在调用CreateProcess时传递的StartInfo结构没有正确填入数据。
全部为0。是否程序发现正在被调试,设置了某些标记? 试试手动填入cb等数据,仍然不行。从任务管理器的显示看,似乎这个新创建的进程仍然在把自己当父进程运行, 大概是某些用于同步的内核对象出了问题。
既然子进程完全独立运行,可以甩开上面的问题,直接使程序以子进程方式运行,继续调试。
4.
分离子进程
用OllyScript在上面的5E3194处jmp,强制以子进程方式运行。记录异常和中断。
也是10次,后面的2个记录是异常。程序直接退出了,弄错了? 从log看,判断应该是正确的。跟一下。
从 handler 5E4590跟起。到这里,出现log中的第1个内存访问异常。
这个异常被OllyDbg处理了。用插件避开这个anti-debug技巧。
在OllyDbg中停在系统断点时就运行UnhandledExeptionFilter插件。仍然用前面的script记录。这次程序正常运行了。
这次记录为0x35。注意异常地址并不在壳代码空间(因为程序在开始运行时总有一个数组越界错误,脱壳后倒好了J),后面的可能是控件抛出的。
这是壳代码执行的最后一个异常,计数为0x2E。
另外,注意这里:
5E78E2这个地址在log中反复出现,后面往往有Module载入。这里是处理IAT的地方?
5.
寻找OEP
用Script停在上面记录的壳代码最后一个异常处(5E707A)。handler结束后回到异常处继续。
F7单步几次就到OEP了。
ret到这里。
没有stolen code?不会吧。可能找错了。先dump出来再说。用OllyDump。
anti-dumpJ。直接在OllyDbg中修改每个section的内存块属性。
这次可以了。
用资源编辑器看看,是Delphi/BCB程序。用IDA反编译一下dumped.exe。
看起来OEP是对的J。
6.
修复IAT
IAT从这里开始:
结束:
size = 4D58E4 – 4D5168 = 77C
单步跟了几个API调用,既有redirected到壳中的,也有由壳代码emulate的。从5E78E2处的异常入手, 在第一次断下开始跟(counter=3)。
把API(MessageBoxA)的地址保存在了2个地方:壳空间和heap。这个地址比进程的映射地址更低。在OEP可以看到,IAT中很多entry是指向这里的。
在5E78E2第1次异常时,处理user32,GDI32, ADVAPI32, RPCRT4, IMM32,LPK共6个dll。KERNEL32的api已经在更早的时候处理过了。此时上面已经填入了约60项kernel32的api地址。
相关代码与SDP以前的版本相似:
需要注意的dll仍然是: kernel32, user32, advapi32, shell32,gdi32,以及sdprotector.dll。
在决定以子进程运行前,壳代码处理了上述几个dll,获得的API地址保存了2份,分别在壳空间和堆中。
现在试试跟从按子进程运行后,这段代码的处理。试跟了2个,并非5E78E2异常都与IAT相关。用脚本试试:
到这里IAT中已经填入了一些重定向值了。从上一个int 3异常开始跟。
这里开始了。注意这2个函数:
进去看看:
连续6个这样的函数。5EEB3D进去有些计算,象CRC32(有个初始化256 byte向量的动作J)。不过这已经不重要了,只要稍微想一想,就明白这是在干什么。6个特殊对待的dll,显然这些函数用于判断J。
IAT中的数据很规范,基本是这样的格式。
什么时候变成指向14xxxx或5Exxxx就是被处理过了J。
第0x12次int 3 -> 处理kernel32.dll
(这个也许不跳也行?我没有试J)
第0x13次 Single step event -> 处理user32.dll
第0x14次 Single step event -> 处理advapi32.dll
依此类推,直到IAT全部填充完毕。
全部有效。Fix dump,运行:
只脱掉壳,并未破解。