首页
社区
课程
招聘
[转帖]pespin1.0,1.1,1.3脱壳
发表于: 2005-12-2 18:05 6366

[转帖]pespin1.0,1.1,1.3脱壳

2005-12-2 18:05
6366
源网址:http://www.reversing.be/article.php?story=20050811173217736

PESpin v1.0 , 1.1 & 1.3 - manually unpacking
          
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.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PESpin v1.x -manually unpacking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Hi and welcome to my new unpacking tutorial.

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:

0040905F NOP
00409060 NOP
...
...
...
0040909A NOP
0040909B NOP
0040909C CALL packed1_.004089F2
004090A1 CALL packed1_.004090A9
004090A6 JMP SHORT packed1_.004090AC

Now prss F9 again to land on previous bp:

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:

00409278   E8 616A00EB      CALL EB40FCDE

Patch first byte, place bp on POPAD and run:

00409278  90               NOP                           ;Patch this byte.
00409279  61               POPAD                         ;Place bp here.
0040927A  C1E2 F3          SHL EDX,0F3
0040927D  23D0             AND EDX,EAX
0040927F  0FC1D1           XADD ECX,EDX
00409282  BA 69533D94      MOV EDX,943D5369
00409287  69C8 262C4AF2    IMUL ECX,EAX,F24A2C26
0040928D  11C2             ADC EDX,EAX
0040928F  0FBDD0           BSR EDX,EAX
00409292  C7C2 BDCC6DB9    MOV EDX,B96DCCBD
00409298  42               INC EDX
00409299  FFC2             INC EDX
0040929B  C1D2 83          RCL EDX,83
0040929E  0FBCC8           BSF ECX,EAX
004092A1  23D0             AND EDX,EAX
004092A3  39C2             CMP EDX,EAX
004092A5  81E9 B576A23F    SUB ECX,3FA276B5
004092AB  EB 01            JMP SHORT packed1_.004092AE
004092AD  E7 0F            OUT 0F,EAX
004092AF  AF               SCAS DWORD PTR ES:[EDI]
004092B0  C8 0FB7D0        ENTER 0B70F,0D0
004092B4  C7C1 0EE985D6    MOV ECX,D685E90E
004092BA  F3:              PREFIX REP:
004092BB  F7C1 1E8ABDAB    TEST ECX,ABBD8A1E
004092C1 -E9 3A7DFFFF      JMP packed1_.00401000

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:

004092C1 -E9 3A7DFFFF      JMP packed1_.00401000

In this case jump leads straight to OEP:

00401000  . 6A 00          PUSH 0
00401002  . E8 F1F1FFFF    CALL packed1_.004001F8
00401007  . A3 CA204000    MOV DWORD PTR DS:[4020CA],EAX
0040100C  . 6A 00          PUSH 0
0040100E  .-E9 EBF1FFFF    JMP packed1_.004001FE

00401013  . E8 F1F1FFFF    CALL packed1_.00400209
00401018  . 0BC0           OR EAX,EAX
0040101A  . 74 01          JE SHORT packed1_.0040101D
0040101C  . C3             RETN
0040101D  > C705 64204000 >MOV DWORD PTR DS:[402064],4003
00401027  . C705 68204000 >MOV DWORD PTR DS:[402068],packed1_.00401>

If you dump file, it will not work. Reason is redirected code. Note where some CALLs and JMPs leads:

00401002 CALL packed1_.004001F8
...
0040100E JMP packed1_.004001FE

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):

77ED6FC4>-E9 1860FB42      JMP BAE8CFE1
77ED6FC9  90               NOP
77ED6FCA  90               NOP
77ED6FCB  90               NOP
77ED6FCC  90               NOP
77ED6FCD  90               NOP
77ED6FCE  90               NOP
77ED6FCF  90               NOP
77ED6FD0  90               NOP
77ED6FD1  90               NOP
77ED6FD2  90               NOP
77ED6FD3>-E9 7D61FB42      JMP BAE8D155
77ED6FD8  90               NOP
77ED6FD9  90               NOP
77ED6FDA  90               NOP
77ED6FDB  90               NOP
77ED6FDC  90               NOP
77ED6FDD  90               NOP
77ED6FDE  90               NOP
77ED6FDF  90               NOP

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):

0040D491  85C0             TEST EAX,EAX
0040D493  0F84 3F090000    JE keygen.0040DDD8
0040D499  E8 01000000      CALL keygen.0040D49F
0040D49E  FF59 50          CALL FAR FWORD PTR DS:[ECX+50]
0040D4A1  51               PUSH ECX
0040D4A2  55               PUSH EBP
0040D4A3  810424 12374000  ADD DWORD PTR SS:[ESP],keygen.00403712
0040D4AA  814424 04 220000>ADD DWORD PTR SS:[ESP+4],22

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

