Thursday, August 11 2005 @ 05:32 PM CEST
Contributed by: haggar
Views: 988
Level : intermediate
PESpin v1.0 , 1.1 & 1.3 - manually unpacking
This is a detailed tutorial about manually unpacking a couple of PESpin versions.(discussing crypted parts, IAT redirection, stolen bytes, ...). Inside archive you'll find tutorial, targets, scripts and some more stuff.
This tutorial is about manually unpacking PESpin 1.x versions, to be more precisely about 1.0, 1.1 and 1.3 versions. However, this tutorial has some limits:
- PESpin v0.7 will not be described because there is no need for that. If you know how to unpack version 1.3, then you know how to unpack all previous versions.
- Version 1.2 is private version not available to public so I was not able to examne this one.
- PESpin v1.3 have option for advanced code redirection which is not available in public version so this tutorial will not cover that kind of protection (if you find target packed with that option, send me a PM on the forum).
- All unpacked targets in this tutorial runs only on machine on which they where unpacked. More about that read in the first tutorial.
Tutorial is for advanced beginners. You must have basic knowledge about PE file structure and how to use usuall unapcaking tools.
Get the attachment with all examples, scripts, ...here
Content:
1. Tools and useful stuff
2. Tutorial 1 - PESpin v1.0
3. Tutorial 2 - PESpin v1.1
4. Tutorial 3 - PESpin v1.3
5. Final words and thanks
1. Tools and useful stuff
- OllyDbg 1.10
- LordPE
- Hex Editor
In this archive you will find two folders: "PESpin scripts" and "PESpin signatures". First folder holds some useful scripts for unpacking PESpin. Those scripts are far away from perfect, but they could hep you a bit. More about them later, or just open them in notepad and read info. In the second folder you will find signatures for PEiD. If you scan with PEiD some file that is packed with some PEspin 1.x version, it will not recognize it properly. Just copy-paste those signatures to userdb.txt that is in PEiD folder, use external scan on packed file and PEiD will recognize real PESpin version. If you notice some bugs, mail or PM me.
There is some good papers that you should read if you want understand this tutorial better:
- Descryption of PE format by Michael J.O'Leary ; I think that this can be found on old BIW site.
- PORTABLE EXECUTABLE FILE FORMAT by Goppit ; find it here http://cracking.accessroot.com/ , it's 6 MB big file which is worth to download.
- Read my previous tutorial about unpacking PESpin 0.3 because new versions are improved previous.
2. Tutorial 1 - PESpin v1.0
In this version, PESpin has two new tricks; API bp check and code redirection. Grab first target packed1.0.exe and open it in Olly (set ignore ALL exceptions). Finding OEP and blocking imports redirection is similar as we done it in 0.3 version.
Protector has a check on API breakpoints so open "Executable Modules" window, select kernel32.dll and click "view names".
Find GetTickCount and double click on it. You should be in kernel32 now:
77E7A1EE> BA 0000FE7F MOV EDX,7FFE0000
77E7A1F3 8B02 MOV EAX,DWORD PTR DS:[EDX]
77E7A1F5 F762 04 MUL DWORD PTR DS:[EDX+4]
77E7A1F8 0FACD0 18 SHRD EAX,EDX,18
77E7A1FC C3 RETN
Place bp on RETN opcode and run targed. You will break on bp in kernel.
Remove bp and return to target code:
00409D17 8BD8 MOV EBX,EAX
00409D19 F7D3 NOT EBX
00409D1B 33D8 XOR EBX,EAX
00409D1D 43 INC EBX
...
...
Now scroll waaaaaay up and find this place:
00409109 F9 STC ;Can be CLC (E8hex).
0040910A 72 0D JB SHORT packed1_.00409119 ;Place bp here!!!
0040910C 8D85 0660271E LEA EAX,DWORD PTR SS:[EBP+1E276006]
00409112 2D 8417E71D SUB EAX,1DE71784
00409117 FFD0 CALL EAX ;Call to timer function.
00409119 EB 01 JMP SHORT packed1_.0040911C
This is the last place before jumping to OEP, just like in 0.3 version except here is little different.
If target is packed with timer option, instead of first STC opcode there will be CLC=E8hex.
In that case JB will not be executed and program flow will continue to CAL EAX where timer thread is created.
This place is same for all next PESpin versions. You just place bp on JB opcode (don't run target).
Scroll up again and take a look at this part of code:
0040905F JMP DWORD PTR SS:[ESP-4] ;Important jump, place bp here.
00409063 CALL FAR EB00:000003E8
...
...
...
...
0040909B ???
0040909C CALL packed1_.004089F2
004090A1 CALL packed1_.004090A9
Place bp at that jump and run target. When you break on it, you'll see that jump leads to 409022 address.
That jump is resposible for IAT redirecting.
If there are no IAT redirecting, that jump will jump to 40909C address to that CALL and skip all redirection procedure.
So to avoid IAT redirecting, you just patch all those bytes between jump (you can patch it too) and CALL at 40909C:
00409109 STC
0040910A JB SHORT packed1_.00409119 ;You're here!!!
0040910C LEA EAX,DWORD PTR SS:[EBP+1E276006]
00409112 SUB EAX,1DE71784
00409117 CALL EAX
00409119 JMP SHORT packed1_.0040911C
Now, if first opcode instead STC is CLC, patch all instructions at 0040910C,00409112 and 00409117 to prevent calling timer fuction.
Acctualy, you can always patch that instructiones, it will change nothing in that case. Then scroll down and search for POPAD opcode.
You won't see it so search for 61hex byte in some instruction. I should be here:
You're now at the last part of the protectors code. Here you can find the stolen OEP bytes mixed with junk code.
I didn't chose to remove OEP when I packed file so here is only junk to confuse you.
No matter if you have stolen OEP or not, you can always dump file when you reach first opcode after POPAD one and dumped file will normally work.
It doesn't matter that OEP is in the protectors code and that target execution starts from here.
But if you want you can always find stolen bytes, restore it in original place and dump from there.
Now, just trace untill you get to some jump that leads to the code section:
They lead to the address below 401000, that means to the PE header. That is not normal. I will explain this briefly:
PESpin will load all sections in memory, decrypt and do all that it needs to be done for unpacking. After that PEheader isn't anymore important for anything so it can be modyfied as you like - it won't affect the program in memory. PESpin will take adwantage of that fact and it will fill one part of PEheader with FFFFF... (and btw destroy part of the header which holds sections info). Then it will search in code section for PUSH xxxxxxxx or CALL xxxxxxxx (maybe JMP too) instructions because they are 5 bytes long. Then it will substitute that opcode with some JMP or CALL to PEheader where it will place original instruction and return to code section (if needed). The reason why instructions must be 5 bytes long is because they are easy to substitute with JMP opcode (it's 5 bytes long). Let's see two examples:
a) CALL example:
00401002 . E8 F1F1FFFF CALL packed1_.004001F8
This CALL points to PEheader. Right click on it and folow it. There you will find original "stolen code":
004001F8 -E9 09130000 JMP packed1_.00401506
So you need just to replace CALL 4001F8 with JMP 401506. As you see it's very simple.
b) JMP example:
0040100E .-E9 EBF1FFFF JMP packed1_.004001FE
Again, follow this jump to PEheader and you'll see stolen original code:
004001FE 68 F4204000 PUSH packed1_.004020F4
You gonna fix this as we fixed it in first example, instead of jump you place PUSH 4020F4 instruction.
Ok, you sow it and it's not hard. First tought would be "Why restoring that code, why just not leaving it there and just dump it?". Reason is that PEheader is destroyed (sections info is erased) and we doesn't have valid PE file anymore. If we paste original header from disk, then program will jump to it and it will not find redirected code, so result is crushing. Second tought is "But there is lot of redirected code, how to fix them all? Manually? I'm not crazy to waste whole night in front of computer!". Acctualy, there can be limited number of redirected code because size of PEheader, but that number is pretty big too. So to fix lot of that code we will just use/write Olly Script for automating job. That is not hard at all. It should be like this:
- First we need to find such JMP/CALL opcodes. Our srcipt must search for JMP/CALL which ends on F because it means that it jumps backword. So search for instructions with E9???????F or E8???????F bytes.
-Then we must check does it jumps to PEheader. Take our a) example. Take address of CALL, 401002 and take CALL value (four bytes after E8 in reversed order) FFFFF1F1. Now add those two values 401002+FFFFF1F1=4001F3, and if result is between 400000<=RESULT<401000 than you know that you are at right place.
- Than, using basic math you must restore original opcode. It can't be copy-pasted, it must be calculated. Basic hex math is needed here, little calculation and some time. After that you will have script that works perfect. If you don't (or don't know how to) do it your self, you can use mine "PESpin - Code Fixer.txt" (included in archive).
Now, if you have done all just like I wrote and then use script to fix redirected code, dump file. If you run it, you'll probably get error message that tels you how this app is not valid Win32 app. Don't worry. Open LordPE, go to options and under rebuilder check only "Validate PE" option. Close options, open rebuilder and open our dumped file in it. After validating of dump, run it and it will work just perfect ;) Good job! You have just unpacked PEspin v1.0.
This aproach will work for ASM and BC++ programs , but not for Delphi and MSVC++. Reason is different IAT redirection which OllyDump plugin isn't able to handle. That can be fixed by building new import section. That we will see in next tutorial.
But there is one reason why dumped file will probably work only on machine on which file was unpacked. If you take look at import jumps, you'll see that most of those jumps are absolute jumps that jump to specified loacation in memory. Because of that, dumped file will probably crush on another computer that has different versions of dll's. This can be fixed , but for bigger file it's impossible to do it manually. This is problem with all PESpin versions.
3. Tutorial 2 - PESpin v1.1
In this version everything is same as in 1.0 except this one brings new option - CRYPT and CLEAR MARKERS. What is that? Simple, read PESpin's help file:
Crypt markers: Code between encryption markers CRYPT_START and CRYPT_END is encrypted during process of file protection and when protected file is executed, code is being decrypted closely before it is used and is again encrypted right after use (runtime protection against dumping).
Clear markers: CLEAR_START and CLEAR_END markers offers ability to remove blocks of code after being executed.These macros should only be placed in regions of code that are executed once in your application.Any attempt to re-execute a block of code inside those macros will cause an exception in the protected application.
This is not a big problem for us. For our second target we will chose keygen.exe which is packed with all CLEAR and CRYPT options (thanks to detten ;). Procedure of finding OEP is exactly the same as in 1.0 version so there is no point to write it all again.
In a short:
exclude all exceptions, place bp to the end of GetTickCount API and run target, remove bp and return to target code.
But... We will meet one problem here that we'll had in all PESpin 1.x versions with Delphi and MSVC++ targets.
Problem is OllyDump cannot rebuild IAT. We will had to do it all manually. Because of that problem we'll had to know which one dll's our packed target is using.
Lets go!
First we will find what dll's keygen.exe needs. Place bp on LoadLibraryA in command line and run target.
You will break in weird place (maybe values are different at you):
Remove bp, open memory window and place "memory bp on access" on last section. That is one SFX. Now press Shift+F9 and you'll return to protector code (then remove mem bp):
What are we searching here? If you remember from last chapter, PESpin decrypts DLL name, load it in memory and then delete it's name. We are now at place where protector loads dll and we will find place where he erases DLL name. We will patch that erasing procedure so later we can see what DLL's are used. Trace with F7 untill you get here (that's lot of tracing):
0040D4F0 800B 00 OR BYTE PTR DS:[EBX],0
0040D4F3 74 0D JE SHORT keygen.0040D502
0040D4F5 8813 MOV BYTE PTR DS:[EBX],DL
0040D4F7 C1C2 04 ROL EDX,4
0040D4FA 75 01 JNZ SHORT keygen.0040D4FD
0040D4FC E8 43FF6424 CALL 24A5D444
That is place where DLL name is being erased. Get to this line
0040D4F5 8813 MOV BYTE PTR DS:[EBX],DL
and follow it in dump window. There you will see KERNEL32.DLL name. Patch this opcode MOV BYTE PTR DS:[EBX],DL and then just run target.
Our target will start, but that's what we want. Go in dump and there you will find all dll's that target uses:
Our target is small and it uses only kernel32.dll, user32.dll and gdi32.dll. Good!
Now we gonna restart target in Olly and find OEP. Place bp on last RETN in GetTickCount API, run target, remove bp and returne to code:
This is that place where timer is maybe called. Our target doesn't have that option enabled and we don't need to patch opcodes from 40D6DB to 40D6E6.
Ok, scroll down and find the POPUP opcode. You will find it a little obfuscated
0040D8FA
E8 610FC90F CALL 1009E860
so patch first byte to get clear picture
0040D8FA 90 NOP
0040D8FB 61 POPAD
After that POPAD starts stolen OEP code mixed with junk code. It seams that there is no stolen OEP bytes because jump that leads to code section jumps to 401000 at beginning of section:
Execute that jump and you'll land on OEP. Code there has crappy look even if you press Ctrl+A, so right click on it, select analysis and remove analysis from module.
Then you'll have better picture:
First we gonna check if there is stolen/redirected code to PE header section. Just run "PESpin - Code Fixer.txt" script. Check log window and you'll see that script has fixed 4A opcodes:
[Simple Code Fixing - number of stolen opcodes ] stolen = 0000004A
Ok,that's all we know from previous version of PESpin, but now we gonna met new trick - code decrypting and erasing. This part of code at very beginning is start of first decrypt procedure:
Trace with F8 to line that I showed abowe and you'll see that it changed to:
00401022 FF10 CALL DWORD PTR DS:[EAX]
That call will lead you to one procedure that will decrypt some code in our target and erase something. You can go ther by F7 if you would like to see what is happening there. It will just decrypt couple opcodes below POPFD opcode and erase all above. Instead F7 press F8 and you'll see changes:
00401045
E8 7DF1FFFF CALL keygen.004001C7 <--- to PE header!
Notice that some of new decrypted jumps and calls lead to PE header, so we must run script for fixing redirections again and then fill all code from 401000 to 401025 with NOP's:
We have fixed the first kind of decryption, CLEAR type. Now we gonna find and fix CRYPT one. Since target is small, you can scroll down and find two interesting calls easy:
You see that these two calls leads to protector section. Place bp on first call and run target. You will get dialog box which asks for name. Enter some name and press generate button. You will break on first bp. Remove bp and enter in call with F7, then press Ctrl+F9 to get to RETN opcode. Execute it and return to target code, press Ctrl+A and scroll up few lines:
Code is decrypted and now wee need to patch deryption calls. Patch first call wich decrypts (abowe picture). Now enter in second call at 40141A and trace to this line
003C004E AA STOS BYTE PTR ES:[EDI]
That line again encrypts decrypted code. So patch it to prevent encrypting. Then return to target code. You will notice that you are not imidiatley after encryptor call, but couple bytes further. That mean some bytes after call are junk, so patch encryptor call and all useless bytes.
Now that decrypted and patched code should look like this:
Now open OllyDump plugin, set OEP = 1000 and dump our unpacked target. If you try to run it, it will just crush. Reason is that OllyDump has failed to rebuild IAT (btw, if you get "Not valid PE file message" run LordPE's rebuilder to validate file). Scroll down to imports:
00401442 .-E9 1C84A777 JMP kernel32.ExitProcess
00401447 FF DB FF
00401448 $-E9 8C98A777 JMP kernel32.GetModuleHandleA
0040144D FF DB FF
0040144E $-E9 8B929477 JMP 77D4A6DE
00401453 FF DB FF
00401454 $-E9 87429677 JMP 77D656E0
00401459 FF DB FF
0040145A $-E9 85C59477 JMP 77D4D9E4
0040145F FF DB FF
00401460 $-E9 D3539677 JMP 77D66838
00401465 FF DB FF
00401466 $-E9 B4829477 JMP 77D4971F
0040146B FF DB FF
0040146C $-E9 72699477 JMP 77D47DE3
00401471 FF DB FF
00401472 $-E9 DB509677 JMP 77D66552
00401477 FF DB FF
00401478 $-E9 A04A9477 JMP 77D45F1D
0040147D FF DB FF
0040147E $-E9 636A9477 JMP 77D47EE6
00401483 FF DB FF
00401484 $-E9 5B0CC07E JMP 7F0020E4
00401489 FF DB FF
0040148A $-E9 F308C07E JMP 7F001D82
0040148F FF DB FF
00401490 $-E9 BE09C07E JMP 7F001E53
00401495 FF DB FF
00401496 $-E9 3C08C07E JMP 7F001CD7
What happened?
You see that required dll's like USER32.DLL and GDI32.DLl are not loaded in memory. As you can see, imports jumps jump at properly place where API's should be, but because dll are not there, those jumps leads to nowhere.
OllyDump plugin has added one new section .newIID which should hold IAT but if you take look at the dump of that section you'll see that it is totally empty.
How we gonna fix this?
There is way. Since all import jumps point to correct API's, we need just to load required dll's and that we can do by manually making basic smallest IAT. That IAT will hold every dll with just one API per each DLL and that API even doesn't matter.
It's important only that windows loader loads required dll's.
First you must know how basic import section must look. Import section must have following parts:
1) Thunks - these are groups of 4 bytes. That 4 bytes are relative address (from image base) that point to hints. There is one thunk per each dll. After PE file is loaded in memory, thunks are filled with API addresses. Every thunk must end with 4 zero bytes.
2) IMAGE_IMPORT_DESCRIPTOR - this is group of 5x4 bytes. There is one descriptor per each dll. Every 4 bytes of descriptor holds specific information:
1. First 4 bytes represent RVA to the hint array.
2. Second 4 bytes represent TimeDateStamp for dll. This is not important and it can be filled with zeros.
3. Third 4 bytes represent some ForwarderChain which is not important and it can be filled with zeros too. This ForwarderChain is something obsolete.
4. Forth 4 bytes are address that point to RVA of dll ASCII name in import section.
5. The last 4 bytes points to RVA of thunks.
3) Then we have one more IMAGE_IMPORT_DESCRIPTOR filled with zeros (null terminating one).
4) Hint arrays - these arrays holds RVA's of hints. This is acctually same thing as thunks, but they will not be filled with import addresses like thunks will.
5) Hint - it is a two byte value that is hint for windows loader where it should look first for API. If hint doesn't work (because newer/different version of dll) loader will perform binary search for ASCII API name. Hint is not neccessery and we can just fill it with zeros, just like some compilers do.
6) API name - it is ASCII name of API function. It's purpose is that windows loader can perform binary search for API. It's length isn't determined, but last byte must be 0 (null terminator).
7) DLL name - same thing as API name but for DLL and it must end with two zero bytes.
That is basic import section. We will make our import section in .newIID section which is added by OllyDump. We have three DLL's which need to be loaded, so we'll have three thunks, three decriptors + one zero filled, three hint arrays, and three hint-api-dll names. Open our dumped file with LordPE's PE editor and click on sections. You'll see that RawOffset of .newIID section is 10000. Close LordPE and open dumped file in hex editor. We need to organise place for IAT. Next picture shows how did I reserve place and for what. Note that all values and chars are just symbolic to give you (and me) better view how section should look:
thnk1... are thunks, then with numbers 1,2,3,0 I've fill 1. 2. 3. and null one Image_Import_Descriptors. Hint arrays are that arr1... Hint, API and DLL place cannot be reserved because I don't know length of API and DLL names before I search them.
This is first area that I will fill with data. We said that we need kernel32.dll, user32.dll and gdi32.dll, and one API per each dll. I will chose first API that I know name.
After filling that area, I have this picture:
Now we need to fill all other data; thunks, descryptors and arrays. All that data needs to be filled with RVA's so open this file in Olly and see dump of .newIID section. From that you can easy read all RVA's or even make changes in Olly. Next picture shows how arrays must be filled for first dll , KERNEL32.DLL:
After you fill other arrays, you should have this picture:
That's it, import section is done but you have save these changes to our file if you have done all this in Olly. But here is no option for save to file, so we ganna do that different.
Go in CPU window to 410000 address:
Select all "opcodes" (these are not opcodes, but our import section) from 410000 to 4100CF, right click, copy to executable -> selection, then in new window select save file.
Save it as our dump.exe
Now we need only to tell windows where is our IAT and how big it is.
What is IAT? IAT isn't whole imports section, but only IMAGE_IMPORT_DESCRIPTORS.
First descriptor starts at RVA 410018==>RawA 10018.
It's size is three descryptors + last one zero filled = 5*4*4 = 50h.
Open dump.exe in LeordPE's PE editor, click on directories and there change ImportTable RVA and Size to 10018 and 50.
Save changes and run dump.exe.
Is it working?
Mine works fine and yours should too, unless you didn't made some mistake.
Thats all for this version of PESpin. It's hard to fix IAT manually because if you have lot of dll's than you have big job to do and you can make mistake on any step.
There are some tools that claim that they can build import section, but non of them didn't work for me.
4. Tutorial 3 - PESpin v1.3
Unpacking PESpin v1.3 is same as unpacking 1.1 version except 1.3 version has option for advanced code redirection.
Unfortunatley for us that option isn't available in the public version so I couldn't use it when packing our target. Target file is Crackme05.exe, which is a Delphi 3 app. Delphi and VC++ programs have different import redirection which make it more difficult to repair.
Grab our target and load it in Olly.
Find place where stolen OEP starts , but also fix import redirection on your way. Let me help you a bit; import jump is at 4137D9, timer check is at 4138D1 and stolen OEP you'll find at:
But notice how the imports look.
Usually import is one absolute jump that jumps to API, but in this case PESpin didn't change JMP DWORD[xxxxxxxx] to JMP xxxxxxx like he did with ASM and BC++ programs.
Instead he redirected DWORD[xxxxxxxx] which was in .idata import section to it's own section. OllyDump cannot rebuild this kind of redirection, but there is way around it.
As I said, find where the stolen OEP is , fix that redirection jump before and use script for stolen code.
Now just dump it with OllyDump plugin. My dumped file has lost icon and it crashes when I run it.
Thats good ;)!?
Open it in another Olly and check some imports (don't close first Olly).
You will be at stolen OEP beginning so trace with F7 until you enter to crackme code and to the place where first import should be:
As you can see, our import is bad just like most of the restof them because they all point to 00000000 address. There are just couple good ones in whole dump.
But there is one good thing, all required dll's are loaded in memory:
What's good there if we don't have imports? It's good because we can change all import jumps to absolute jumps that jump directly to API. We will done that in our first/original crackme and just copy-paste all code section and dumped file will work. But why didn't I do that the first time? Because OllyDump isn't able to find DLL's then. To change jumps you can use my script which I made for that "PESpin v1.x - Delphi & VC++.txt".
You can do this manually, but there is lot of jumps so this script will do the job for you. But maybe/probably it will not change all jumps and it may *censored* some of them.
For example: if you want that this script finds all import jumps, you'll need to select all code from 401000 to 40104D and NOP it (because it some code can confuse Olly and script).
Then run script and it will change all jumps.
Note that you will have to run script several times for bigger files (~bigger than 500kb). Then after script has fixed all jumps
select all code from 401054 to 407D10 and binary copy it, paste it to our dumped file. Save changes and run dump, it works! If your dump is not working, then open it in Olly and try to find what did went wrong. As usuall, dumped file will only work on machine with same version of operating system because absolute imports.
There it is, that was unpacking of PESpin 1.3. We didn't bothered with stolen OEP bytes because dump will work fine in this way too.
5. Final words and thanks
PESpin is solid protector which cannot be unpacked by some totally noob.
Also some programing knowledge is required if you want to make dump that will work on any OS because way how PESpin handles imports. For second example I was searching for tools that can add imports to some section, but I just couldn't find nothing that will done that.
With this tutorial we sow how all PESpin versions can be unpacked, except we didn't fully explain 1.3 version because public version doesn't have Advanced Code Redirection option enabled. If you find some target - PM me.
Also, the game is not over - new PESpin is on it's way. Check version 1.3 which is packed with 1.3beta2 (as cyberbob sad on one forum) and you'll notice big differences in behaviour of packer.
Sorry for bugs, errors and gramatical mistakes.
Big greets goes to whole BIW crew and all BIW users. Special thanks to detten who made unpackme for 1.1 version. Of course, thanks to you reader and I hope that you will learn something from this text. I learned a lot while writting it.