作者:haggar
原文链接:http://www.reversing.be/article.php?story=20061206203632615
译者:Cyg07(cyg0x7 at 0wei.com)
译者注:本人翻译不佳,望路过的兄弟多多指正!
感谢machenglin和KuNgBiM两位前辈的耐心指导!
本文是针对ExeCryptor 2.3.9小部分更新而写的脱文。本文主要目的是脱壳,故没有做后期的破解。脱壳前你需要用到的工具有:OllyDbg(包含脚本插件和一些隐藏插件),LordPE,ImpREC 和 一个稳定Windows XP平台。
- 调试前的准备 -
首先打开 Olly 中的 "Debugging options" 选项,在 "events" 中设置 "Make first pause at:System breakpoint"。这样一来可以防止ExeCryptor直接在TLS回调上做的手脚。好了,我们开始把目标程序载入 Olly 。
打开断点管理窗口,清除断点:
0076E427 EXECrypt One-shot CALL EXECrypt.0076E323
使用一些插件对 Olly 进行隐藏设置(或者参看以前的教程进行手工清除校验):
IsDebuggerPresent/BeingDebugged
NtGlobalFlag
HeapFlag
CheckRemoteDebuggerPresent
不要在插件中设置任何检查,因为EC(ExeCryptor)都有可能发现它的行为。EC一般都会检测一些HOOK行为。
Ctrl+G 查找 OutputDebugStringA 函数,并把函数的开始部分用 "RETN 4" 操作码替换。此举是为了防止 Olly 处理 "s%s%s" 的Bug。
- 巧用硬件异常 -
这是个新的技巧。该版的EC好象或多或少的设置了些硬件异常断点。我不知道这点是如此体现出来的,但可以使用 Olly 来检测。
来到 Olly 的异常选项窗口,打上所有复选框。清除所有自定义异常。确保你没有修改任何内存,软件或者硬件断点。现在 Shift+F9 运行程序。你将停在第一个异常处:
007637D0 F0:F1 LOCK INT1
连续Shift+F9(3次以上),然后检查下硬件断点(Debug menu -> Hardware breakpoints)。你将看到硬件断点窗口中有个断点:
Hardware breakpoints
# Base Size Stop on
1 0076098F Temporary
这是个EC脱壳的新技巧。如果你带着这个硬件断点继续调试,最终程序将会挂掉。所以我们应该删除该断点。另外回到异常选项框里在自定义栏中添加入最后异常(C000001E INVALID LOCK SEQUENCE)。
- FindWindowA 检测 -
硬件断点检测问题被解决了。接下来的IsDebuggerPresent检测也已经被我们的插件解决了。该是解决 FindWindowA检测的时候,EC会搜索当前进程中含有"OLLYDBG" 类名的窗口。这里我们还是Ctrl+G->FindWindowA,在函数入口处设置断点。Shift+F9后停在断点处,EC对API的函数的检测行为真的很烦琐(开始清理下代码中的校验):
0055A3DB 8A00 MOV AL,BYTE PTR DS:[EAX]
00595A6F 2C 99 SUB AL,99
00595A71 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
00595A74 F62A IMUL BYTE PTR DS:[EDX]
005DEE5C 3C A4 CMP AL,0A4
005DEE5E ^ 0F85 2BFFF7FF JNZ EXECrypt.0055ED8F
005DEE64 E9 598A0000 JMP EXECrypt.005E78C2
有的地方EC鉴于某些原因检测函数的第一个操作码:
005F8C87 0FB600 MOVZX EAX,BYTE PTR DS:[EAX]
005F8C8A 833C85 00004E00>CMP DWORD PTR DS:[EAX*4+4E0000],0
005F8C92 ^ 0F84 D4A2F1FF JE EXECrypt.00512F6C
005F8C98 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ; user32.FindWindowA
005F8C9B 8038 CF CMP BYTE PTR DS:[EAX],0CF
005F8C9E ^ 0F84 C1A2F1FF JE EXECrypt.00512F65
005F8CA4 ^ E9 8571FFFF JMP EXECrypt.005EFE2E
检测API是否被HOOK或做重定向处理:
005EFE2E 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ; user32.FindWindowA
005EFE31 8038 E9 CMP BYTE PTR DS:[EAX],0E9 ; Is JMP?
005EFE34 ^ 0F84 2B31F2FF JE EXECrypt.00512F65
005EFE3A 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ; user32.FindWindowA
005EFE3D 8038 EB CMP BYTE PTR DS:[EAX],0EB ; Is JMP SHORT?
005EFE40 ^ 0F84 1F31F2FF JE EXECrypt.00512F65
005EFE46 E9 38770100 JMP EXECrypt.00607583
检测是否被涂改了补丁代码 RETN XXXX:
004EB930 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] ; user32.FindWindowA
004EB933 8A00 MOV AL,BYTE PTR DS:[EAX]
004EB935 24 F6 AND AL,0F6
004EB937 3C C2 CMP AL,0C2
0060CCB1 ^ 0F84 AE62F0FF JE EXECrypt.00512F65
查看第一条操作码 JMP DWORD [EAX] 是否被重定向:
0060CCBA 66:8B00 MOV AX,WORD PTR DS:[EAX]
0060CCBD 66:25 FF38 AND AX,38FF
005F0C95 66:3D FF20 CMP AX,20FF
005F0C99 ^ 0F84 C622F2FF JE EXECrypt.00512F65
检查 call xxxxxxxx 类型的重定向行为:
00546361 8038 E8 CMP BYTE PTR DS:[EAX],0E8
00546364 0F85 DBED0300 JNZ EXECrypt.00585145
0054636A ^ E9 5DF7FCFF JMP EXECrypt.00515ACC
一些更深入的研究是执行后调用 FindWindowA 函数寻找 "OLLYDBG" 窗口。OK,我们清零EAX后返回就可以避开这个检测了。
EC 将会进一步的遍历所有进程来寻找是否被 Olly 调试。EC使用 GetWindowThreadProcessId 来获取所有进程的PID,然后依次使用 ReadProcessMomery 读取分析。(EC对该API上有检测)我们不能在 ReadProcessMomery 上设置断点,我们可以使用内存断点的方法,但这样太慢了。实际上ReadProcessMemory函数也是调用ZwReadVirtualMemory 函数的,我们可以在ZwReadVirtualMemory上设置断点避开EC的检测。现在我们可以F9运行了。当停在了我们设置的断点处,跳出系统领空并返回到EC的代码中跟踪检测代码(如果你在本次调用没有发现到检测,F9后继续观察):
005ABBE2 81BD 38FEFFFF 45>CMP DWORD PTR SS:[EBP-1C8],2B584245
005ABBEC ^ 0F85 8A8BFAFF JNZ EXECrypt.0055477C
005ABBF2 ^ E9 6371F6FF JMP EXECrypt.00512D5A
跟随到数据窗口中:
005ABBE2 81 BD 38 FE FF FF 45 42 58 2B ..8...EBX+
可以看出EC在检测 Olly 的字符 "EBX+"。我们把 2B584245 修改下(比如12345678)让它的检测失效。EC没有对此设置校验。
如果查看EC的所有区段,你会发现有不少类似的检测。查找下CMP DWORD[EAX],CONST 你就知道了:
Found commands
Address Disassembly Comment
00514CDB CMP DWORD PTR DS:[EAX],47424454 ; TDBG
0053A13B CMP DWORD PTR DS:[EAX],47424454
0055DF80 CMP DWORD PTR DS:[EAX],4742444F ; ODBG
0057FD31 CMP DWORD PTR DS:[EAX],47424454
00599B86 CMP DWORD PTR DS:[EAX],4742444F
005A6EC7 CMP DWORD PTR DS:[EAX],4742444F
005AF1DF CMP DWORD PTR DS:[EAX],4742444F
005BA618 CMP DWORD PTR DS:[EAX],47424454
005C88BD CMP DWORD PTR DS:[EAX],47424454
005C96FF CMP DWORD PTR DS:[EAX],4742444F
005D77C5 CMP DWORD PTR DS:[EAX],4742444F
005E0B5A CMP DWORD PTR DS:[EAX],4742444F
005EC439 CMP DWORD PTR DS:[EAX],4742444F
005EEDDE CMP DWORD PTR DS:[EAX],47424454
005F6512 CMP DWORD PTR DS:[EAX],47424454
006004FA CMP DWORD PTR DS:[EAX],47424454
还有对sysinternals tools的检测:
Found commands
Address Disassembly Comment
00525A2A CMP DWORD PTR SS:[EBP-10C],656C6946 ;File
0056A41F CMP DWORD PTR SS:[EBP-10C],6D676552 ;Regm
00593828 CMP DWORD PTR SS:[EBP-10C],6D676552
005BE5A2 CMP DWORD PTR SS:[EBP-10C],76676264 ;dbgv
005DF3F9 CMP DWORD PTR SS:[EBP-10C],36343831 ;1846
005E95A7 CMP DWORD PTR SS:[EBP-10C],36343831
0060BCE9 CMP DWORD PTR SS:[EBP-10C],76676264
- 多线程保护 -
启用多线程是用来持续保护壳程序的。可以直接NOP掉整个 CreateThread API (保留 RETN xxxx)。
- OEP -
EC使用Delphi编写的,所以OEP应该在代码段的末尾处。但是OEP已经被"偷"了,并且原OEP处的代码已经被EC涂改得怪凌乱的。我们使用以前教程提及过的内存断点大法在代码段上设置断点。OEP应该就是这里:
004D2AB8 - E9 81560D00 JMP EXECrypt.005A813E
004D2ABD 45 INC EBP
004D2ABE 91 XCHG EAX,ECX ; EXECrypt.005A8100
004D2ABF 3D 6F43525F CMP EAX,5F52436F
004D2AC4 35 92C3E6E9 XOR EAX,E9E6C392
004D2AC9 DA87 0900D6AD FIADD DWORD PTR DS:[EDI+ADD60009]
004D2ACF 9E SAHF
004D2AD0 D7 XLAT BYTE PTR DS:[EBX+AL]
...
飞向光明之颠--OEP:
005A813E 55 PUSH EBP
005A813F ^ E9 7C52FAFF JMP EXECrypt.0054D3C0
005A8144 ^ FF25 58D74D00 JMP DWORD PTR DS:[4DD758] ; EXECrypt.0051256B
005A814A ^ 0F88 670CF7FF JS EXECrypt.00518DB7
...
现在我们开始修复IAT。
- 修复IAT -
修复IAT在以前的教程里都有说明了。这里直接用脚本修复它们,然后使用LordPE转储文件,重建输入表即可。
//-------------------------------- SCRIPT START -----------------------------------------
//ExeCryptor 2.x IAT for asm/Delphi/BorlandC++ type - by haggar
var addr
var oep
var pointer
var counter
var esp_ref
var temp
mov addr,401000
mov oep,eip
LABEL_01:
find addr,#ff25????4D00#
cmp $RESULT,0
je END_01
mov addr,$RESULT
add addr,2
mov pointer,addr
mov pointer,[pointer]
mov pointer,[pointer]
cmp pointer,10000000 //Check is import placed in thunk, or redirection.
ja LABEL_01
cmp pointer,0 //For delphi!!!!!!!!!!!!!!!!
je LABEL_01
sub addr,2
mov eip,addr
add addr,2
mov esp_ref,esp //Stack reference.
mov counter,0
LABEL_02: //Trace some code.
sti
add counter,1
cmp counter,30
jne LABEL_02
mov temp,esp
LABEL_03: //Find referenced stack value.
add temp,4
cmp temp,esp_ref
jne LABEL_03
sub temp,4
mov temp,[temp] //Go to "Magic address".
bp temp
esto
bc eip
mov temp,[eip]
and temp,0ffff
cmp temp,025ff //SelfWriting import type? No need to fix it then.
je LABEL_01
cmp eax,10000000 //If EAX=!IMPORT, then it is a first type.
jb LABEL_01
mov temp,addr //In this case EAX=IMPORT.
mov temp,[temp]
mov [temp],eax
jmp LABEL_01
END_01:
mov eip,oep
ret
//------------------------- END SCRIPT ------------------------------------------------
- 脱壳修复 -
使用LordPE修改TLS指针为 000E2000。处理后应该可以正常运行,可能还会有点小问题。最大的问题是可能出现一系列的异常,存在一定的兼容性问题(比如程序无法跨机器运行)。 EC的脱壳修复方面可以通过分析官网的Excryptor crackme了解到更多。
- 结语 -
没有了。
- 标 题:【翻译】ExeCryptor 2.3.9 - Unpacking
- 作 者:回忆罐头
- 时 间:2008-02-22 20:08:12
- 链 接:http://bbs.pediy.com/showthread.php?t=60035