0040D501  FC               CLD
0040D502  93               XCHG EAX,EBX
0040D503  8B56 10          MOV EDX,DWORD PTR DS:[ESI+10]

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:

00402102 4B 45 52 4E 45 4C 33 32 2E 44 4C 4C 00 00 F7 00  KERNEL32.DLL..÷.
00402112 00 00 00 00 00 00 00 00 00 00 C4 00 00 00 00 00  ..........Ä.....
00402122 00 00 00 00 00 00 00 00 00 00 00 00 C5 00 00 00  ............Ĺ...
00402132 00 00 00 00 00 00 00 00 CC 00 00 00 00 00 00 00  ........Ě.......
00402142 00 00 00 00 00 00 CC 00 00 00 00 00 00 00 00 00  ......Ě.........

00402152 00 00 D2 00 00 00 00 00 00 00 00 00 00 00 00 00  ..Ň.............
00402162 00 00 00 00 D3 00 00 00 00 00 00 00 00 00 00 00  ....Ó...........
00402172 00 00 00 00 00 00 00 00 00 00 D3 00 00 00 00 00  ..........Ó.....
00402182 00 00 00 00 00 00 00 00 00 00 D3 00 00 00 00 00  ..........Ó.....
00402192 00 00 00 00 00 00 00 00 00 00 00 00 55 53 45 52  ............USER
004021A2 33 32 2E 44 4C 4C 00 00 C3 00 00 00 00 00 00 00  32.DLL..Ă.......

004021B2 00 00 00 00 00 00 00 00 00 00 00 00 D3 00 00 00  ............Ó...
004021C2 00 00 00 00 00 00 00 00 00 00 D3 00 00 00 00 00  ..........Ó.....
004021D2 00 00 00 00 00 00 D3 00 00 00 00 00 00 00 00 00  ......Ó.........
004021E2 00 00 00 00 00 00 47 44 49 33 32 2E 44 4C 4C 00  ......GDI32.DLL.
004021F2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

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:

0040E4D8  8BD8             MOV EBX,EAX
0040E4DA  F7D3             NOT EBX
0040E4DC  33D8             XOR EBX,EAX
0040E4DE  43               INC EBX
0040E4DF  68 87000000      PUSH 87
0040E4E4  59               POP ECX
0040E4E5  66:35 4C50       XOR AX,504C
0040E4E9  66:05 8911       ADD AX,1189
0040E4ED  AA               STOS BYTE PTR ES:[EDI]
0040E4EE  EB 01            JMP SHORT keygen.0040E4F1
0040E4F0  68 499CC12C      PUSH 2CC19C49
0040E4F5  24 06            AND AL,6

Find the jump that leads to redirection procedure and patch all bytes between it and good call:

0040D5DF  FF6424 FC        JMP DWORD PTR SS:[ESP-4]  <---Jump!!!
...
...                                                  <--- Patch all these opcodes!!!
...
0040D643  E8 C4F6FFFF      CALL keygen.0040CD0C      <---Good call!!!
0040D648  E8 03000000      CALL keygen.0040D650

Then scroll little down to our well known place:
BR>

0040D6D7  B0 F9            MOV AL,0F9
0040D6D9  72 3F            JB SHORT keygen.0040D71A
0040D6DB  8D85 F469271E    LEA EAX,DWORD PTR SS:[EBP+1E2769F4]
0040D6E1  2D 8417E71D      SUB EAX,1DE71784
0040D6E6  FFD0             CALL EAX
0040D6E8  EB 02            JMP SHORT keygen.0040D6EC

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:

0040D8FC  0FC9             BSWAP ECX
0040D8FE  0FACC2 13        SHRD EDX,EAX,13
...
...
...
0040D947 -E9 B436FFFF      JMP keygen.00401000 <---Jump toOEP!!!

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:

00401000  9C               PUSHFD
00401001  60               PUSHAD
00401002  B9 B5381023      MOV ECX,231038B5
00401007  BF 7896AA00      MOV EDI,0AA9678
0040100C  81E9 91381023    SUB ECX,23103891
00401012  B8 5F1360C2      MOV EAX,C260135F
...
...

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:

00401000  9C               PUSHFD
00401001  60               PUSHAD
00401002  B9 B5381023      MOV ECX,231038B5
00401007  BF 7896AA00      MOV EDI,0AA9678
0040100C  81E9 91381023    SUB ECX,23103891
00401012  B8 5F1360C2      MOV EAX,C260135F
00401017  05 DCE1E03D      ADD EAX,3DE0E1DC
0040101C  FF0D 22104000    DEC DWORD PTR DS:[401022]
00401022  0011             ADD BYTE PTR DS:[ECX],DL   <---Trace to this line!!!
00401024  61               POPAD
00401025  9D               POPFD

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:

