这是RCE Message Board上贴的一个unpackme,留的MSN是mm@digitalnuke.com,不会是骨灰mm吧J。用SDProtector 1.10保护,从壳代码看好象不是注册版,但运行时并没有Nag screen,也许是破解版本。
我是脱壳新手,这次好歹全程跟完了一回。壳的anti-debug比较强,在OllyDbg下不能运行,要是先运行再attach,也会被杀掉。全程跟也是没办法。
运行几步,就会解出完整的壳代码。用WinHex拷出来,贴回程序,用IDA编译。写了个IDC脚本,去掉大部分花指令。因为程序中充斥大量的自校验,跟的过程中没有改代码,对照着IDA跟的。另外,壳中反复检查运行时间,为了能在上一次停下的地方继续,写了OllyScript脚本。即使这样,有一处的检测有时候仍然通不过,多试两次就好了。
用的是自己修改过的OD 110c来调试。在W2K下脱的。
1.
anti-debug
1) int 3 异常
大部分int 3异常都是用来anti-debug的。除了清调试寄存器,SHE中还对代码做个patch,结束异常处理后检查,以测试是否执行了SHE。
检查:
有部分int 3 在SHE中恢复异常处理结构,不再返回异常位置:
2) int 1 异常
只出现了一次。与int 3相似,只用于anti-debug。
3) 单步陷阱
修改EFLAGS寄存器,引发异常。
在OD中如果F7单步执行,SHE不会执行,下面的检查通不过。
4) API断点检测
5) debugger检测( ZwQuerySystemInformation
)
用ZwQuerySystemInformation检测NTICE.SYS,ICEEXT.SYS。
IsDebuggerPresent检测ring 3调试器。
6) 从注册表检查是否安装IceExt
7) debugger检测(
CreateToolHelp32Snapshot,ProcessFirst,Process32Next )
黑名单:
fly的OD也在J。找到自己退出,用TerminateProcess干掉坏人。
8) debugger检测(
CreateFile )
9) 执行时间检查
一般在异常处理前后。这样如果以在OD中拦截异常的方式切入,通不过检查。有3种方式:
两次调用rdtsc,检查差值。
两次调用GetTickCount,检查差值。
另外还有一种,只用了一次:
10) SMC反单步跟踪
执行rep指令时ecx=3,如果在这里F7,这句rep会修改自己(此时ecx减1,即只mov了一个字节),变成jmp到下面的0106B705。这个call执行就下课了。这一招用了无数次L。在执行到rep以前,直接在第2个call(这里是106B70A)用F4。
F7的结果:
这是正确的结果:
11) 多线程anti-debug
除了主线程,另外运行2个专门执行anti-debug的线程(线程中会随机引发不同种类的异常),二者相互检查是否一直在运行,以及是否被修改。这也是attach失败的原因。
在OllyScript脚本中跳过2句ResumeThread,使其处于suspended状态。
2. 寻找OEP
在回到原程序前,在原程序中查找若个C3(即ret)一一调用,此时还没有到OEP。
跟到最后一个int 3,结束后就到OEP了。
此时的stack:
前面找的ret所在地址,压了几个在栈中。回原程序后连续执行几个ret才到OEP:
对比一下WinXP的Notepad,可以看到这里并没有stolen code。
3. anti-dump
壳代码在解出原程序时,把内存属性设为PAGE_NOACCESS,使在OEP处dump失败。修改一下脚本,改属性为PAGE_READWRITE|PAGE_EXECUTE。
4. 修复IAT
在这里处理IAT。
这里检查当前处理的dll名。如果匹配,会得到特殊照顾。包括kernel32,user32,gdi32,advadi32,
shell32以及sdprotector.dll。在这里手工使dll都不满足检测。这里会置标记(Magic JumpJ)。
sdprotector.dll还有另一个标记:
前面手工使所有的dll都不匹配,到这里esi->IAT:
这里还有两处检查。当处理的是user32.dll时,注意MessageBoxA和MessageBoxW必须跳走,否则IAT被破坏,想必是用emulated API替代。
对于不在列表中的dll,直接取出API地址送到IAT(这正是我想要的J):
这样得到的IAT全部有效。
5. 最后,作者的love
letter J