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.
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:
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.
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):
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:
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:
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:
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:
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:
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:
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!
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
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 ;)