00401000  0000             ADD BYTE PTR DS:[EAX],AL
00401002  0000             ADD BYTE PTR DS:[EAX],AL
00401004  0000             ADD BYTE PTR DS:[EAX],AL
00401006  0000             ADD BYTE PTR DS:[EAX],AL
00401008  0000             ADD BYTE PTR DS:[EAX],AL
0040100A  0000             ADD BYTE PTR DS:[EAX],AL
0040100C  0000             ADD BYTE PTR DS:[EAX],AL
0040100E  0000             ADD BYTE PTR DS:[EAX],AL
00401010  0000             ADD BYTE PTR DS:[EAX],AL
00401012  0000             ADD BYTE PTR DS:[EAX],AL
00401014  0000             ADD BYTE PTR DS:[EAX],AL
00401016  0000             ADD BYTE PTR DS:[EAX],AL
00401018  0000             ADD BYTE PTR DS:[EAX],AL
0040101A  0000             ADD BYTE PTR DS:[EAX],AL
0040101C  0000             ADD BYTE PTR DS:[EAX],AL
0040101E  0000             ADD BYTE PTR DS:[EAX],AL
00401020  0000             ADD BYTE PTR DS:[EAX],AL
00401022  0000             ADD BYTE PTR DS:[EAX],AL
00401024  61               POPAD
00401025  9D               POPFD
00401026  6A 00            PUSH 0
00401028  E8 83F1FFFF      CALL keygen.004001B0
0040102D  A3 E4304000      MOV DWORD PTR DS:[4030E4],EAX
00401032  6A 00            PUSH 0
00401034 -E9 7DF1FFFF      JMP keygen.004001B6  <--- to PE header!
00401039  6A 00            PUSH 0
0040103B  6A 01            PUSH 1
0040103D  50               PUSH EAX
0040103E  E8 7EF1FFFF      CALL keygen.004001C1 <--- to PE header!
00401043
  6A 00            PUSH 0

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:

00401000  . 90             NOP
...
...
...
00401024  . 90             NOP
00401025  . 90             NOP
00401026  . 6A 00          PUSH 0
00401028  . E8 1B040000    CALL keygen.00401448
0040102D  . A3 E4304000    MOV DWORD PTR DS:[4030E4],EAX
00401032  . 6A 00          PUSH 0
00401034  . 68 7F104000    PUSH keygen.0040107F
00401039  . 6A 00          PUSH 0
0040103B  . 6A 01          PUSH 1
0040103D  . 50             PUSH EAX
0040103E  . E8 11040000    CALL keygen.00401454
00401043  . 6A 00          PUSH 0
00401045  . E8 F8030000    CALL keygen.00401442
0040104A  . EB 0B          JMP SHORT keygen.00401057
0040104C    EB             DB EB

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:

004013D1  $ FF15 5FF54000  CALL DWORD PTR DS:[40F55F]
004013D7  . 71 27          JNO SHORT keygen.00401400
004013D9  . 0B3E           OR EDI,DWORD PTR DS:[ESI]
...
...
...
0040141A  . FF15 83F54000  CALL DWORD PTR DS:[40F583]
00401420  . 27             DAA
00401421  . DCE5           FSUBR ST(5),ST
00401423  . 3AFF           CMP BH,BH
00401425  .^75 F8          JNZ SHORT keygen.0040141F

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:

004013D1  $ FF15 5FF54000  CALL DWORD PTR DS:[40F55F] <--NOP this!!!
004013D7  . 71 27          JNO SHORT keygen.00401400  <--NOP this!!!
004013D9  . 0B3E           OR EDI,DWORD PTR DS:[ESI]  <--NOP this!!!
004013DB  . 55             PUSH EBP
004013DC  . 8BEC           MOV EBP,ESP
004013DE  . 83C4 F4        ADD ESP,-0C
...
...
...
00401415  . D9E1           FABS
00401417  . DD5D F4        FSTP QWORD PTR SS:[EBP-C]
0040141A  . FF15 83F54000  CALL DWORD PTR DS:[40F583]
00401420  . 27             DAA
00401421  . DCE5           FSUBR ST(5),ST
00401423  . 3AFF           CMP BH,BH
00401425  .^75 F8          JNZ SHORT keygen.0040141F
00401427  . FF75 F4        PUSH DWORD PTR SS:[EBP-C]
0040142A  . 68 BD304000    PUSH keygen.004030BD

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:

