📄 cn.txt
字号:
Sdprotector 1.12增强版手动脱壳教程(作者:KaGra)
目前最新的1.12增强版SDPR可以在www.sdprotector.com下载。
好!很高兴再次与大家交流,这次我们的实验品是Sdprotector.
这次我用另一种方法脱这个壳。下面是详细过程:
工具准备:Olly v1.1, Imprec v1.6f, Ollydump插件,HideOlly插件(不再赘述)
我钩选了SDprotector所有保护选项给程序加壳。它位于“选项”菜单的屏幕左半部分。其它未变。
用Olly装载加壳程序。在“调试”选项中,全部钩选,这样程序的执行就不会被例外所中止(中止后还需按SHIFT+F9或F8,F7才能继续执行)。加壳程序运行后,对话框提示“发现调试器”。我们很难确定引起反调试的保护究竟是什么。
我发现它使用CreateToolHelp32Snapshot函数侦测调试器,最终找到“什么窗口被打开”并把它们与默认的字符串列表(“Olly”也在此列)进行比较。另外SetUnhandledExceptionfiler也能找到调试器,但调用这个函数以后例外是怎样产生的却令我大惑不解。我产生了一个想法:单纯运行加壳程序,再用Olly附加进程。
说干就干。运行加壳程序,对话框显示demo版壳的一些信息(别担心,它是用来保护文件运行的),接着加壳程序进程出现在屏幕上。现在,打开Olly。还没等你附加进程,对话框提示“发现调试器”并关闭了Olly.再次运行加壳程序,打开LordPE对进程Dump,也是如此。我们该怎么办?
我们可以知道反调试并没有调用SetUnhandledExceptionFiler函数或者别的什么例外陷阱,因为函数或例外并没有被调试。唯一的想法就是代码包含了关于进程或窗口句柄的难解的字符串。同时代码还含有一个循环,用来校验包含那些字符串的运行中的进程和窗口句柄。尽管代码是在原程序的代码段中,但壳却赋予了一些特别的代码,即使我们通过了入口点,这些代码仍然存在并进行循环校验。
我的猜想是正确的。我们重命名LordPE->运行加壳文件->运行重命名后的LordPE。重命名后并未被关闭。可是Olly重命名后却仍然被关闭。正如猜想的一样,是由于加壳程序的.data和.edata段中包含了以“Olly”开头的字符串,并在安全循环中被复杂编码(hardcoded)。我们用Olly装载自身,单击”M“按钮查看装载的Olly:
Memory map(内存镜像)
Address Size Owner Section Contains Type Access Initial Mapped as
00400000 00001000 OLLYDBG PE header Imag R RWE
00401000 000AF000 OLLYDBG .text code Imag R RWE
004B0000 0005B000 OLLYDBG .data data Imag R RWE
0050B000 00001000 OLLYDBG .tls Imag R RWE
0050C000 00001000 OLLYDBG .rdata Imag R RWE
0050D000 00002000 OLLYDBG .idata imports Imag R RWE
0050F000 00002000 OLLYDBG .edata exports Imag R RWE
00511000 00036000 OLLYDBG .rsrc resources Imag R RWE
00547000 0000C000 OLLYDBG .reloc relocations Imag R RWE
在.idata段中模糊查询字符串“Olly”,发现:
0050F780 6F 6C 6C 79 64 62 67 2E 65 78 65 00 5F 41 64 64 ollydbg.exe._Add
0050F790 73 6F 72 74 65 64 64 61 74 61 00 5F 41 64 64 74 sorteddata._Addt
0050F7A0 6F 6C 69 73 74 00 5F 41 6E 61 6C 79 73 65 63 6F olist._Analyseco
把“Ollydbg.exe“重命名如fffffff.exe,再次查找。我们会发现只有那一个。在.data段再次查找,会发现很多,修改所有的这些字符串。
不钩选“输入表重建”选项,我们用Ollydump插件对进程Dump. 这时Olly已打上补丁。任意命名Dump文件但不能含有”Olly”字符串。再次运行加壳程序直到主窗口出现。现在运行“新”的Olly。。。。。。
未发现Olly!如果你犯错误或者做错一个步骤,Olly将会被发觉,你将不得不全部重打补丁。当程序发现可能的调试器字串或任何可能构成“威胁“的代码时,它会把这些字串或代码存放起来(寄存器,内存,其它),并做上记号。因此补丁要正确,要遵照说明一步步地去做。还有一种一次性Olly补丁,通过替换字符串中的ASCII”FH“来解决反调试的问题。虽然不知道为什么,但是很有效。你也可以如法炮制。
在“新“的Olly“调试器”选项中钩选所有的例外,“忽略”选项中“下列典型例外”为空。
选择并附加加壳程序进程,由于可能会侦测到Olly,请不要打开其它窗口。完成上述步骤后,你会来到这里:
77F767CE C3 RETN
77F767CF > CC INT3
77F767D0 C3 RETN
77F767D1 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
77F767D5 CC INT3
77F767D6 C2 0400 RETN 4
77F767D9 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
77F767DF C3 RETN
77F767E0 > 57 PUSH EDI
77F767E1 8B7C24 0C MOV EDI,DWORD PTR SS:[ESP+C]
77F767E5 8B5424 08 MOV EDX,DWORD PTR SS:[ESP+8]
77F767E9 C702 00000000 MOV DWORD PTR DS:[EDX],0
77F767EF 897A 04 MOV DWORD PTR DS:[EDX+4],EDI
77F767F2 0BFF OR EDI,EDI
77F767F4 74 11 JE SHORT ntdll.77F76807
77F767F6 83C9 FF OR ECX,FFFFFFFF
77F767F9 33C0 XOR EAX,EAX
77F767FB F2:AE REPNE SCAS BYTE PTR ES:[EDI]
现在,按下“M”按钮,在.text段设置“内存访问断点”。此段包含了加壳程序的原代码。接着调整你的时钟,缩短或加快一个小时。这是因为时钟的调整可以阻止加壳程序反调试循环校验的执行。这个循环通过对时-分-秒周期性的校验执行对调试器的侦测。按F9执行程序。调试器条件中断(译注:内存访问断点)在如下地方:
0040111B C8 000000 ENTER 0,0
0040111F 53 PUSH EBX
00401120 56 PUSH ESI
00401121 57 PUSH EDI
00401122 817D 0C 11010000 CMP DWORD PTR SS:[EBP+C],111
00401129 0F84 AB000000 JE sdprotec.004011DA
0040112F 817D 0C 10010000 CMP DWORD PTR SS:[EBP+C],110
00401136 0F84 86000000 JE sdprotec.004011C2
0040113C 837D 0C 10 CMP DWORD PTR SS:[EBP+C],10
00401140 0F84 B5000000 JE sdprotec.004011FB
00401146 B8 00000000 MOV EAX,0
如果你没有调整时钟,跟踪时会产生一个无法处理的例外,这将导致Olly被侦测到进而被关闭。一旦失败重试,调整时钟就可解决。
好,我们到达了脱壳后的原代码中。可是入口点在哪里呢?其实我们已经通过了入口点,因为“新”的Olly运行前加壳程序已经运行了。但是,由于加壳程序仅仅显示了注册对话框,因此程序是处于入口点“后”不远的循环之中的,可是循环未被完全执行(译注:时钟调整),这使得程序离入口点又远一些。现在,我们暂停在0040111B处。往上找找。如下地方:
004010C2 6A 00 PUSH 0
004010C4 E8 E1010000 CALL sdprotec.004012AA ; JMP to kernel32.GetModuleHandleA
004010C9 A3 F3204000 MOV DWORD PTR DS:[4020F3],EAX
004010CE C705 C7204000 03>MOV DWORD PTR DS:[4020C7],4003
004010D8 C705 CB204000 89>MOV DWORD PTR DS:[4020CB],sdprotec.00401>
004010E2 C705 CF204000 00>MOV DWORD PTR DS:[4020CF],0
004010EC C705 D3204000 00>MOV DWORD PTR DS:[4020D3],0
004010F6 A1 F3204000 MOV EAX,DWORD PTR DS:[4020F3]
很像入口的地方,因为我们发现了GetModuleHandleA的CALL(许多程序都利用这一CALL返回数值并把它作为入口点后的操作码),另外我们发现在004010C2之前并没有其它的API CALL(一般情况下,入口处的CALL是GetVersion, GetModuleHandleA, LoadLibrary或GetCommandLineA一类的API)。你可能会说:为什么GetModuleHandleA不能提前?我尝试过,可是在我完成输入表重建后(这是后话),我却无法使程序工作。事实上,入口点就是004010C2。因为在有些情况下,使用如C++一类的语言编译器,编译同样的可执行程序,入口点处的4-5个操作码是相同的。因此,附加和Dump后所停留的代码处,由于本身和周围没有过多的操作码序列(译注:GetVersion, GetModuleHandleA, LoadLibrary或GetCommandLineA一类的API),其实离入口点已经不远了。无论怎样,仅仅为了Dump的方便,我们没有必要确切地停在入口点处。我们称入口点是最理想的地方,那是在所有的段都完整、美好、干净的情况下(还有无自校验的修改、在内存和其它进程或自身的运行中无修改发生)。我们删除内存断点。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -