Contribute  :  Web Resources  :  Past Polls  :  Site Statistics  :  Downloads  :  Forum  
    BiW ReversingThe challenge is yours    
 Welcome to BiW Reversing
 Thursday, June 01 2023 @ 02:07 PM CEST

EXEstealth v2.74a - manually unpacking

   

TutorialsLevel : newbie

Manually unpacking EXEstealth 2.74a featuring erased imports and PE header, CRC check, Anti debugging/dumping checks, and API redirection.

~~~~~~~~~~~~~~~~~~~~~~~~~~~
ExeStealth v2.74a - manually unpacking
~~~~~~~~~~~~~~~~~~~~~~~~~~~



Author: haggar
OS: Windows XP
Level: easy
(read this tutor with Courier new font)




Hi! This is simple tutorial about simple protector, ExeStealth v2.74a. If you do some Googling, you'll notice that there is newer version of this packer. So why even bother with this one? That newer version is not much interesting, it packs target .exe first with UPX and then encrypt it with Morphine. I think that anybody can unpack UPX and for Morphine you have nice KaGra's tutorial so there is no need to doing the same thing twice. This version is doing something different.

It is very easy to unpack this version in a few steps, but we will dig a little deeper in the protector and try to understand it better. Then we will try to write Olly script that will automate unpacking job.






1. TOOLS