004013D1 /$ 90             NOP
004013D2 |. 90             NOP
004013D3 |. 90             NOP
004013D4 |. 90             NOP
004013D5 |. 90             NOP
004013D6 |. 90             NOP
004013D7 |. 90             NOP
004013D8 |. 90             NOP
004013D9 |. 90             NOP
004013DA |. 90             NOP
004013DB |. 55             PUSH EBP
004013DC |. 8BEC           MOV EBP,ESP
004013DE |. 83C4 F4        ADD ESP,-0C
004013E1 |. 52             PUSH EDX                                 ;
keygen.004030F0
004013E2 |. 51             PUSH ECX
004013E3 |. 33C9           XOR ECX,ECX
004013E5 |. 8B55 08        MOV EDX,DWORD PTR SS:[EBP+8]
004013E8 |. DD05 DB304000  FLD QWORD PTR DS:[4030DB]
004013EE |> 33C0           XOR EAX,EAX
004013F0 |. 8A0411         MOV AL,BYTE PTR DS:[ECX+EDX]
004013F3 |. 24 7F          AND AL,7F
004013F5 |. 8945 FC        MOV DWORD PTR SS:[EBP-4],EAX
004013F8 |. DB45 FC        FILD DWORD PTR SS:[EBP-4]
004013FB |. DEC1           FADDP ST(1),ST
004013FD |. DC0D C3304000  FMUL QWORD PTR DS:[4030C3]
00401403 |. 41             INC ECX
00401404 |. 3B4D 10        CMP ECX,DWORD PTR SS:[EBP+10]
00401407 |.^72 E5          JB SHORT keygen.004013EE
00401409 |. DC0D CB304000  FMUL QWORD PTR DS:[4030CB]
0040140F |. DC0D D3304000  FMUL QWORD PTR DS:[4030D3]
00401415 |. D9E1           FABS
00401417 |. DD5D F4        FSTP QWORD PTR SS:[EBP-C]
0040141A |. 90             NOP
0040141B |. 90             NOP
0040141C |. 90             NOP
0040141D |. 90             NOP
0040141E |. 90             NOP
0040141F |. 90             NOP
00401420 |. 90             NOP
00401421 |. 90             NOP
00401422 |. 90             NOP
00401423 |. 90             NOP
00401424 |. FF75 F8        PUSH DWORD PTR SS:[EBP-8]
00401427 |. FF75 F4        PUSH DWORD PTR SS:[EBP-C]
0040142A |. 68 BD304000    PUSH keygen.004030BD
0040142F |. 68 54314000    PUSH keygen.00403154
00401434 |. E8 15000000    CALL keygen.0040144E
00401439 |. 83C4 10        ADD ESP,10
0040143C |. 59             POP ECX
0040143D |. 5A             POP EDX
0040143E |. C9             LEAVE
0040143F . C2 0C00        RETN 0C

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:

00410000  8000 01          ADD BYTE PTR DS:[EAX],1
00410003  0000             ADD BYTE PTR DS:[EAX],AL
00410005  0000             ADD BYTE PTR DS:[EAX],AL
...
...
...
004100CF  0000             ADD BYTE PTR DS:[EAX],AL
004100D1  0000             ADD BYTE PTR DS:[EAX],AL
004100D3  0000             ADD BYTE PTR DS:[EAX],AL

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:

00413AF6  F7D2             NOT EDX          <--------------- Start of  stolen OEP!
00413AF8  39C2             CMP EDX,EAX   
00413AFA  F7C0 74E7F921    TEST EAX,21F9E774
00413B00  0FACC2 48        SHRD EDX,EAX,48     
...
...
00413B5C  3C 68            CMP AL,68
00413B5E  67:3B41 00       CMP EAX,DWORD PTR DS:[BX+DI]
00413B62 -E9 4D0AFFFF      JMP Crackme0.004045B4     <------- Jump to code section!

Trace to this last jump that leads to code section and enter in it:

004045B4  . 50             PUSH EAX
004045B5  . 6A 00          PUSH 0
004045B7  . E8 F8FEFFFF    CALL Crackme0.004044B4    <------- Enter here!
004045BC  . BA 98804000    MOV EDX,Crackme0.00408098
004045C1  . 52             PUSH EDX
004045C2  . 8905 B4944000  MOV DWORD PTR DS:[4094B4],EAX

Now trace a little more and enter to first call. You are at the place where one import is:

004044B4  $-FF25 D4564100  JMP DWORD PTR DS:[4156D4]   ;kernel32.GetModuleHandleA

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.

[haggar A.D 11.82005 ]

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (4)
雪    币: 61
活跃值: (160)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
2
先来坐沙发。。
2005-12-2 18:38
0
雪    币: 234
活跃值: (370)
能力值: ( LV9,RANK:530 )
在线值:
发帖
回帖
粉丝
3
看语文费时间和精力的,如果有翻译就好了
2005-12-2 21:05
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
翻译好了再发出来多好..一头雾水中
2006-3-30 02:19
0
雪    币: 277
活跃值: (312)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
5
studying!!!!
2006-3-30 13:37
0
游客
登录 | 注册 方可回帖
返回
//