Wednesday, November 29 2006 @ 07:14 PM CET Contributed by: haggar Views: 10843
Level : intermediate
1. Quick Intro
Hi folks, this is my second tutorial about ExeCryptor , altough I don't know will I publish it before one that I wrote first (for official ExeCryptor crackme). Target for this tutorial is diProtector v1.0 (google it). I don't know is it latest since I had it on my hard drive for a while. Target is compiled in MASM and it doesn't have stolen OEP or functions, plus there are very few imports in the file. All that makes unpacking much easier. Tools that I used for this job are OllyDbg 1.10, LordPE, ImpREC, some hex editor. You will not need any plugins or patches to hide Olly. We will do all manually.
Let's see what tricks ExeCryptor has prepared for us:
- Because TLS callbacks, our target will run before OEP is reached. Olly will be detected by other debugging tricks and protected program will exit. To avoid that , we must set Olly to break on system breakpoint. You can set that in "debugging options" , under "events" (make first pause at - system, breakpoint). Now we can load target in Olly. But here we have first EC (ExeCryptor) trap. When you loaded target in Olly, check breakpoints window and you will see one breakpoint:
In Olly pane you can see information that on that address is E8 byte
but when we step in with trace over the opcode and byte is loaded to AL, check registers window:
EAX 000000CC <----------------------- EAX = CC = BREAKPOINT IS DETECTED !!!
ESI 0045F3D1 diProtec.0045F3D1
EDI 00400000 diProtec.00400000
EIP 00455EB2 diProtec.00455EB2
This doesn't have to be breakpoint check on that opcode. It could be some checksup on code block which is same thing. It's not important. To avoid this trick just delete breakpoint in bp window and problem is solved.
- Further EC debugger tricks are described in PNLUCK's tutorial. These are:
These flags needs to be set to 0 so EC doesn't detect debugger. You can use any plugin for this, but we will see how manually this can be resolved. Open memory window , scroll down to the block "data block of main thread":
Address Size Owner Section Contains Type Access Initial Mapped as
00400000 00001000 diProtec PE header Imag R RWE
00401000 00002000 diProtec .text Imag R RWE
00403000 00001000 diProtec c3o2hlw9 Imag R RWE
00404000 00001000 diProtec .data data Imag R RWE
00405000 00004000 diProtec WCE_ARM Imag R RWE
00409000 00002000 diProtec KG Imag R RWE
0040B000 00003000 diProtec .rsrc resources Imag R RWE
0040E000 00001000 diProtec bjmdft2e Imag R RWE
0040F000 00025000 diProtec n9cjl4.c Imag R RWE
00434000 0002C000 diProtec g0qnncsj code,imports Imag R RWE
7F6F0000 00007000 Map R E R E
7FFB0000 00024000 Map R R
7FFDA000 00001000 Priv RW RW
7FFDF000 00001000 data block o Priv RW RW <------------ HERE!!!
7FFE0000 00001000 Priv R R
Double click on that block to open new dump window:
Follow that address in dump (this block and that address will be different for you, but block is always labeled by olly as "data block of main thread" and addres that we want inside is data_block_base+30). In dump:
DWORD at block_base+10 must be 0 if program is not debugged. In this case it is 60000040=40000060. So set it to 0.
And these three checks are now disabled. This is also the same thing what hide plugins for Olly do.
- Almost every import that EC uses is checked for breakpoints, but also it is checked for hooks and redirections. So if some plugin redirects some API to it's own code (like some hide plugins do for ZwQueryInformationProcess) it will be detected.
- IsDebuggerPresent API is used , but we already disabled that check.
- CheckRemoteDebuggerPresent API is used to detect debugger. It is big API and we can place bp at the and to break there. EC didn't check whole API for bp. Just set return value in EAX to 0.
3. Finding OEP
When you have disabled all debugger checks, just place memory breakpoint on access on first section and run. First time we will break in the unpacking procedure:
00454D55 AA STOS BYTE PTR ES:[EDI]
00454D56 ^EB E9 JMP SHORT diProtec.00454D41
00454D58 E8 FC000000 CALL diProtec.00454E59
00454D5D 0F82 97000000 JB diProtec.00454DFA
00454E85 11C9 ADC ECX,ECX
00454E87 E8 CDFFFFFF CALL diProtec.00454E59
00454E8C ^72 F2 JB SHORT diProtec.00454E80
00454E8E C3 RETN
00454E8F 8BE5 MOV ESP,EBP
00454E91 5D POP EBP
00454E92 C3 RETN <------------ Place bp and run.
This procedure unpacks code to 401000 section. Remove mem bp, place bp at RETN (one belov MOV ESP,EBP and POP EBP) and run. Remove that bp, code section is unpacked now. Olace memory bp again and run. You will stop here:
00434142 AC LODS BYTE PTR DS:[ESI]
00434143 D0E8 SHR AL,1
00434145 80F8 74 CMP AL,74
00434148 75 0E JNZ SHORT diProtec.00434158
0043414A 8B06 MOV EAX,DWORD PTR DS:[ESI]
0043414C 0FC8 BSWAP EAX ; diProtec.00401000
0043414E 01C8 ADD EAX,ECX
00434150 8906 MOV DWORD PTR DS:[ESI],EAX ; diProtec.00401000
00434152 83C6 04 ADD ESI,4
00434155 83E9 04 SUB ECX,4
00434158 49 DEC ECX
00434159 ^7F E7 JG SHORT diProtec.00434142
0043415B 59 POP ECX
0043415C 5E POP ESI ; diProtec.00401000
0043415D C3 RETN <------------- Place bp here !
Again , remove mem bp, place bp at RETN and run, remove that bp and place again mem bp on code. You will break on OEP:
As we can see, some imports are OK, but most of them are redirected. Imports are hashed, EC decrypts them, then it jumps to them with JMP or RETN. If JMP is used, then original import is decrypted and placed in IAT. In case of RETN, import is never writen in IAT and decrypting will always be performed. Note that these types can have sub-types and these are just examples. Also these subtypes are not important, we can write script that will fix all imports. I will descrybe how did I managed to fix imports.
This is first type of import. Import is decrypted from and here it jumps to it. Also that import is written in IAT section and this decryption is performed only once. Check stack and you will see that it is maintained , ei. ESP again points to starting value.
$-4 > 00427F51 RETURN to diProtec.00427F51 from diProtec.0042924B
$ ==> > 7C816D4F RETURN to kernel32.7C816D4F
I place bp on that addres and run:
00427F51 E8 4E91FFFF CALL diProtec.004210A4
But check value in EAX register:
EAX 77D588E1 user32.DialogBoxParamA
And that is good import. Plus, stack is again maintained. Note this! If at that addres EAX doesn't hold IMPORT, it means that it will just after that trace couple opcodes to the final JMP_IMPORT. So it can be a sub-type of first type. Always check EAX at that point.
So we see, that address in stack is return address where we will see EAX=IMPORT or JMP_IMPORT. Problem is that we need to trace some code untill stack value is formed. But that is not problem for a script, only that it will take some time to trace (my script traces 30 opcodes , then finds return address in stack, places bp there, checks type and fixes import if needed).
We can dump image from memory with LordPE. But notice that ImageSize is 00001000. In LordPE, right click on target process and select "Correct ImageSize". New image size will be set to 00060000. Now dump file.
Funny part is, that if we leave EC sections in dump and we doesn't change TLS data, then we use ImpREC to rebuild IAT, unpacked target will detect debugger! It is because EC layer is present and activated with TLS callbacks. If we delete EC sections but leave TLS info, target will fail to work. So open dump with LordPE and set TLS table address and size to 0. Then fix imports with ImpREC and unpacking is finished :)
To make perfect dump, you first dump it, then erase last three sections (EC sections) and then fix imports. Such dump is around 60Kb while with EC code it is 300kb big.
6. Last words
Btw, about threads. They check for Olly, regmon, filemon, and maybe some other tools with FindWindowA (for olly) and EnumWindows/GetClassNameA (for rest). Some "EBX+" string exist in Olly which is also used for detection. Also, they use ReadProcessMemory for checking all processes. I didn't examne that, just throw quick look. You can disable threads by just patching CreateThread API. Oh, yeah, OutputDebugStringA is also used to crush Olly.
I will not include my dump for obvious reasons, neither ImpREC tree, but you can check how my IAT script works. I wrote it just for this tutorial, but I unpacked ExeCryptor 2.2.4 itself using this script.
In this tutorial we didn't see case when OEP is stolen. That part is indentical as in my first tutorial.
//----------- ExeCryptor 2.2.4 IAT (ASM,Delphi,BorlandC++) - by haggar -----------
cmp pointer,10000000 //Check is import placed in thunk, or redirection.
cmp pointer,0 //For delphi.
mov esp_ref,esp //Stack reference.
LABEL_02: //Trace some code without purpose.
LABEL_03: //Find referenced stack value.
mov temp,[temp] //Go to "Magic address".
cmp temp,025ff //SelfWriting import type? No need to fix it then.
cmp eax,10000000 //If EAX=!IMPORT, then it is a first type.
mov temp,addr //In this case EAX=IMPORT.
//---------------------- end of script -------------------------------