- target http://www.reversing.be/binaries/articles/2005072621165937.rar
- OllyDbg 1.10 (and it's OllyDump, OllyScript plugins)
- LordPE
- ImpREC






2. ANALYZING PROTECTOR

I've packed target file with all possible options:

- Erase header
- CRC check
- API redirection
- Imports erasing
- Anti dumping
- Anti SoftICE, SmartCheck, IDA.



2.1. Decryptor loop

So let's begin. Open target in Olly (before that unceheck all exceptions but kernel in options). If you trace with F7 you'll see that this packer is not wasting time with junk code, it starts decripting it's code right at the begining. Note that in this first loop ECX=0C96 will be counter of how many bytes will be decrypted and decryption will start from 40613F address. This is the end of this loop:

...
0040613C AA STOS BYTE PTR ES:[EDI]
0040613D ^E2 C5 LOOPD SHORT IczEdit.00406104
...

Just pass this loop (let the first opcode below LOOPD decrypts and then put bp on it ond press F9, then remove bp). After that, trace with F7 until you land on next interesting part:



2.2. Procedure that calculates CRC

004063C4 MOV EDI,EAX
004063C6 XOR EAX,EAX
004063C8 XOR EBX,EBX
004063CA XOR EDX,EDX
004063CC MOV AL,BYTE PTR DS:[EDI]
004063CE MUL EDX
004063D0 ADD EBX,EAX
004063D2 INC EDX
004063D3 INC EDI
004063D4 LOOPD SHORT IczEdit.004063CC
004063D6 XCHG EAX,EBX
004063D7 RETN

This seams to be general CRC check procedure. Protector will couple times use this procedure to calculate some summ and store it for later comparation. When you exit this loop, you'll get on place where protector is storing calculated value:

00406183 MOV DWORD PTR SS:[EBP+402FF8],EAX

First value 0002A41C will be stored to 4068C3 address/buffer. You will read more about it later.




2.3. Protector API's

Continue with F7 untill you reach this part:

0040620F CALL DWORD PTR SS:[EBP+403444] ; kernel32.LoadLibraryA
00406215 MOV ESI,EAX
00406217 MOV DWORD PTR SS:[EBP+403459],EAX
0040621D LEA EAX,DWORD PTR SS:[EBP+40345D]
00406223 CALL IczEdit.004062BE
...
...
0040629F MOV DWORD PTR SS:[EBP+4034E4],EAX
004062A5 LEA EAX,DWORD PTR SS:[EBP+4034E8]
004062AB CALL IczEdit.004062BE
004062B0 MOV DWORD PTR SS:[EBP+4034F4],EAX
004062B6 LEA EAX,DWORD PTR SS:[EBP+4029FC]
004062BC PUSH EAX
004062BD RETN
004062BE PUSH EAX
004062BF PUSH ESI
004062C0 CALL DWORD PTR SS:[EBP+403448] ; kernel32.GetProcAddress
004062C6 RETN

There, protector will load kernel32.dll in memory and use GetProcAddress to catch addresses of some API functions that it needs. It will read API names from it's own little table and write those addresses at their names. Before (zeroes are reseved for writing):

00406D39 00 00 00 00 56 69 72 74 75 61 6C 50 72 6F 74 65 ....VirtualProte
00406D49 63 74 00 00 00 00 00 47 65 74 4D 6F 64 75 6C 65 ct.....GetModule
00406D59 46 69 6C 65 4E 61 6D 65 41 00 00 00 00 00 43 72 FileNameA.....Cr
00406D69 65 61 74 65 46 69 6C 65 41 00 00 00 00 00 47 6C eateFileA.....Gl
00406D79 6F 62 61 6C 41 6C 6C 6F 63 00 00 00 00 00 47 6C obalAlloc.....Gl
00406D89 6F 62 61 6C 46 72 65 65 00 00 00 00 00 52 65 61 obalFree.....Rea
00406D99 64 46 69 6C 65 00 00 00 00 00 47 65 74 46 69 6C dFile.....GetFil
00406DA9 65 53 69 7A 65 00 00 00 00 00 43 6C 6F 73 65 48 eSize.....CloseH
00406DB9 61 6E 64 6C 65 00 00 00 00 00 49 73 44 65 62 75 andle.....IsDebu
00406DC9 67 67 65 72 50 72 65 73 65 6E 74 00 00 00 00 00 ggerPresent.....

After:

00406D39 D9 AC E7 77 56 69 72 74 75 61 6C 50 72 6F 74 65 ٬çwVirtualProte
00406D49 63 74 00 9E 16 E6 77 47 65 74 4D 6F 64 75 6C 65 ct.žćwGetModule
00406D59 46 69 6C 65 4E 61 6D 65 41 00 FC AC E7 77 43 72 FileNameA.ü¬çwCr
00406D69 65 61 74 65 46 69 6C 65 41 00 C9 B3 E7 77 47 6C eateFileA.ÉłçwGl
00406D79 6F 62 61 6C 41 6C 6C 6F 63 00 C9 56 E7 77 47 6C obalAlloc.ÉVçwGl
00406D89 6F 62 61 6C 46 72 65 65 00 04 58 E7 77 52 65 61 obalFree.XçwRea
00406D99 64 46 69 6C 65 00 A1 AA E7 77 47 65 74 46 69 6C dFile.ˇŞçwGetFil
00406DA9 65 53 69 7A 65 00 B7 15 E8 77 43 6C 6F 73 65 48 eSize.·čwCloseH
00406DB9 61 6E 64 6C 65 00 43 A6 E7 77 49 73 44 65 62 75 andle.C¦çwIsDebu
00406DC9 67 67 65 72 50 72 65 73 65 6E 74 00 00 00 00 00 ggerPresent.....



2.4. CRC of whole file

Than, protector will call VirtualProtect, GetModuleHandle (to get packed file path), CreateFileA (to open file), GetFileSize (to get file size, but note that crackme will the substract 5 from that value, ie. it will not count last 5 bytes), GlobalAlloc, ReadFile and etner again at 4063AF to previous CRC check procedure. In another words, protector will check file for some modifications. In the same CRC procedure, protector will calculate CRC summ of whole_file-last_5_bytes and then, it will store it to some buffer:

004063B4 8985 F42F4000 MOV DWORD PTR SS:[EBP+402FF4],EAX

CRC_summ=001D0B99
buffer=4068BF





2.5. Decrypting sections

Now check something interesting. Open file in hex editor and check last 5 bytes of file. The last 5 bytes are 990B1D0000 what is reverse 00001D0B99, what is our calculated CRC_summ. Now you see why protector didn't take last five bytes of file in calculation, because he wrote there it's checksum. If you continue further with F7, you'll se that GlobalFree and CloseHandle are called. If we continue to trace, we'll find next interesting place:

00406410 CMP DWORD PTR DS:[ESI],63727372
00406416 JNZ SHORT IczEdit.0040641D
00406418 JMP IczEdit.004064E2
0040641D CMP DWORD PTR DS:[ESI],7273722E
00406423 JNZ SHORT IczEdit.0040642A
00406425 JMP IczEdit.004064E2
0040642A CMP DWORD PTR DS:[ESI],7365722E
00406430 JNZ SHORT IczEdit.00406437
00406432 JMP IczEdit.004064E2
00406437 CMP DWORD PTR DS:[ESI],6164652E
0040643D JNZ SHORT IczEdit.00406444
0040643F JMP IczEdit.004064E2
00406444 CMP DWORD PTR DS:[ESI],6F6C6572
0040644A JNZ SHORT IczEdit.00406451
0040644C JMP IczEdit.004064E2
00406451 CMP DWORD PTR DS:[ESI],6C65722E
00406457 JNZ SHORT IczEdit.0040645E
00406459 JMP IczEdit.004064E2
0040645E CMP DWORD PTR DS:[ESI],73656F6E
00406464 JNZ SHORT IczEdit.00406468
00406466 JMP SHORT IczEdit.004064E2
00406468 CMP DWORD PTR DS:[ESI],53657845
0040646E JNZ SHORT IczEdit.00406472
00406470 JMP SHORT IczEdit.004064E2
00406472 CMP DWORD PTR DS:[ESI],6164652E
00406478 JNZ SHORT IczEdit.0040647C
0040647A JMP SHORT IczEdit.004064E2

Here protector takes section names from PE header DS:[ESI], and compare them with some hardcoded:

rsrc , .rsr , .res , .eda , relo , .rel , noes , ExeS, .eda

What's the point? When some section name pass this checks, you'll see that protector is decrypting that section. Conclusion is that protector will not decrypt (and that means that it didn't encrypt them either) one of these sectiones. Why? I don't know what all those sections mean, but I know that there is bug in oleaut32.dll which forbide .rsrc section to change name. Protector will obviously not encrypt resource section. It will also not encrypt it's own ExeS section in this loop. Next little check is checking does section Physical Size == 0 and is it's Physical Offset == 0:

0040647C CMP DWORD PTR DS:[ESI+14],0
00406480 JE SHORT IczEdit.00406488
00406482 CMP DWORD PTR DS:[ESI+10],0
00406486 JNZ SHORT IczEdit.0040648A
00406488 JMP SHORT IczEdit.004064E2

In both cases, decryption is skipped. After decryption is done, program flow continues to place where next section is taken or, if counter of sections EDX reach last number, decryption is done:

004064E1 POPAD
004064E2 ADD ESI,28
004064E5 INC EDX
004064E6 CMP DX,WORD PTR DS:[EDI+6]
004064EA JNZ IczEdit.00406410
004064F0 RETN



2.6. Checking CRC of whole file

After decryption, continue to first CRC check:

00406532 MOV EAX,DWORD PTR SS:[EBP+402FF4]
00406538 OR EAX,EAX
0040653A JE SHORT IczEdit.00406549
0040653C CMP EAX,DWORD PTR SS:[EBP+403522]
00406542 JE SHORT IczEdit.00406549
00406544 JMP IczEdit.004066F8

To EAX is poped CRC_summ=001D0B99 of whole_file-5_bytes and compared with hard written at the end of file. If that values are not equal, JMP at 406544 that lead to ExitThread will be executed.



2.7. Imports redirection

First, protector will count how many there are imports and it will allocate some memory for redirections:

00406575 CMP DWORD PTR DS:[EDX],0
00406578 JNZ SHORT IczEdit.00406571
0040657A ADD ESI,0C
0040657D CMP DWORD PTR DS:[ESI+4],0
00406581 JNZ SHORT IczEdit.00406566
00406583 XOR EDX,EDX
00406585 MOV EAX,5
0040658A MUL ECX
0040658C PUSH EAX
0040658D PUSH 0
0040658F CALL DWORD PTR SS:[EBP+4034B8]
00406595 OR EAX,EAX
00406597 JNZ SHORT IczEdit.0040659E
00406599 ADD ESP,4
0040659C POPAD
0040659D RETN

ECX=49 is number of imports. Then protector will decrypt one DLL name in this call:

...
004065B3 CALL IczEdit.004065C0
...
...
004065C0 PUSH ESI
004065C1 PUSH EDI
004065C2 MOV ESI,EAX
004065C4 MOV EDI,EAX
004065C6 LODS BYTE PTR DS:[ESI]
004065C7 ROR AL,4
004065CA STOS BYTE PTR ES:[EDI]
004065CB CMP BYTE PTR DS:[EDI],0
004065CE JNZ SHORT IczEdit.004065C6
004065D0 POP EDI
004065D1 POP ESI
004065D2 RETN

First decrypted DLL is GDI32.DLL. After decrypting, protector will load this DLL:

004065D4 FF95 44344000 CALL DWORD PTR SS:[EBP+403444] ; kernel32.LoadLibraryA

and then it will delete DLL name from imports section jumping to:

00406845 JMP SHORT IczEdit.0040684B
00406847 MOV BYTE PTR DS:[EAX],0
0040684A INC EAX
0040684B CMP BYTE PTR DS:[EAX],0
0040684E JNZ SHORT IczEdit.00406847
00406850 RETN

After loading DLL, protector will decrypt it's API functions one by one in the same decrypting loop and than, it will get that API address via GetProcAddress and store it:

00406630 CALL IczEdit.004065C0 ; Decrypt API names.
00406635 POP EAX
00406636 MOV EDI,EAX
00406638 PUSH EDX
00406639 PUSH ECX
0040663A PUSH EAX
0040663B PUSH EBX
0040663C CALL DWORD PTR SS:[EBP+403448] ; kernel32.GetProcAddress
00406642 OR EAX,EAX
00406644 JNZ SHORT IczEdit.0040664D
00406646 POP ECX
00406647 POP EDX
00406648 JMP IczEdit.004066F8
0040664D POP ECX
0040664E POP EDX
0040664F PUSHAD
00406650 TEST DWORD PTR SS:[EBP+402FF0],4
0040665A JE SHORT IczEdit.0040666A
0040665C LEA EAX,DWORD PTR SS:[EBP+402D9F]
00406662 PUSH EAX
00406663 MOV EAX,EDI
00406665 JMP IczEdit.00406845 ; Erase API name.
0040666A POPAD
0040666B MOV DWORD PTR DS:[EDX],EAX ; Write API address to IAT.

In this first DLL case, protector will not redirect imports, instead it will write it at it's correct place. Then it will decrypt and load second DLL, comdlg32.dll, write it's imports at the correct place, but it will then jump at:

004066BB PUSH EDI ; IczEdit.0040335C
004066BC PUSH ESI
004066BD LEA EDI,DWORD PTR SS:[EBP+403526]
004066C3 MOV ESI,DWORD PTR DS:[EDI+4]
004066C6 MOV DWORD PTR DS:[EDX],ESI ; Redirect API's!
004066C8 SUB EAX,ESI
004066CA SUB EAX,5
004066CD MOV BYTE PTR DS:[ESI],0E9
004066D0 MOV DWORD PTR DS:[ESI+1],EAX
004066D3 ADD DWORD PTR DS:[EDI+4],5
004066D7 POP ESI
...
...

Here it will redirect imports to allocated place. After that, it will again delete API name. Now you know what hapens with imports. Imports are first written to their correct place, but then, some of them are redirected and all DLL and API names are deleted. This prevents simple dumping but ImpREC can easy find and rebuild this kind of IAT destroing.




2.8. Erasing PE header

This is easy job. After loading all necesery DLL's, redirecting and erasing, protector will delete whole PE header to prevent correct dumping:

004066FF TEST DWORD PTR SS:[EBP+402FF0],2
00406709 JE SHORT IczEdit.00406723
0040670B MOV EDI,DWORD PTR SS:[EBP+402FE8]
00406711 ADD EDI,DWORD PTR DS:[EDI+3C]
00406714 MOV ECX,DWORD PTR DS:[EDI+54]
00406717 MOV ESI,DWORD PTR SS:[EBP+402FE8]
0040671D MOV BYTE PTR DS:[ESI],0 ; Erasing PE header.
00406720 INC ESI
00406721 LOOPD SHORT IczEdit.0040671D

This is easy to fix. We can copy-paste header from unpacked file, or simply patch opcode at 40671D. But, later about this.



2.9. CRC check - again

And now it comes clear what does CRC at the begining do. Go and look at "2.2. Procedure that calculates CRC" at the begining of the tutorial. There was calculated CRC value 0002A41C of protector section that was never used up to now. Here, again is called same CRC calculation procedure and again is calculated same CRC summ. Than this new value will be compared with old one:

00406739 MOV EBX,DWORD PTR SS:[EBP+402FF8]
0040673F XOR EAX,EBX
00406741 JE SHORT IczEdit.0040674B
00406743 JMP SHORT IczEdit.00406746

This check will detect any changes made to ExeStealth code in memory such as our breakpoints or patches. JE at 406741 must be executet. If not, program flow will lead to ExitThread in kernel32.dll. Since I used togle bp at the end of CRC calculation loop, that CC bp was taken in calculation. Simply set EAX=EBX=0 before XOR opcode and you will pass this check.



2.10. Secod decryptor loop and anti debugger stuff

When you pass abowe CRC check, you will land in next decryptor loop what decrypts next pice of ExeStealth code. Do the same as in the first loop to decrypt code. Then IsDebuggerPresent is called:

00406766 LEA EAX,DWORD PTR SS:[EBP+4034F8]
0040676C PUSH EAX
0040676D PUSH DWORD PTR SS:[EBP+403459]
00406773 CALL DWORD PTR SS:[EBP+403448] ; kernel32.GetProcAddress
00406779 OR EAX,EAX
0040677B JE SHORT IczEdit.00406785
0040677D CALL EAX ; kernel32.IsDebuggerPresent
0040677F OR EAX,EAX
00406781 JE SHORT IczEdit.00406785
00406783 POPAD
00406784 RETN

Simply, one of two JE SHORT IczEdit.00406785 must be executed to pass this check. The easiest way is to use IsDebugPresent plugin, or you just set EAX value to zero at 40677F.

After that, if you continue tracing with F7, you'll face on INT 68. I'm not much familiar with interrupts, but it's not so important right now. Scroll down a little and place bp on:

0040681A XOR AL,AL
0040681C LEA EDI,DWORD PTR SS:[EBP+402795]
00406822 MOV ECX,788
00406827 STOS BYTE PTR ES:[EDI]
00406828 LOOPD SHORT IczEdit.00406827
0040682A LEA EDI,DWORD PTR SS:[EBP+402F7A]
00406830 MOV ECX,590
00406835 STOS BYTE PTR ES:[EDI]
00406836 LOOPD SHORT IczEdit.00406835
00406838 POPAD

Press Shift+F9 to break on your bp and then remove it. Place new bp on POPAD instruction and run. You'll see that those two loops deleted all code abowe and below them. Only thing that's left is:

00406838 POPAD
00406839 PUSH EAX
0040683A XOR EAX,EAX
0040683C PUSH DWORD PTR FS:[EAX]
0040683F MOV DWORD PTR FS:[EAX],ESP
00406842 JMP SHORT IczEdit.00406845
00406844 XCHG DWORD PTR DS:[EAX],EAX
00406846 ADD BYTE PTR DS:[EAX],AL
00406848 ADD BYTE PTR DS:[EAX],AL
0040684A ADD BYTE PTR DS:[EAX],AL
0040684C ADD BYTE PTR DS:[EAX],AL
...
...

This is the end ;) Place memory bp on access on whole .code section and run target (Shift+F9 two times). You'll land on OEP of packed target:

00401000 MOV BYTE PTR DS:[40438C],0 ; This should be the OEP!
00401007 MOV BYTE PTR DS:[40448C],0
0040100E PUSH 0 ; /pModule = NULL
00401010 CALL IczEdit.00402E7A ; GetModuleHandleA
...
...




3. QUICK WAY TO UNPACK IT

-To find OEP, uncheck all exceptions, use olly plugin to hide debugger, press Shift+F9 untill you reach last exception, place memory breakpoint to code section and pressShift+F9.
-To restore missing PE header, open in new olly packed file and copy-paste it's PE header and then dump file with olly dump plugin.
-To rebuild IAT, use ImpREC.

That is one way to unpack it. Second way would be to patch all those erasing opcodes that destroys PE header, API/DLL names and redirections. We can write Olly script that will do that.




4. MAKING OLLY SCRIPT

We will make olly script for automaticly unpacking ExeStealth v2.74a. For this you need OllyScript plugin (I don't know does ShAg support it anymore. Last time his site was down :( , but try get it from OllyDbg forum). Making scripts it's very easy, just be sure that you have read readme.txt included with plugin.
Advantage of scripts is to make boring unpacking faster. After you unpack ASpack for 100 times manually, it's no fun anymore. Also, with scripts you can make much simpler some tasks that are manually hard, like redirecting some imports or similar.
As we sow, our packer redirect imports, deletes DLL's and API's names, and it erases PE header. We have fixed that using ImpREC and pasting PE header from unpacked file, but with script, we can patch redirection and erasing procedures so dumped file will not be damaged.

The logic for making script was this:

- First we must hide debugger.

- Then we must get to place where interesting things happens. For that I used GetProcAddress to break on, since that API is called after first decryption loop is done.

- Then we must find places where DLL and API names are erased and patch those instructions.

- Find redirection instruction and patch it.

- Find PE header erasing loop and patch it.

- Patch CRC check.

- Land on OEP.

I have included script for this version of EXEstealth in the archive. Read it in notepad, I've wrote lot of coments so it won't be hard to understand it.




5. FINAL WORDS

Yep, it's easy protector to bypass.
After done with this protector I've grabed Yoda's Crypter 1.3 and done little examing. I was surprised! Yoda Crypter 1.3 is completly indentical with EXEstealth 2.74a! Header erasing, IAT redirection, CRC checks and rest of it's functions that are described in this tutorial are implemented in same way. It means that EXEshield 2.74a is rip of Yoda's Crypter! And guy is seling it and did not even mention Yoda's name in the about box. Yoda is freeware and open source protection/crypter. And new EXEstealth v3.04 is using UPX and Morphine who are again freeware and open source too. Only this time he did mention reall software authors.

I have notice one more interesting thing: if do not use all options while packing target, like you don't wan't to use CRC check or PE header erasing, packer will not exclude it from loader code, it will place them there, but it will jump over them using contitional jumps. So this could be even easier way to prevent erasing header and IAT and redirecting it.



Probably there is lot of spelling and grammar errors in the tutorial, sory for that.


As usuall, big greets to biw moderators/crew, users and tutorial writers!




[ haggar A.D.22.07.2005 ]




What's Related

Story Options

EXEstealth v2.74a - manually unpacking | 2 comments | Create New Account
The following comments are owned by whomever posted them. This site is not responsible for what they say.
EXEstealth v2.74a - manually unpacking
Authored by: int21h on Monday, August 01 2005 @ 04:16 AM CEST
First I would like to say thank you for all your hard work on these essays; I really do appreciate it. For some reason I could not get your script to run using my version of OllyScript; I am using OllyScript v.81. With that being read; I modified your script to work on my version of OllyScript: Here it is:

msg "Script works only on XP systems. Ignore ALL exceptions!!   "
var csize 
var cbase 
dbh
gpa "GetProcAddress", "kernel32.dll"      
bp $RESULT                                
run                                       
find 406000,#8B9D??????0033C37408EB01#       
add $RESULT,7
fill $RESULT,1,C0                         
find 406000,#EB04C600004080380075F7C3#       
add $RESULT,2
fill $RESULT,3,90
find 406000,#57568DBD??????008B770489322BC683E805C606E9894601834704055E5F# 
add $RESULT,0B
fill $RESULT,2,90
find 406000,#8BBD??????00037F??8B4F??8BB5??????00C6060046E2FA#  
add $RESULT,12
fill $RESULT,3,90   
esto
bc eip 
esto
esto
gmi eip,CODEBASE 
mov cbase,$RESULT 
gmi eip,CODESIZE 
mov csize,$RESULT 
bprm cbase,csize
esto
bpmc 
cmt eip,"This should be the OEP!"
msg "OEP found! Dump with OllyDump plugin, but rebuild IAT with ImpREC.  "
dbs      
ret  
This worked for me; If others are having a problem running your script they may want to try this one. Once again I thank you for your time and effort to help all here understand what is happening with these packers. int21h
EXEstealth v2.74a - manually unpacking
Authored by: haggar on Monday, August 01 2005 @ 08:35 PM CEST
I have OllyScript v0.92 so that could be the problem. Since now forum has file upload option, I will post it there so everybody can download it. I don't know does author will develop it anymore :(

And btw, every comment is welcome. I'm glad that somebody read my tut ;)
 Copyright © 2023 BiW Reversing
 All trademarks and copyrights on this page are owned by their respective owners.
Powered By Geeklog 
Created this page in 0.94 seconds