Tuesday, July 26 2005 @ 09:14 PM CEST Contributed by: haggar Views: 4659
Level : beginner
PESpin v0.3 has some nice features; good IAT redirection with some emulation API opcodes and stolen OEP are it's best weapon. It has also some antidebug tricks based on exceptions, but Olly is imune on that, it checks for SoftICE and NTICE, it has couple CRC checks, and it has password locking and time limit.
This is older version of PESpin, but I have decide to start with it, tought it will be easier later to try new versions. Tutorial is for beginners who have some experience with unpacking protectors, also you must be familiar with tools (Olly, ImpREc, LordPE).
PESpin v0.3 has some nice features; good IAT redirection with some emulation API opcodes and stolen OEP are it's best weapon. It has also some antidebug tricks based on exceptions, but Olly is imune on that, it checks for SoftICE and NTICE, it has couple CRC checks, and it has password locking and time limit, but that's not problem. Also there is some small diferences between packed files compiled with different compilers (VC++,Borland C++, Delphi, VB, ASM...).
1. ANALYZING PESpin CODE
Grab Olly, exclude all exceptions in options and load target, cruehead's CRACKME3.EXE which I packed with PESpin. Scroll little down and you'll see two decrypting loops that decrypt PESpin code section:
00407127 XOR BYTE PTR DS:[ECX+EDI],BL
0040712A DEC BL
0040712C LOOPD SHORT CRACKME3.00407127 ;First loop.
0040712E PUSH 13C
00407133 POP ECX
00407134 LEA EDI,DWORD PTR SS:[EBP+4036B6]
0040713A ROR BYTE PTR DS:[ECX+EDI],2
0040713E LOOPD SHORT CRACKME3.0040713A ;Second loop.
00407140 CALL CRACKME3.00407147 ;Place bp here to pass them.
Pass those loops and trace to here:
00408138 MOV EDI,DWORD PTR SS:[ESP+20]
0040813C AND EDI,FFFF0000
00408142 CMP WORD PTR DS:[EDI],5A4D
00408147 JNZ SHORT CRACKME3.0040815A
00408149 MOVZX EDX,WORD PTR DS:[EDI+3C]
0040814D TEST DX,0F800
00408152 JNZ SHORT CRACKME3.0040815A
00408154 CMP EDI,DWORD PTR DS:[EDX+EDI+34]
00408158 JE SHORT CRACKME3.00408162
0040815A SUB EDI,10000
00408160 JMP SHORT CRACKME3.00408142
00408162 XCHG EAX,EDI
00408163 PUSH CRACKME3.00402CFC
00408168 PUSH EAX
00408169 XCHG DWORD PTR SS:[EBP+402CED],EAX
0040816F ADD DWORD PTR SS:[ESP+4],EBP
00408173 LEA EAX,DWORD PTR SS:[EBP+EB8382F8]
00408179 LEA EAX,DWORD PTR DS:[EAX+14BCAABD]
0040817F CALL EAX
Here starts procedure that will find all API's in kernel that PESpin needs for it's work. This first part seams that want be sure that it is in kernel32.dll. Then you'll enter in CALL EAX where PESpin search for API's in some weird way. For us is important which are those API's. Trace to here and you'll see that here API addresses are stored to some location:
Then PESpin will make some exceptions which purpose is to detect debugger, but olly doesn't fall on this so you do this: press Shift+F9 4. times and then, place memory bp on PESpin section press Shift+F9 untill you break on mem bp. Code that we skiped doesnt hold anything important for our unpacking, just some exceptions tricks and one decryption loop. You should be here:
There is called GetModuleFileNameA API that searchin path for our packed file, then CreateFile opens our file, GetFileSize, VirtualAlloc will reserve enough place in memory for (starting from 390000 on my computer) loading that file, ReadFile will load it to that allocated place, CloseHandle. Than you will enter to CRC calculating routine which calculates CRC of whole file:
00408864 PUSH ECX
00408865 OR DL,4
00408868 INC EDI
00408869 XOR AH,BYTE PTR DS:[EDI]
0040886B SHR EAX,3
0040886E XOR AL,BH
00408870 ADD EAX,7801A018
00408875 XOR EAX,EBX
00408877 MOV CL,BL
00408879 ROR EAX,CL
0040887B XCHG EAX,EBX
0040887C DEC EDX
0040887D JNZ SHORT CRACKME3.0040886E
0040887F POP ECX
00408880 LOOPD SHORT CRACKME3.00408864
00408882 XCHG EAX,EBX
00408883 POP EBX
00408884 POP EDX
00408885 RETN
Then that CRC is substracted from real one which is written in file and stored to it's place:
00407397 SUB DWORD PTR SS:[EBP+403827],EAX
If you open crackme in hex editor or take look a in olly dump last section, you can find where is written reall CRC. Than is caled VirtualFree. After that you will get on one exception, put mem bp on .taz section and press Shift+F9 untill you break here:
00408240 NOP
00408241 NOP
00408242 XOR EBX,EBX
00408244 POP DWORD PTR FS:[EBX]
00408247 POP EBX
00408248 SUB EBX,16
0040824E JMP SHORT CRACKME3.00408251
....
....
Remove bp and trace in to:
00408251 CMP BYTE PTR DS:[EBX],0CC
00408254 JNZ SHORT CRACKME3.00408261
00408256 AND ESP,0FFFF
0040825C CALL CRACKME3.0040827B
00408261 JMP EBX
Here packer is checking is breakpoint placed on [ebx]=4073e0 address. Pass that check and you'll get to that address, then trace and trace untill you get here:
0040745A MOVZX ECX,WORD PTR SS:[EBP+402CCF]
00407461 MOV EDX,DWORD PTR SS:[EBP+402CD5]
00407467 ADD EDX,0F8
0040746D MOV EBX,DWORD PTR SS:[EBP+403817]
00407473 XOR EAX,EAX
00407475 PUSH ECX
00407476 BT EBX,EAX
00407479 JNB SHORT CRACKME3.0040749F
0040747B PUSH EDX
0040747C MOV EDI,DWORD PTR DS:[EDX+C]
0040747F ADD EDI,DWORD PTR SS:[EBP+402CCB]
00407485 MOV ECX,DWORD PTR DS:[EDX+10]
00407488 MOV EDX,DWORD PTR SS:[EBP+403827] ;CRC value is taken.
0040748E SHR EDX,1
00407490 JB SHORT CRACKME3.00407498
00407492 XOR EDX,ED43AF32
00407498 XOR BYTE PTR DS:[EDI],DL ;Decrypting section.
0040749A INC EDI
0040749B DEC ECX
0040749C LOOPD SHORT CRACKME3.0040748E
0040749E POP EDX
0040749F INC EAX
004074A0 ADD EDX,28
004074A3 POP ECX
004074A4 LOOPD SHORT CRACKME3.00407475
004074A6 OR DWORD PTR SS:[EBP+4036B0],0
004074AD JE SHORT CRACKME3.004074BC
004074AF LEA EAX,DWORD PTR SS:[EBP+403524]
004074B5 SUB EAX,3D1
004074BA CALL EAX
OK, we see that it takes that CRC value and then it decrypts .code section with it. Hmm, maybe that value isn't CRC at all, but some key that purpose is to decrypt sections? And maybe is both? I think that it's some key that decrypts sectiones. Pass this check and all sections will be decrypted , but that's only one layer. Trace further and you'll get to SOFTICE check, procedure starts at 408620 and ends at 40867D. It uses CreateFileA to search for SICE and NTICE files and if finds them, program will terminate. Tracing further you'll find one more decryptor loop at 4074dc and one more at 4088B9, not much interesting for us. Let we speed up a little and place bp on VirtualAlloc in command bar, then press Shift+F9 until we break on it. Remove it and keep tracing untill you get here
This is unpacking procedure which will unpack or decrypt sections. It goes like this: first was allocated enough memory, then section is unpacked there, than original section is erased and unpacked is copied from alocated memory to original section, then VirtuallFree is called and VirtualProtect. Place bp on last RETN and run 2 times untill packed section are unpacked, remove bp , put bp on VirtualFree, Shift+F9 untill you break on it, return to user code and you should be here:
We will not enter in imports redirection procedure yet. I will talk about that later so trace further with F7, you'll get to VirtualProtect again, just trace until you get to this place that looks like dead end street:
Packer uses two GetTickCount calls, why I don't know but this will be usefull information for us. Press Shift+F9 once to get to INT3 exception. This is another debugger check, place mem bp on .taz section, run and you're here (right below):
00408A27 JMP SHORT CRACKME3.00408A2A
Now trace and trace alot untill you enter to VirtualAlloc, exit it, trace and trace ..... and you'll get here where packer decrypts DLL names:
004086B3 JE SHORT CRACKME3.004086BA
004086B5 NOT BYTE PTR DS:[EDI] ;This decrypts.
004086B7 INC EDI
004086B8 JMP SHORT CRACKME3.004086B0
004086BA POP EDI
004086BB RETN
Trace and you'll be thrown to LoadLibraryA. Place mem bp on .taz section and press Shift+F9 to get out from there:
You know what to do :), trace again and you will get to DLL name erasing:
00407A8A OR BYTE PTR DS:[EBX],0
00407A8D JE SHORT CRACKME3.00407A9C
00407A8F MOV BYTE PTR DS:[EBX],DL
00407A91 ROL EDX,4
When erasing is over trace to this interesting place
004077D4 CMP BYTE PTR SS:[EBP+4026E5],0CC
004077DB JNZ SHORT CRACKME3.004077E2
004077DD JE SHORT CRACKME3.004077E0
Here it checks is there bp placed on that [EBP+xxxxx] address which is here:
00407BA6 STC
00407BA7 JB SHORT CRACKME3.00407BAE
00407BA9 CALL CRACKME3.0040867E
00407BAE JMP SHORT CRACKME3.00407BB1
That is important place since PESpin is checking it. This place is very close to the end of protector code. That call between two jumps is optional thing; if you chose in PESpin options "Close program after: xxx minutes" that CALL will be executed and there will start thread that will close program after specified time. After this, protector will start with IAT redirection in that big procedure that I mention earlier. Then we must find stolen OEP bytes. That is not far away from here. We must find one POPAD opcode that is last PESpin code before stolen bytes and jump to real OEP. Scroll down:
Since this place has obfuscated code, Olly doesn't disply it right, but you will see one 61 byte at 407C0B. 61hex=POPAD. Binary edit (NOP one byte) that line of code to make it clear:
Now place bp on POPAD and run crackme. Trace and you'll enter in code section at some 401xxx address, but you will probably be confused by the look of code. Than you will enter to kernel32 and then return to packer code. That is becuse PESpin stole couple first instructions and first instructions of this crackme are some API calls so here is PESpin still interfear with crackme. If you would like, you can trace further and enter to IAT relocations. There you can see how PESpin has mangled imports.
We now know what PESpin is doing and now we gonna unpack our target in a fast way, fix IAT and find stolen OEP.
2. unpacking, FIXING IAT AND STOLEN OEP
PESpin , when packing our file, has already changed IAT and OEP of our packed program. But IAT relocation will be done on the fly, in the memory and we can prevent that to happened. GetTickCount is one of last API that are called by protector code and it's very useful since it throw us at most interesting parts, so place bp on it in command bar and run crackme twice (ignore all exceptions in olly before, we don't need them anymore). When you break second time in kernel, remove bp and return to:
Maybe this is not the best and the most clever solution, but I couldn't find place to patch or change only few instructions. There is one jump which can be redirected, but I had some errors while testing bigger files (Mozilla ~7 MB,~2000 Imports).
IAT redirection is fixed and now we can go to OEP problem. Run crackme and you will break on our bp:
Trace to PUSH 0. Below POPAD is first stolen opcode of our OEP. If on that place is some jump to code section, that means that there are no stolen bytes. Our example has them. As I sad, that is the place where stolen OEP is and we can find our bytes now, but guess what? We don't need to do that! If you like, you can dump file now and fix IAT with ImpREC and file is properly unpacked and it is not protected anymore. Fact that it starts from protectors section doesn't change anything. But if you would like to be Mr. Perfect, we will find stolen bytes and restore them. It's easy!
Patch false bytes to get correct picture (only one byte in this example):
00407C0C 61 POPAD
00407C0D 6A 00 PUSH 0 ;Stolen code.
00407C0F EB 01 JMP SHORT CRACKME3.00407C12 ;Junk jump.
00407C11 90 NOP ;Patched byte, junk too.
00407C12 E8 6D98FFFF CALL CRACKME3.00401484 ;Stolen code.
00407C17 -E9 EB93FFFF JMP CRACKME3.00401007 ;Jump to false OEP, go to it.
Write those stolen bytes and jump to false OEP, scroll up and restore bytes instead zeroes, dump file with ollydump, OEP is 401000=1000. Run file and it works fine! Yeah ;)
3. ISSUES
-Delphi programs dump with OllyDump plugin (do not check repair IAT option) and repair IAT with ImpREC. If ImpREC cannot find imports at real OEP, set OEP to 1000 and then try. When it finds imports, restore real OEP and then fix dump. This common issue with ImpREC and Delphi packed programs with lot of packers.
-ASM and Borland C++ programs can be dumped and fixed with OllyDump plugin. You can use ImpREc if you like or have problems with plugin in-built fixer.
-MSVC++ , dump with OllyDump fix IAT with it or with ImpREC.
-MSVB, use what you want.
LordPE cannot dump files. Why, I don't know.
I have included OllyScript for PESpin 0.3 that fix IAT and finds stolen bytes. Works pretty well ,although some Delphi file can crush after it ;)
Note: If you have some target that PEiD sad it's PESpin 0.3 protected, and you cannot unpack it with this method, that's because that file is packed with different version of PESpin no mater what PEiD sad.
4. FINAL WORDS
Huh, this tutorial was exhausting to write. Sorry for errors, mistakes and bad grammar. Included abexcrackme2.exe is for your homework, it's packed with timer (it will autoclose in 2 minutes) and password. Password is 123456. Unpack it!
Good by, and good night. zZZzzzzZZZZzzZZZZzzZZZzzZZzzZZzzzzzzzz.......