-
Unpacking UPX packed Gamemon.des
By: g3nuin3
Sup all, bored so I thought id write this one up right quick..In this tutorial ill go through the process of successfully unpacking the UPX packed gamemon.des file, its really simple because UPX is probably the easiest packer on the inet, well, enough crap. Lets get to work
Tools
PEID - > (optional)
OllyDBG -> (mandatory)
Gamemon.des -> (mandatory)
Imprec ->Mandatory
OllyDump plugin ->(you can use, lordpe too if you want)
Lets Go
Ok the first thing I always do is check to see what kind of packer were working with So fire up PEID and see what it is.
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Yep, so we have our UPX there, doesn’t seem to be much of anything wrong. I wrote this tut because I read Shub-Nigguraths 10 easy steps, they actually work :p
Well ill use this file to show you something, What UPX and most packers/protectors do is, they more than likely compress the IAT, or redirecting it, making it hard for us to unpack or clearly analyze the file.. to show you what I mean I’m going to show you what the packed gamemon’s Imports looks like packed and after its unpacked.
In PEID I load the packed file and I take a look at the kernels first imported functions.
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Well that’s odd, it only has three imported functions from kernel… but we sure do know there are many more than this from the kernel module right?
Hmmm, now look at the file after its unpacked.
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Big change huh?! This is what we want it to look like... Well be patient, now well get to the fun part, actually unpacking it!
Ok, assuming you have your tools prepared, lets go to battle.. First thing we will do it load the gamemon.des into Olly Debugger…After it analyzes We are at a screen similar to this here.
Ok, the next thing you want to do is Set a BREAKPOINT on LoadLibraryA. You can use the commandline plugin to do this in Olly debugger.
Ok, once you are familiar with UPX you will understand why this is so, many of them are the same =)
NOW, that our breakpoint is set we would like to continue until we break at this BREAKPOINT So please everyone , push F9 on ollydebugger, this will continue execution of the program
BAM we break on it ! break means we hit the breakpoint we set and we are now ready to analyze whats going on!
So Look at the bottom right window of olly debugger! We will see THIS!
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
We are interested at What called this function, hmm well here we have it CALL to LoadLibraryA from Gamemon.0049470E
Hah so lets follow this and see whats goin on in this section of code.
In olly debugger press Ctrl + G, this will bring up a window and we go to the expression, which is 0049470E, or we can right click that line in the window and go to Follow in dissassembler.
Ok now that we have gone to this expression lets check out the code there. Well we are not much interested in the place where it was called rather a few instructions down, we will see a few conditional jumps, then a definite JUMP, (JMP) this will be the magic jump to our OEP. Look here.
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
ok so look there, we found a small encryption stuff, a few Juump if equals and stuff, but after this we have the real jump to the real original entry point of gamemon.des, how do I know? Well
Lets go to the expression where it jumps to J JMP Gamemon. 0043C327 , right click this expression and go to Follow or press Enter if you are at that address, And well well what do we know, its our real ENTRY POINT
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
NOTE: When you reach here make sure you make it the origin point! Right click “New origin” Some of you might be wondering… how do you know what the OEP is!!? Well from my experience Any valid PE program always starts with this instruction
Push EBP
MOV EBP, ESP
PUSH -1
….. (Push -1 is not always the case(language specific) but after reading Goppits explanation on the PE I know and other resources I cant remember :S)
here
Don’t know if this is the valid explanation for it, but its what ive seen unpacking files or reversing them. (if you know why, please tell me J )
Anyways, now that we have the address of where our OEP is, we must calculate this and get the offset…
Fom Shubs tutorial:
“EP = OEP - base Remember that the values in the PE header are always file offsets and not addresses.”
And he is correct. What were attempting to do is get the valid ENTRY POINT of the program, so we calculate the ORIGINAL ENTRY POINT we get and subtract this by the BASE IMAGE.. which is USUALLY 00400000. ( don’t know about the cases where its not.)
So now we know that lets calculate it
EP = 0043C327 - 00400000. EP = 3C327 (If youre confused on how this is done, just use the trusty windows calculator and input this under the hex format ; ) )
Now, we have our OEP, EP calculated, lets dump this baby =) Dumping window of olly should look like this:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
As you can see olly has already calculated the ENTRY POINT for us =) Make sure that Rebuild Imports is NOT checked.. I personally trust Imprec, but if you feel you can trust it then if you c3hcked that and dumped the file, you are officially done, for the smarter people, lets get Dump this File, LEAVE OLLY ON, and open up imprec.
Once you have imprec open, target the gamemon file, now look at the screenshot:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Where it says OEP we enter the offset we calculated from the previous part. Remember, the PE works with offsets not actual addresses!
After this we click IAT autoSearch.. We will hopefully get a successful message and it tells us to try to Get Imports..
So lets GetImports!
Press GetImports!
We should be greeted with our imports and they should have YES next to them meaning they are all valid thunks =D..
Now We press Fix Dump, search for the file you dumped with Ollydebugger earlier and then it will successfully fix the dump file and save it…
WE ARE DONE!!!!!
We have successfully dumped and everything went A OK>!
See, unpacking UPX is nothing, now try your luck with the nppgnt.des file =) its just as easy :=)
-
Level : intermediate
===========================
Enigma protector 1.02 - manually unpacking
===========================
Enigma protector 1.02 is another PE file protector. It is still in development and this version is a free demo without limitations. As a target for this tutorial I choose the protector itself because it has all options enabled that it has to offer us. Plus, unpacking a file that I didn't previously packed is chalenging because I do not know what the original was looking like. I must say that this protector gave me some problems, I was writing this tutorial (and that means unpacking it) for a couple of days. Tutorial is not hard to understand but since it's not linear (I was restarting target multiple times) , it is not for beginners. Let's say that it is on intermediate level. At the end of the tutorial, I will show how the protected target can be easily inline patched. Well, not so easy , but it can be easier than unpacking it.
1. Tools and stuff
Tools that we need are usual:
- OllyDbg 1.10
- ImpREC
- LordPE
- hex editor
- Windows XP
The protector has few bugs. Main bug is in "Exit on file name option" because every file packed with that option is non-working. Reason is that protector leave to file name exclamanation character " (example test.exe becomes test.exe") so it thinks that every name is invalid. Virus check also can be buggy.
2. OEP and loading DLL
Reaching OEP can be very easy. Just use Olly tracing option and after some time OEP is found. Protector has IsDebuggerPresent check so we must use plugin to hide olly. This way of finding OEP will work if protected file doesn't emulate OEP of other compilers. Altough with little PE header altering Olly can find OEP in those cases too, later we'll see more generic approach for OEP. When we land on OEP, we will see that we are actually on "false" OEP and that some original OEP opcodes are "stolen":
...
...
004F9903 0000 ADD BYTE PTR DS:[EAX],AL
004F9905 0000 ADD BYTE PTR DS:[EAX],AL
004F9907 007494 4F ADD BYTE PTR SS:[ESP+EDX*4+4F],DH
004F990B 009D 560B5927 ADD BYTE PTR SS:[EBP+27590B56],BL
004F9911 BF D30D736B MOV EDI,6B730DD3
004F9916 6A F8 PUSH -8
004F9918 E8 5BCAF0FF CALL Enigma.00406378 ; Real entry point of SFX code
004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4]
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9925 E8 E648F9FF CALL Enigma.0048E210
004F992A 8B0D 782E3E00 MOV ECX,DWORD PTR DS:[3E2E78]
004F9930 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9932 8B15 D0524E00 MOV EDX,DWORD PTR DS:[4E52D0]
004F9938 E8 EB48F9FF CALL Enigma.0048E228
004F993D 8B0D 14333E00 MOV ECX,DWORD PTR DS:[3E3314]
004F9943 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9945 8B15 50204D00 MOV EDX,DWORD PTR DS:[4D2050]
004F994B E8 D848F9FF CALL Enigma.0048E228
004F9950 8B0D 04333E00 MOV ECX,DWORD PTR DS:[3E3304]
004F9956 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9958 8B15 1CC34D00 MOV EDX,DWORD PTR DS:[4DC31C]
...
...
"False" OEP is at 004F9918, above code is stolen. Olly tok lot of time to find OEP. In log window we can see some interesting informations:
************************************************** **************************
Log data
Address Message
0066C8A4 Access violation when writing to [00000000]
0066C8A4 Access violation when writing to [00000000]
0066C8A4 Access violation when writing to [00000000]
0066C8A4 Access violation when writing to [00000000]
...
loooooot of memory access violation exceptions
...
0066CBEE Access violation when writing to [00000000]
0066CBEE Access violation when writing to [00000000]
0066CD84 Access violation when writing to [00000000]
0066CEBE Access violation when writing to [00000000]
00C24333 INT3 command at 00C24333
00C243E5 Access violation when writing to [00000000]
00C243EC Integer division by zero
...
...
...
00C26C10 Illegal instruction
004F9918 Possible entry point of SFX code
CRC changed, discarding .udd data
Analysing Enigma
3459 heuristical procedures
51 calls to known, 1275 calls to guessed functions
1038 loops, 313 switches
************************************************** ***************************
We can see that protector consist from two parts, first is code attached at the end of main exe, and second is dll loader that is extracted in some allocated memory block. We can see that in attached part occurres lot of memory violation exceptions, most of them at same place. Those exceptions slow down program execution in debugger, particulary if you try trace trough it. But in that attached code, exceptios are all memory violations. When dll loader is extracted, first exception ocurred in it is INT3. So we could easily break in loader just by setting exceptions handling in Olly options.
But there is smarter way: Last exception in attached code is at 0066CEBE. I will place bp there , ignore all exceptions, will not use Olly tracing option this time, and restart target. Soon I break on my bp:
0066CEBE 8900 MOV DWORD PTR DS:[EAX],EAX <------------- Here!
0066CEC0 9A 648F0500 0000 CALL FAR 0000:00058F64 ; Far call
0066CEC7 00EB ADD BL,CH
0066CEC9 02C1 ADD AL,CL
0066CECB 90 NOP
0066CECC 58 POP EAX ; 0012FFE0
0066CECD 61 POPAD
0066CECE EB 01 JMP SHORT Enigma.0066CED1
I removed bp and checked stack:
0012FF58 0012FFE0 Pointer to next SEH record
0012FF5C 0066CE84 SE handler <-------------------- Exception handler!
0012FF60 77EB6930 kernel32.77EB6930
0012FF64 00C00000 ASCII "MZP"
In stack you can see that local exception handler is at 0066CE84. That mean, after exception is coused, program will continue from that line. I'm going there:
0066CE84 EB 02 JMP SHORT Enigma.0066CE88
0066CE86 CD 20 INT 20
0066CE88 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+C]
0066CE8C 8380 B8000000 03 ADD DWORD PTR DS:[EAX+B8],3
0066CE93 31C0 XOR EAX,EAX
0066CE95 C3 RETN
Last RETN will throw us to ntdll.dll that handles exceptions but here is conclusion: Since we passed last exception before jumping to loader code, here around must be some jump or RETN that throw us to loader code. Little down I found last instructions that are executed before jumping:
0066CF6E EB 01 JMP SHORT Enigma.0066CF71 <---- It jumps to obfuscated RETN below!
0066CF70 9A C35589E5 FF75 CALL FAR 75FF:E58955C3
0066CF77 0C FF OR AL,0FF
0066CF79 75 08 JNZ SHORT Enigma.0066CF83
0066CF7B E8 46000000 CALL Enigma.0066CFC6
0066CF80 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+8]
...
...
Follow that jump:
0066CF71 C3 RETN <------------------------ It leads to loader!
0066CF72 55 PUSH EBP
0066CF73 89E5 MOV EBP,ESP
0066CF75 FF75 0C PUSH DWORD PTR SS:[EBP+C]
0066CF78 FF75 08 PUSH DWORD PTR SS:[EBP+8]
0066CF7B E8 46000000 CALL Enigma.0066CFC6
...
...
So I placed bp there and executed untill I stopped there. RETN leads to 00C2720C which is OEP of Borland Dll which is protector's loader:
00C2720C 55 PUSH EBP
00C2720D 8BEC MOV EBP,ESP
00C2720F 83C4 C4 ADD ESP,-3C
00C27212 B8 9C70C200 MOV EAX,0C2709C
00C27217 E8 C0E8FDFF CALL 00C05ADC
00C2721C E8 2BCAFDFF CALL 00C03C4C
00C27221 8D40 00 LEA EAX,DWORD PTR DS:[EAX]
00C27224 0000 ADD BYTE PTR DS:[EAX],AL
00C27226 0000 ADD BYTE PTR DS:[EAX],AL
00C27228 0000 ADD BYTE PTR DS:[EAX],AL
00C2722A 0000 ADD BYTE PTR DS:[EAX],AL
00C2722C 0000 ADD BYTE PTR DS:[EAX],AL
00C2722E 0000 ADD BYTE PTR DS:[EAX],AL
00C27230 0000 ADD BYTE PTR DS:[EAX],AL
...
...
We are at the entry point of loader dll. This dll will unpack/unprotect original file in memory, it will do all tricks to prevent debugging and dumping. We can even dump this file to disk and repair it, I will do this later just for fun. Scrolling up to beggining of section you will get address of it's base. Because this dll will probably be loaded on different base on your machine, it is good thing to know it's base because then you can find all addresses relative to it. What we know untill now:
Jump_to_loader_code = 0066CF71
Loader_Image_Base = 00C00000 (on my machine)
Loader_OEP = 00C2720C (on my machine)
Loader_relative_OEP = +2720C
3. Integrity traps
One wery annoying thing is that this protector has many internal integrity checks that detects every normal or memory breakpoint. Also protector kills/corrupts hardware breakpoints and examning loader code is hard because I had to use lot of breakpoints, then delete them to not be found. Protector uses lot of crypto algorithms for checking. After dumping this dll to disc I scaned it with PEiD's Kanal crypto plugin which found next signatures:
------ encoding (used e.g. in e-mails - MIME)
CRC32 precomputed table for byte transform
MD5 transform ("compress") constants
TWOFISH: pregenerated 8x8 Sbox table
Well, this is not some information that will help me in unpacking , but it's interesting to know.
To find where check of dll is performed, I just placed memory bp on access on dll OEP and I stopped here:
$+1FEC9 > 8D40 00 LEA EAX,DWORD PTR DS:[EAX]
$+1FECC > 32C2 XOR AL,DL
$+1FECE > 25 FF000000 AND EAX,0FF
$+1FED3 > 8B0485 0C8CC200 MOV EAX,DWORD PTR DS:[EAX*4+C28C0C]
$+1FEDA > C1EA 08 SHR EDX,8
$+1FEDD > 81E2 FFFFFF00 AND EDX,0FFFFFF
$+1FEE3 > 33C2 XOR EAX,EDX
$+1FEE5 > C3 RETN
$+1FEE6 > 8BC0 MOV EAX,EAX
$+1FEE8 > 55 PUSH EBP
$+1FEE9 > 8BEC MOV EBP,ESP
$+1FEEB > 53 PUSH EBX
$+1FEEC > 56 PUSH ESI
$+1FEED > 57 PUSH EDI
$+1FEEE > 8B55 0C MOV EDX,DWORD PTR SS:[EBP+C]
$+1FEF1 > 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+8]
$+1FEF4 > 85D2 TEST EDX,EDX
$+1FEF6 > 76 21 JBE SHORT 00C1FF19
$+1FEF8 > 83C8 FF OR EAX,FFFFFFFF
$+1FEFB > 8BDA MOV EBX,EDX
$+1FEFD > 4B DEC EBX
$+1FEFE > 85DB TEST EBX,EBX
$+1FF00 > 72 12 JB SHORT 00C1FF14
$+1FF02 > 43 INC EBX
$+1FF03 > 33F6 XOR ESI,ESI
$+1FF05 > 8D143E LEA EDX,DWORD PTR DS:[ESI+EDI]
$+1FF08 > 8A12 MOV DL,BYTE PTR DS:[EDX] <------------ Stopped here!!!
$+1FF0A > 92 XCHG EAX,EDX
$+1FF0B > E8 BCFFFFFF CALL 00C1FECC
$+1FF10 > 46 INC ESI
$+1FF11 > 4B DEC EBX
$+1FF12 >^75 F1 JNZ SHORT 00C1FF05
$+1FF14 > 83F0 FF XOR EAX,FFFFFFFF
$+1FF17 > EB 02 JMP SHORT 00C1FF1B
$+1FF19 > 33C0 XOR EAX,EAX
$+1FF1B > 5F POP EDI
$+1FF1C > 5E POP ESI
$+1FF1D > 5B POP EBX
$+1FF1E > 5D POP EBP
$+1FF1F > C2 0800 RETN 8
That is procedure that produces some hashes. It is used couple times. It's RVA from base of dll is +1FEE8 and that is same in every target. That line, PUSH EBP is called from two places. You can easily find them "finding references to selected command", those two calls are just up at +1FE86 and +1FEAB. But I'm going too much in that direction. Those who like and know crypto can research that.
So, conclusion is that next important address is +1FEE8 or 00C1FEE8 in my case. Protector also has integrity checks for unpacked sections of file. If we make some changes after sections are unpacked, we will get error Message Box saying that virus damaged file. I used memory bp on location where I sow that message is create and found that check is here:
00C1DA0C E8 0F95FEFF CALL 00C06F20
00C1DA11 84C0 TEST AL,AL <---------------------- Check!
00C1DA13 75 35 JNZ SHORT 00C1DA4A <-------------- Good jump!
00C1DA15 8D45 EC LEA EAX,DWORD PTR SS:[EBP-14]
00C1DA18 8B15 B492C200 MOV EDX,DWORD PTR DS:[C292B4]
00C1DA1E 81C2 28040000 ADD EDX,428
00C1DA24 E8 7765FEFF CALL 00C03FA0
00C1DA29 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14]
00C1DA2C 50 PUSH EAX
00C1DA2D 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
00C1DA30 8B15 B492C200 MOV EDX,DWORD PTR DS:[C292B4]
00C1DA36 81C2 28050000 ADD EDX,528
00C1DA3C E8 5F65FEFF CALL 00C03FA0
00C1DA41 8B45 E8 MOV EAX,DWORD PTR SS:[EBP-18]
00C1DA44 5A POP EDX ; 0012FF54
00C1DA45 E8 8E000000 CALL 00C1DAD8
00C1DA4A 83C3 35 ADD EBX,35
00C1DA4D 837B 0D 00 CMP DWORD PTR DS:[EBX+D],0
00C1DA51 ^0F85 6EFFFFFF JNZ 00C1D9C5
00C1DA57 33C0 XOR EAX,EAX
00C1DA59 5A POP EDX ; 0012FF54
00C1DA5A 59 POP ECX ; 0012FF54
00C1DA5B 59 POP ECX ; 0012FF54
00C1DA5C 64:8910 MOV DWORD PTR FS:[EAX],EDX
00C1DA5F 68 79DAC100 PUSH 0C1DA79
00C1DA64 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
00C1DA67 BA 02000000 MOV EDX,2
00C1DA6C E8 EF62FEFF CALL 00C03D60
00C1DA71 C3 RETN
Jump above must be executed if we want to pass that check. It's relative address from base is +1DA11 and that is one more important address to remember. Actually not so much important. That check isn't important for unpacking, it is not even important in inline patching because changes can be made after this check.
4. OEP and stolen bytes
Let we return to OEP problem. For finding OEP we will use previous metnioned procedure that checks integrity in loader. Reason is that after last one check, that code is going straight to OEP (well, not straight but close). First we get to loader OEP (we already sow how to do it). Then place memory bp somewhere to stop in check procedure. We will break there
00C1FEE8 55 PUSH EBP <-------------------- Toggle bp.
00C1FEE9 8BEC MOV EBP,ESP
...
...
00C1FF08 8A12 MOV DL,BYTE PTR DS:[EDX] <---- We break here!
...
...
00C1FF1D 5B POP EBX
00C1FF1E 5D POP EBP
00C1FF1F C2 0800 RETN 8 <---------------------- Hw bp.
Idea is to count how many times will this procedure be executed before program starts. Then we do it again and stop at RETN 8 of last execution. But you must be careful. When you break at C1FF08 when memory is being read, remove that mem bp and place hw bp at RETN 8. Then place toggle bp on start of procedure 0C1FEE8. By switching breakpoint and hw bp, you will avoid detection and crushing (note: delete hw bp too). After 4 times program started. Do it all again and 4. time just trace with F7:
00C1FEB0 A3 D0CFC200 MOV DWORD PTR DS:[C2CFD0],EAX
00C1FEB5 031D D0CFC200 ADD EBX,DWORD PTR DS:[C2CFD0]
00C1FEBB 8BC3 MOV EAX,EBX
00C1FEBD 5B POP EBX
00C1FEBE C3 RETN
Continue with F7:
00C20547 A3 04D0C200 MOV DWORD PTR DS:[C2D004],EAX
00C2054C 8B65 08 MOV ESP,DWORD PTR SS:[EBP+8]
00C2054F 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
00C20552 894424 1C MOV DWORD PTR SS:[ESP+1C],EAX
00C20556 61 POPAD
00C20557 A1 04D0C200 MOV EAX,DWORD PTR DS:[C2D004]
00C2055C 50 PUSH EAX
00C2055D A1 00D0C200 MOV EAX,DWORD PTR DS:[C2D000]
00C20562 010424 ADD DWORD PTR SS:[ESP],EAX
00C20565 C3 RETN
Execute RETN and you will be here:
00D39E70 E8 08000000 CALL 00D39E7D
00D39E75 EF OUT DX,EAX ; I/O command
00D39E76 28E2 SUB DL,AH
...
cut
...
00D3B943 51 PUSH ECX
00D3B944 5E POP ESI
00D3B945 5E POP ESI
00D3B946 FEC9 DEC CL
00D3B948 68 18994F00 PUSH 4F9918 <----------------- Address of false OEP!
00D3B94D 60 PUSHAD
00D3B94E 31C0 XOR EAX,EAX <----------------- You can (and should) NOP from here...
00D3B950 B9 D81A0000 MOV ECX,1AD8
00D3B955 BF 709ED300 MOV EDI,0D39E70
00D3B95A F2:AA REPNE STOS BYTE PTR ES:[EDI]
00D3B95C 47 INC EDI
00D3B95D AB STOS DWORD PTR ES:[EDI]
00D3B95E AB STOS DWORD PTR ES:[EDI] <----- ...to here. It will prevent crushing.
00D3B95F 61 POPAD
00D3B960 C3 RETN
00D3B961 0000 ADD BYTE PTR DS:[EAX],AL
00D3B963 0000 ADD BYTE PTR DS:[EAX],AL
00D3B965 0000 ADD BYTE PTR DS:[EAX],AL
This huge junky code holds stolen OEP code. I didn't bother to try find exact bytes because program will work with this block attached to main dump. Code that must be NOP-ed is used to delete all those code with stolen bytes. It must be patched because in main dump, it will try to erase bytes in 00D3xxxx block (it won't be there in dumped file) and that will couse exception. Place bp on RETN, run, execute RETN and there is our (false) OEP:
004F9918 E8 5BCAF0FF CALL Enigma.00406378
004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4] ; Enigma.004FEBF0
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
...
...
Now we know how OEP can always be found, also we know that stolen code can be fixed by just adding section. I binary copied stolen bytes and saved it in StolenCode.txt file. I can use it later.
5. Code relocation
Enigma has some "Extra relocations protect" option which redirects whole one section. Check this:
004F9918 E8 5BCAF0FF CALL Enigma.00406378
004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4] <--- This is what I'm talking about.
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
...
...
It redirected whole section to some virtual address and changed all opcodes that points to original section. There is easy way to prevent this. Enigma uses VirtualAlloc to allocate that block. When block is allocated, all we need to do is change EAX=003E0000 to original section value and enigma will direct all to that section. How to break on right place? Using memory breakpoints. This line
004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4]
needs to be changed. More correct, these four bytes E4303E00. Restart target in olly, go to 004F991D+2=004F991F in dump and place mem bp there on 1 byte. Run Olly and you will break couple times in loader code. Wanted one is here:
00C26B6D 0108 ADD DWORD PTR DS:[EAX],ECX <------ ECX=FFEE6000 what redirects code!
00C26B6F EB 35 JMP SHORT 00C26BA6
00C26B71 8B0D B492C200 MOV ECX,DWORD PTR DS:[C292B4]
00C26B77 8B1D 24D3C200 MOV EBX,DWORD PTR DS:[C2D324]
00C26B7D 2B99 DC000000 SUB EBX,DWORD PTR DS:[ECX+DC]
00C26B83 8B15 B492C200 MOV EDX,DWORD PTR DS:[C292B4]
00C26B89 8B92 E9000000 MOV EDX,DWORD PTR DS:[EDX+E9]
00C26B8F 2BDA SUB EBX,EDX
00C26B91 25 FFFFFF7F AND EAX,7FFFFFFF
00C26B96 0305 24D3C200 ADD EAX,DWORD PTR DS:[C2D324]
00C26B9C 8B0D B492C200 MOV ECX,DWORD PTR DS:[C292B4]
00C26BA2 2BC2 SUB EAX,EDX
00C26BA4 0118 ADD DWORD PTR DS:[EAX],EBX
00C26BA6 8305 28D3C200 04 ADD DWORD PTR DS:[C2D328],4
00C26BAD A1 28D3C200 MOV EAX,DWORD PTR DS:[C2D328]
00C26BB2 8B00 MOV EAX,DWORD PTR DS:[EAX] ; Enigma.004FD0E4
00C26BB4 85C0 TEST EAX,EAX ; Enigma.004F991F
00C26BB6 ^75 88 JNZ SHORT 00C26B40
00C26BB8 33C0 XOR EAX,EAX ; Enigma.004F991F
00C26BBA 5A POP EDX ; 0012FF60
00C26BBB 59 POP ECX ; 0012FF60
00C26BBC 59 POP ECX ; 0012FF60
00C26BBD 64:8910 MOV DWORD PTR FS:[EAX],EDX
00C26BC0 EB 0F JMP SHORT 00C26BD1
Scroll up and we can see where it calls VirtualAlloc:
00C26AAF E8 E0F2FDFF CALL 00C05D94 ; JMP to kernel32.VirtualAlloc
That address is +26AAF from dll base. Address that is worth to remember. You need to break there somehow which again includes avoiding geting cought. Find your way. When you break on right line,
00C26AAF E8 E0F2FDFF CALL 00C05D94 ; JMP to kernel32.VirtualAlloc
00C26AB4 A3 24D3C200 MOV DWORD PTR DS:[C2D324],EAX
you need to change EAX value in second line. EAX must have base of original section. To find that, go now to 004F991D where code needs to be changed. You will see that it points somewere in 004FA000 section and that is original one. It means you must set EAX=004FA000. Then place memory bp on false OEP (erase all others) and run untill you break there:
004F9918 E8 5BCAF0FF CALL Enigma.00406378
004F991D 8B1D E4D04F00 MOV EBX,DWORD PTR DS:[4FD0E4] <---- See, it's OK now!
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
...
...
Check section there and you'll see that is OK. Let again see what we know till now:
Jump_to_loader_code = 0066CF71
Loader_Image_Base = 00C00000 (on my machine)
Loader_OEP = 00C2720C (on my machine)
Loader_relative_OEP = +2720C
Internal_check = +1FEE8 (00C1FEE8 on my machine)
Section_check = +1DA11 (00C1DA11 on my machine)
Jump_to_stolen_code = +20565 (00C20565 on my machine)
Code_relocation = +26AB4 (00C26AB4 on my machine)
6. General IAT problem
Enigma has three ways of import handling. First is completly removing whole IAT from IAT section, thunks are placed in some allocated memory block. Second, imports are little emulated/obfuscated. Then, it can combine both of that features together, remove and obfuscate. Third option, it can emulate some whole API's. This also can be combined with first feature. Our target has all this tricks included. Let's see one example: This is first import jump in exe:
004012A4 $-FF25 146FD300 JMP DWORD PTR DS:[00D36F14]
It points to some dword at 00D36F14 address but original jump should point to some dword in 00603000 section which was original IAT section. And dword that is placed there should be some API address, for example 77665544 but our dword is [00D36F14]=00C43268. We can follow that address:
00C43268 56 PUSH ESI
00C43269 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
00C4326F 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+8]
00C43273 83F9 F4 CMP ECX,-0C
00C43276 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
00C43279 68 F8E3E777 PUSH 77E7E3F8
00C4327E C3 RETN
These opcodes are emulated first couple instructions of GetStdHandle API, then PUSH-RETN combination jumps someware in the middle of API. This was eaxample of removing IAT and emulating couple first opcodes. I found way how this can be little changed to help ImpREC fix imports. I found that these two jumps are important:
00C1ECE3 0F87 300A0000 JA 00C1F719 <------------------------- This one!
00C1ECE9 FF249D F0ECC100 JMP DWORD PTR DS:[EBX*4+C1ECF0]
00C1ECF0 F0: PREFIX LOCK:
00C1ECF1 F0:C100 FC LOCK ROL DWORD PTR DS:[EAX],0FC
00C1ECF5 F0:C100 08 LOCK ROL DWORD PTR DS:[EAX],8
00C1ECF9 F1 INT1
00C1ECFA C100 14 ROL DWORD PTR DS:[EAX],14
00C1ECFD F1 INT1
...
00C1F719 803C24 00 CMP BYTE PTR SS:[ESP],0
00C1F71D 0F84 A6000000 JE 00C1F7C9 <------------------- And this one (comes after first one)!
00C1F723 33C0 XOR EAX,EAX
00C1F725 8AC2 MOV AL,DL
00C1F727 83F8 78 CMP EAX,78
00C1F72A 7F 35 JG SHORT 00C1F761
00C1F72C 74 79 JE SHORT 00C1F7A7
00C1F72E 83C0 90 ADD EAX,-70
00C1F731 83F8 07 CMP EAX,7
00C1F734 0F87 8F000000 JA 00C1F7C9
00C1F73A FF2485 41F7C100 JMP DWORD PTR DS:[EAX*4+C1F741]
...
Distance first one from loader base is +1ECE3 and second one +1F719. Why are those jumps important? Because code below them is emulating api's. If those jumps are patched to JMP, all import jumps of that type will now look like this:
00C42FAE 68 E7E3E777 PUSH kernel32.GetStdHandle
00C42FB3 C3 RETN
00C42FB4 68 39D6D677 PUSH user32.GetKeyboardType
00C42FB9 C3 RETN
00C42FBA 68 FB6DD477 PUSH user32.LoadStringA
00C42FBF C3 RETN
00C42FC0 68 64B0D677 PUSH user32.MessageBoxA
00C42FC5 C3 RETN
...
And this is combination which ImpREC can easily find and repair. But jumps are still pointing to allocated block JMP DWORD PTR DS:[00D36F14]. There is way to fix that too. Restarting , placing memory breakpoints on address where import jump should be, stoppin couple times and finally I break on place where jumps are redirected:
00C1E457 037D 00 ADD EDI,DWORD PTR SS:[EBP]
00C1E45A 890F MOV DWORD PTR DS:[EDI],ECX <------ Here it changes jump pointers!
00C1E45C 40 INC EAX
00C1E45D 4A DEC EDX
00C1E45E ^75 D0 JNZ SHORT 00C1E430
00C1E460 43 INC EBX
00C1E461 FF0C24 DEC DWORD PTR SS:[ESP]
00C1E464 ^75 B2 JNZ SHORT 00C1E418
00C1E466 5A POP EDX
00C1E467 5D POP EBP
00C1E468 5F POP EDI
00C1E469 5E POP ESI
00C1E46A 5B POP EBX
00C1E46B C3 RETN
That line will change
004012AC FF25 18326000 JMP DWORD PTR DS:[603218]
to
004012AC -FF25 0B6FD300 JMP DWORD PTR DS:[D36F0B]
Let's remember this address too 00C1E457 or +1E45C. I have fixed that problem with injecting little code. Instead these two lines, I placed jump to empty space where I will add couple more lines:
00C1E457 037D 00 ADD EDI,DWORD PTR SS:[EBP]
00C1E45A 890F MOV DWORD PTR DS:[EDI],ECX
00C1E457 E9 CE8A0100 JMP 00C36F2A <------- I'm redirecting to empty space.
There I will inject some code. This code will not change jumps (I excluded MOV DWORD PTR DS:[EDI],ECX line which is doing that), it will place pointers in original IAT section.
00C36F2A 037D 00 ADD EDI,DWORD PTR SS:[EBP] <-- Old line copied.
00C36F2D 60 PUSHAD
00C36F2E 8B07 MOV EAX,DWORD PTR DS:[EDI]
00C36F30 8B09 MOV ECX,DWORD PTR DS:[ECX]
00C36F32 8908 MOV DWORD PTR DS:[EAX],ECX
00C36F34 61 POPAD
00C36F35 ^E9 2275FEFF JMP 00C1E45C <---------------- Return to main code.
I will fix fully emulated API's later, that is harder problem.
7. Dumping and repairing
And finally, I have now enough informations for making dump that is worth to dump. Let's repeat important addresses because I must all over again restart target:
Jump_to_loader_code = 0066CF71
Loader_Image_Base = 00C00000 (on my machine)
Loader_OEP = 00C2720C (on my machine)
Loader_relative_OEP = +2720C
Internal_check = +1FEE8 (00C1FEE8 on my machine)
Section_check = +1DA11 (00C1DA11 on my machine)
Jump_to_stolen_code = +20565 (00C20565 on my machine)
Code_relocation = +26AB4 (00C26AB4 on my machine)
First_import_jump = +1ECE3 (00C1ECE3 on my machine)
Second_import_jump = +1F71D (00C1F71D on my machine)
Jumps_redirection = +1E457 (00C1E457 on my machine)
To make dump we need to break on jumping to leader dll (0066CF71) , pass three times that internal integrity check (+1FEE8 from loader base) and then place breakpoints on Code_relocation, First_import_jump, Second_import_jump and Jumps_redirection where we need to inject that code. We must leave breakpoint on Internal_check. Then we must stop on every place and change all jumps, values and ijnect code like described before. After that, we will break on Internal_check for the last time. Then we need to undo all changes in loader code (jus select whole section and undo changes) and pass Internal_check for the last time. Continue to that RETN described before that should throw us to stolen code. But something happended and that RETN will lead to wrong address (to nowere , something like F453CDE5). I don't know reason for this, but it isn't important if you already know where stolen code starts. Go there and put new origin there and that's it. Break on fasle OEP, dump file , run ImpREC. In ImpREC use "Trace Level1" to find invaid imports. There should be ~10 invalid ones. Those are emulated API's. I didn't cut them, instead I saved import tree to hard disk.
I spend some time finding those API's, tracing trough original program and looking in some other Delphi programs. I managed to find them all:
004062B4 -FF25 64326000 JMP DWORD PTR DS:[603264]
DS:[00603264]=00C43002 --> GetModuleHandleA
004013B4 -FF25 B8316000 JMP DWORD PTR DS:[6031B8]
DS:[006031B8]=00C42F18 --> GetVersion
00401314 -FF25 EC316000 JMP DWORD PTR DS:[6031EC]
DS:[006031EC]=00C42F66 --> GetModuleHandleA
0040671C -FF25 48336000 JMP DWORD PTR DS:[603348]
DS:[00603348]=00C4314C --> GetModuleHandleA
0040677C -FF25 18336000 JMP DWORD PTR DS:[603318]
DS:[00603318]=00C43104 --> GetVersion
004066CC -FF25 70336000 JMP DWORD PTR DS:[603370]
DS:[00603370]=00C43188 --> GetCurrentProcessId
004067F4 -FF25 DC326000 JMP DWORD PTR DS:[6032DC]
DS:[006032DC]=00C430AA --> LoadLibraryA
00406804 -FF25 D4326000 JMP DWORD PTR DS:[6032D4]
DS:[006032D4]=00C4309E --> LockResource - SetHandleCount
Some are repeating. This one LockResource it seams that it doesn't exits on XP because it uses SetHandleCount (I have no idea :-?). What I have done is, I just wrote missing one in saved import tree. Then I load it in ImpREC and fixed dump. After that I added new section and placed stolen OEP's code and set OEP to be there in LordPE. And that's it :)
8. Final words
I sad that I will describe inline patching, but I have no will for that after all this writing. Unpacking this protector was very time consuming so I wrote two scripts that can help you. First script will find where is stolen code and OEP. Remember those values because second script that fixes imports needs that information. So, run first one, then restart an use second one. I also include Import_Tree.txt so you can fix all imports.
Protector has trial options which can be easily defeated. Using Regmon you can find where trial info is stored. Breaking on RegCreateKeyExA you can find where is procedure that checks trial limitations. Protector desn't check PE header so you can add new sections which makes inline patching easier. I don't know did I forgot something, but I guess that is all.
-
Level : intermediate
=======================
ACProtect 1.09 - manually unpacking
=======================
ACProtect is not bad protector. It has some dumb things , but it also has some good features. This tutorial will explain unpacking of version 1.09 altough at first look his version looks like 1.41. I guess that they are all very simular.
Problems that we have to solve:
- Find OEP,
- Avoid IsDebuggerPresent check,
- Avoid CreateToolhep32Snapshot check,
- Restore stolen OEP bytes,
- Fix imports,
- Fix stolen code.
1. Avoid debugger tricks and find OEP
Use some plugin to hide olly from IsDebuggerPresent api.
Then go to CreateToolhelp32Snapshot:
77E999A6 > 55 PUSH EBP
77E999A7 8BEC MOV EBP,ESP
77E999A9 83EC 0C SUB ESP,0C
77E999AC 56 PUSH ESI
77E999AD 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C]
77E999B0 85F6 TEST ESI,ESI
77E999B2 0F84 52E60000 JE kernel32.77EA800A
77E999B8 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
77E999BB 50 PUSH EAX
77E999BC 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8]
77E999BF 50 PUSH EAX
...
...
And patch it:
77E999A6 > 33C0 XOR EAX,EAX
77E999A8 90 NOP
77E999A9 90 NOP
77E999AA 90 NOP
77E999AB 90 NOP
...
...
77E99A15 90 NOP
77E99A16 90 NOP
77E99A17 C2 0800 RETN 8 <------- Place bp here.
This api retrieves snapshot of all processes, heaps and threads (depends of parameters). If EAX=0, protector will think that there are no processes running. Smart protector :)
Place bp's and run. You will two times break on bp. When you break second time, check in memory map first section (select disassemble view):
00401000 56 PUSH ESI
00401001 57 PUSH EDI
00401002 8BF1 MOV ESI,ECX
00401004 E8 996A0300 CALL UnPackMe.00437AA2
00401009 8D7E 3C LEA EDI,DWORD PTR DS:[ESI+3C]
0040100C C706 74B14400 MOV DWORD PTR DS:[ESI],UnPackMe.0044B174 ; ASCII "IMD"
00401012 8BCF MOV ECX,EDI
00401014 E8 68070000 CALL UnPackMe.00401781
00401019 6A 0F PUSH 0F
0040101B C706 ACB04400 MOV DWORD PTR DS:[ESI],UnPackMe.0044B0AC ; ASCII "IMD"
00401021 C707 9CB04400 MOV DWORD PTR DS:[EDI],UnPackMe.0044B09C
00401027 FF15 800E4600 CALL DWORD PTR DS:[460E80]
0040102D 50 PUSH EAX
0040102E 8BCF MOV ECX,EDI
00401030 E8 4A080000 CALL UnPackMe.0040187F
00401035 8BC6 MOV EAX,ESI
00401037 5F POP EDI
...
...
Code is decrypted! ACProtector desn't encrypt sections, it only has packing option. If target is not packed, you can make inline patch imidialtey, but I don't remember anymore if there was some CRC checks.
Place memory bp on access on first section and run. You will break on false OEP:
004271B5 . 68 600E4500 PUSH UnPackMe.00450E60
004271BA . 68 C8924200 PUSH UnPackMe.004292C8 ; SE handler installation
004271BF . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
004271C5 . 50 PUSH EAX
004271C6 . 64:8925 000000>MOV DWORD PTR FS:[0],ESP
004271CD . 83C4 A8 ADD ESP,-58
004271D0 . 53 PUSH EBX
004271D1 . 56 PUSH ESI
004271D2 . 57 PUSH EDI ; ntdll.77F5164E
004271D3 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
004271D6 . FF15 DC0A4600 CALL DWORD PTR DS:[460ADC] ; UnPackMe.0046B492
004271DC . 33D2 XOR EDX,EDX
004271DE . 8AD4 MOV DL,AH
004271E0 . 8915 34E64500 MOV DWORD PTR DS:[45E634],EDX
004271E6 . 8BC8 MOV ECX,EAX
004271E8 . 81E1 FF000000 AND ECX,0FF
004271EE . 890D 30E64500 MOV DWORD PTR DS:[45E630],ECX
004271F4 . C1E1 08 SHL ECX,8
004271F7 . 03CA ADD ECX,EDX
004271F9 . 890D 2CE64500 MOV DWORD PTR DS:[45E62C],ECX
004271FF . C1E8 10 SHR EAX,10
...
...
This looks like some VC++ 5-6. Scroll up and you'll see that five bytes are destroyed:
004271B0 4B DB 4B ; CHAR 'K'
004271B1 81 DB 81
004271B2 73 DB 73 ; CHAR 's'
004271B3 F2 DB F2
004271B4 19 DB 19
Just 5 bytes missing, you probably already know what one goes there, but we will lates talk about this.
2. Imports problem
You can also see that imports are obfuscated, check this call which should lead to GetVersion:
004271D6 . FF15 DC0A4600 CALL DWORD PTR DS:[460ADC] ; UnPackMe.0046B492
Instead it jumps to protector section at:
0046B492 68 DECED170 PUSH 70D1CEDE
0046B497 813424 4B1E3607 XOR DWORD PTR SS:[ESP],7361E4B
0046B49E C3 RETN
Easy, 70D1CEDE XOR 7361E4B = GetVersion address.
You will notice whole table of this jumps. We could easy write script for olly that would fix all these jumps and XOR-ing. But we will later see that we can just prevent this jump redirection.
3. Stolen code
This is at first look heavy problem. Below import table, you can find new redirection table:
0046C0F4 00FF ADD BH,BH
0046C0F6 25 605A1600 AND EAX,165A60
0046C0FB -FF25 645A1600 JMP DWORD PTR DS:[165A64]
0046C101 -FF25 685A1600 JMP DWORD PTR DS:[165A68]
0046C107 -FF25 6C5A1600 JMP DWORD PTR DS:[165A6C]
...
...
0046D841 -FF25 E8691600 JMP DWORD PTR DS:[1669E8]
0046D847 -FF25 EC691600 JMP DWORD PTR DS:[1669EC]
0046D84D -FF25 F0691600 JMP DWORD PTR DS:[1669F0]
0046D853 -FF25 F4691600 JMP DWORD PTR DS:[1669F4]
0046D859 -FF25 F8691600 JMP DWORD PTR DS:[1669F8]
0046D85F -FF25 FC691600 JMP DWORD PTR DS:[1669FC]
This jumps points to new table:
00166A00 8BF1 MOV ESI,ECX
00166A02 8B4E 34 MOV ECX,DWORD PTR DS:[ESI+34]
00166A05 C3 RETN
00166A06 8BF1 MOV ESI,ECX
00166A08 8B4E 38 MOV ECX,DWORD PTR DS:[ESI+38]
00166A0B C3 RETN
00166A0C 8BF1 MOV ESI,ECX
00166A0E 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
00166A11 C3 RETN
...
...
001672BE 33DB XOR EBX,EBX
001672C0 8B40 0C MOV EAX,DWORD PTR DS:[EAX+C]
001672C3 C3 RETN
001672C4 8948 0C MOV DWORD PTR DS:[EAX+C],ECX
001672C7 8B0F MOV ECX,DWORD PTR DS:[EDI]
001672C9 C3 RETN
001672CA 8BF1 MOV ESI,ECX
001672CC 8B46 5C MOV EAX,DWORD PTR DS:[ESI+5C]
001672CF C3 RETN
001672D0 03C1 ADD EAX,ECX
001672D2 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8] ; ntdll.77F5164E
001672D5 C3 RETN
Ths can be easy fixed too.
4. Deprotecting target
Now we going to restart unpackme again and fix all our problems.
4.1. Fixing imports
First we need to fix import problems. Remember where GetVersion should be? At 460ADC. Go in dump and place hw bp on access there. Run and you should break here:
004743D3 8907 MOV DWORD PTR DS:[EDI],EAX
004743D5 8385 24404000 04 ADD DWORD PTR SS:[EBP+404024],4 <-------- You are here!
You are in procedure that fu**s imports. In below snippet I wrote what needs to be patched in order to prevent redirecting:
00474309 58 POP EAX
0047430A 5F POP EDI
0047430B 5E POP ESI
0047430C 81E3 FFFFFF0F AND EBX,0FFFFFFF
00474312 53 PUSH EBX
00474313 FFB5 20404000 PUSH DWORD PTR SS:[EBP+404020]
00474319 FF95 68C24100 CALL DWORD PTR SS:[EBP+41C268] ; kernel32.GetProcAddress
0047431F 3B9D 28404000 CMP EBX,DWORD PTR SS:[EBP+404028]
00474325 7C 0F JL SHORT UnPackMe.00474336
00474327 90 NOP
00474328 90 NOP
00474329 90 NOP
0047432A 90 NOP
0047432B 60 PUSHAD
0047432C 2BC0 SUB EAX,EAX
0047432E 8803 MOV BYTE PTR DS:[EBX],AL
00474330 43 INC EBX
00474331 3803 CMP BYTE PTR DS:[EBX],AL
00474333 ^75 F9 JNZ SHORT UnPackMe.0047432E
00474335 61 POPAD
00474336 0BC0 OR EAX,EAX ; kernel32.MulDiv
00474338 ^0F84 2EFFFFFF JE UnPackMe.0047426C
0047433E 3B85 78C24100 CMP EAX,DWORD PTR SS:[EBP+41C278] ; USER32.MessageBoxA
00474344 75 0A JNZ SHORT UnPackMe.00474350 <------------------------ Patch to JMP!!!
00474346 90 NOP
00474347 90 NOP
00474348 90 NOP
00474349 90 NOP
0047434A 8D85 CB454000 LEA EAX,DWORD PTR SS:[EBP+4045CB]
00474350 56 PUSH ESI ; UnPackMe.00460014
00474351 FFB5 20404000 PUSH DWORD PTR SS:[EBP+404020] ; kernel32.77E60000
00474357 5E POP ESI ; UnPackMe.0047891A
00474358 39B5 E9204000 CMP DWORD PTR SS:[EBP+4020E9],ESI ; UnPackMe.00460014
0047435E 74 15 JE SHORT UnPackMe.00474375
00474360 90 NOP
00474361 90 NOP
00474362 90 NOP
00474363 90 NOP
00474364 39B5 ED204000 CMP DWORD PTR SS:[EBP+4020ED],ESI ; UnPackMe.00460014
0047436A 74 09 JE SHORT UnPackMe.00474375
0047436C 90 NOP
0047436D 90 NOP
0047436E 90 NOP
0047436F 90 NOP
00474370 EB 60 JMP SHORT UnPackMe.004743D2
00474372 90 NOP
00474373 90 NOP
00474374 90 NOP
00474375 80BD 87A34000 00 CMP BYTE PTR SS:[EBP+40A387],0
0047437C 74 54 JE SHORT UnPackMe.004743D2 <------------ Patch to JMP!!!
0047437E 90 NOP
0047437F 90 NOP
00474380 90 NOP
00474381 90 NOP
00474382 EB 07 JMP SHORT UnPackMe.0047438B
00474384 90 NOP
00474385 90 NOP
00474386 90 NOP
00474387 0100 ADD DWORD PTR DS:[EAX],EAX ; kernel32.MulDiv
00474389 0000 ADD BYTE PTR DS:[EAX],AL
0047438B 8BB5 ED404000 MOV ESI,DWORD PTR SS:[EBP+4040ED] ; UnPackMe.0046B49F
00474391 83C6 0D ADD ESI,0D
00474394 81EE C71F4000 SUB ESI,UnPackMe.00401FC7
0047439A 2BF5 SUB ESI,EBP
0047439C 83FE 00 CMP ESI,0
0047439F 7F 31 JG SHORT UnPackMe.004743D2 <------------ Patch to JMP!!!
004743A1 90 NOP
004743A2 90 NOP
004743A3 90 NOP
004743A4 90 NOP
004743A5 8BB5 ED404000 MOV ESI,DWORD PTR SS:[EBP+4040ED] ; UnPackMe.0046B49F
004743AB 53 PUSH EBX ; UnPackMe.00461298
004743AC 50 PUSH EAX ; kernel32.MulDiv
004743AD 0F31 RDTSC
004743AF 8BD8 MOV EBX,EAX ; kernel32.MulDiv
004743B1 58 POP EAX ; UnPackMe.0047891A
004743B2 33C3 XOR EAX,EBX ; UnPackMe.00461298
004743B4 C606 68 MOV BYTE PTR DS:[ESI],68
004743B7 8946 01 MOV DWORD PTR DS:[ESI+1],EAX ; kernel32.MulDiv
004743BA C746 05 81342400 MOV DWORD PTR DS:[ESI+5],243481
004743C1 895E 08 MOV DWORD PTR DS:[ESI+8],EBX ; UnPackMe.00461298
004743C4 C646 0C C3 MOV BYTE PTR DS:[ESI+C],0C3
004743C8 5B POP EBX ; UnPackMe.0047891A
004743C9 8BC6 MOV EAX,ESI ; UnPackMe.00460014
004743CB 8385 ED404000 0D ADD DWORD PTR SS:[EBP+4040ED],0D
004743D2 5E POP ESI ; UnPackMe.0047891A
004743D3 8907 MOV DWORD PTR DS:[EDI],EAX ; kernel32.MulDiv
004743D5 8385 24404000 04 ADD DWORD PTR SS:[EBP+404024],4
004743DC ^E9 D6FEFFFF JMP UnPackMe.004742B7
004743E1 83C6 14 ADD ESI,14
004743E4 8B95 28404000 MOV EDX,DWORD PTR SS:[EBP+404028] ; UnPackMe.00400000
004743EA ^E9 38FEFFFF JMP UnPackMe.00474227
004743EF 60 PUSHAD
004743F0 E8 00000000 CALL UnPackMe.004743F5
004743F5 5E POP ESI ; UnPackMe.0047891A
004743F6 83EE 06 SUB ESI,6
004743F9 B9 F2010000 MOV ECX,1F2
004743FE 29CE SUB ESI,ECX ; ntdll.77F532FA
00474400 BA FDD5D6E3 MOV EDX,E3D6D5FD
00474405 C1E9 02 SHR ECX,2
00474408 83E9 02 SUB ECX,2
0047440B 83F9 00 CMP ECX,0
0047440E 7C 1A JL SHORT UnPackMe.0047442A
00474410 8B048E MOV EAX,DWORD PTR DS:[ESI+ECX*4]
00474413 8B5C8E 04 MOV EBX,DWORD PTR DS:[ESI+ECX*4+4]
00474417 33C3 XOR EAX,EBX ; UnPackMe.00461298
00474419 C1C8 06 ROR EAX,6
0047441C 33C2 XOR EAX,EDX ; ntdll.77FC306C
0047441E 81F2 356E723B XOR EDX,3B726E35
00474424 89048E MOV DWORD PTR DS:[ESI+ECX*4],EAX ; kernel32.MulDiv
00474427 49 DEC ECX ; ntdll.77F532FA
00474428 ^EB E1 JMP SHORT UnPackMe.0047440B
0047442A 61 POPAD
0047442B 61 POPAD
0047442C E8 2DA7FFFF CALL UnPackMe.0046EB5E
00474431 C3 RETN
If you patch all those jumps, there won't be redirection. Firts jump is if api is MessageBoxA, that api is always redirected (for some internal purpose probably). Two others needs to be patched too. I forgot to say, you need place hv bp on execution on some line in that procedure, like on
00474319 FF95 68C24100 CALL DWORD PTR SS:[EBP+41C268]
and restart target to find this again before every single import is redirected. Then patch jumps, place bp on first section and run. You will land on false OEP , but imports are OK, see:
004271B5 . 68 600E4500 PUSH UnPackMe.00450E60
004271BA . 68 C8924200 PUSH UnPackMe.004292C8 ; SE handler installation
004271BF . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
004271C5 . 50 PUSH EAX
004271C6 . 64:8925 000000>MOV DWORD PTR FS:[0],ESP
004271CD . 83C4 A8 ADD ESP,-58
004271D0 . 53 PUSH EBX
004271D1 . 56 PUSH ESI
004271D2 . 57 PUSH EDI
004271D3 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
004271D6 . FF15 DC0A4600 CALL DWORD PTR DS:[460ADC] ; kernel32.GetVersion
...
...
4.2. Fixing stolen code
Remember our jumps:
0046C0F5 -FF25 605A1600 JMP DWORD PTR DS:[165A60]
0046C0FB -FF25 645A1600 JMP DWORD PTR DS:[165A64]
0046C101 -FF25 685A1600 JMP DWORD PTR DS:[165A68]
...
...
...
0046D853 -FF25 F4691600 JMP DWORD PTR DS:[1669F4]
0046D859 -FF25 F8691600 JMP DWORD PTR DS:[1669F8]
0046D85F -FF25 FC691600 JMP DWORD PTR DS:[1669FC]
Follow first jump and you will see that it points to first obfuscation pattern in table. Every jump points to single pattern in table. All jumps are 6-bytes opcode and all patterns in table are 6-byte opcodes too:
00166A00 8BF1 MOV ESI,ECX
00166A02 8B4E 34 MOV ECX,DWORD PTR DS:[ESI+34]
00166A05 C3 RETN
00166A06 8BF1 MOV ESI,ECX
00166A08 8B4E 38 MOV ECX,DWORD PTR DS:[ESI+38]
00166A0B C3 RETN
00166A0C 8BF1 MOV ESI,ECX
00166A0E 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
00166A11 C3 RETN
...
...
...
001672C4 8948 0C MOV DWORD PTR DS:[EAX+C],ECX
001672C7 8B0F MOV ECX,DWORD PTR DS:[EDI]
001672C9 C3 RETN
001672CA 8BF1 MOV ESI,ECX
001672CC 8B46 5C MOV EAX,DWORD PTR DS:[ESI+5C]
001672CF C3 RETN
001672D0 03C1 ADD EAX,ECX
001672D2 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
001672D5 C3 RETN
So I will just binary copy-paste whole onfuscation table and replace jumps:
0046C0F5 8BF1 MOV ESI,ECX
0046C0F7 8B4E 34 MOV ECX,DWORD PTR DS:[ESI+34]
0046C0FA C3 RETN
0046C0FB 8BF1 MOV ESI,ECX
0046C0FD 8B4E 38 MOV ECX,DWORD PTR DS:[ESI+38]
0046C100 C3 RETN
...
...
0046C9BF 8BF1 MOV ESI,ECX
0046C9C1 8B46 5C MOV EAX,DWORD PTR DS:[ESI+5C]
0046C9C4 C3 RETN
0046C9C5 03C1 ADD EAX,ECX
0046C9C7 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
0046C9CA C3 RETN
0046C9CB -FF25 44601600 JMP DWORD PTR DS:[166044]
0046C9D1 -FF25 48601600 JMP DWORD PTR DS:[166048]
0046C9D7 -FF25 4C601600 JMP DWORD PTR DS:[16604C]
0046C9DD -FF25 50601600 JMP DWORD PTR DS:[166050]
0046C9E3 -FF25 54601600 JMP DWORD PTR DS:[166054]
...
...
0046D859 -FF25 F8691600 JMP DWORD PTR DS:[1669F8]
0046D85F -FF25 FC691600 JMP DWORD PTR DS:[1669FC]
But we can see that there are mush more jumps that obfuscation patterns! Rest of jumps are probably just leftover with no purpose.
4.3. Dumping
We will dump target now, but stolen OEP we will solve later. I just dumped it with LordPE. I used ImpREC to find imports and it found all of them, about 441. Neither one invalid. I fixed dump and run it, file worked OK without stolen OEP.
4.4. Finding stolen OEP bytes
Ah, this is pain in the ass. We need to trace whole day to find where it hide those bytes because ACProtect has encryption layer under layer. I made small script that will pass this layers ( script just finds jmp that jumps backwards, place bp below it and run, then again). Stolen code is here:
00485AF2 61 POPAD
00485AF3 55 PUSH EBP <------------- Stolen.
00485AF4 8BEC MOV EBP,ESP <---------- Stolen.
00485AF6 6A FF PUSH -1 <-------------- Stolen.
00485AF8 90 NOP
00485AF9 60 PUSHAD
00485AFA 60 PUSHAD
00485AFB E8 00000000 CALL UnPackMe.00485B00
00485B00 5E POP ESI ; ntdll.77F5164E
00485B01 83EE 06 SUB ESI,6
00485B04 B9 35000000 MOV ECX,35
00485B09 29CE SUB ESI,ECX
00485B0B BA FDD5D6E3 MOV EDX,E3D6D5FD
00485B10 C1E9 02 SHR ECX,2
00485B13 83E9 02 SUB ECX,2
00485B16 83F9 00 CMP ECX,0
00485B19 7C 1A JL SHORT UnPackMe.00485B35
00485B1B 8B048E MOV EAX,DWORD PTR DS:[ESI+ECX*4]
00485B1E 8B5C8E 04 MOV EBX,DWORD PTR DS:[ESI+ECX*4+4]
00485B22 33C3 XOR EAX,EBX
00485B24 C1C8 06 ROR EAX,6
00485B27 33C2 XOR EAX,EDX
00485B29 81F2 356E723B XOR EDX,3B726E35
00485B2F 89048E MOV DWORD PTR DS:[ESI+ECX*4],EAX
00485B32 49 DEC ECX
00485B33 ^EB E1 JMP SHORT UnPackMe.00485B16
00485B35 61 POPAD
In newer versions of ACProtect there are lots of this caves with stolen code and we cannot find original bytes. But we can fix that by just attaching whole this code to main image and placng oep there. But in this example I will just restore these bytes. Before:
004271B0 4B DB 4B ; CHAR 'K'
004271B1 81 DB 81
004271B2 73 DB 73 ; CHAR 's'
004271B3 F2 DB F2
004271B4 19 DB 19
004271B5 > $ 68 600E4500 PUSH dumped_.00450E60
004271BA . 68 C8924200 PUSH dumped_.004292C8 ; SE handler installation
004271BF . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
004271C5 . 50 PUSH EAX
004271C6 . 64:8925 000000>MOV DWORD PTR FS:[0],ESP
After:
004271B0 55 PUSH EBP
004271B1 8BEC MOV EBP,ESP
004271B3 6A FF PUSH -1
004271B5 > $ 68 600E4500 PUSH dumped_.00450E60
004271BA . 68 C8924200 PUSH dumped_.004292C8 ; SE handler installation
004271BF . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
004271C5 . 50 PUSH EAX
004271C6 . 64:8925 000000>MOV DWORD PTR FS:[0],ESP
Now just with LordPE set OEP to 4271B0-400000=271B0 and that's it!
Here is script for passing layers:
/////////////// Start /////////////////
LABEL01:
find eip,#0F85????FFFF#
add $RESULT,6
bp $RESULT
esto
bc eip
msgyn "One more layer?"
cmp $RESULT,0
jne LABEL01
ret
////////////// End /////////////////////
haggar 2005
-
Level : beginner
=====================
SVKP 1.32 - manually unpacking
=====================
Hi folks, this tutorial will describe unpacking of SVKP 1.32 version.
1. OEP
There is very easy way how to find OEP, olly can do it for us. In options, under SFX tab, set "Trace real entry bytewise (very slow!)" and just load exe in olly. After second or two, oly will stop on OEP:
00402259 C705 AC374000 00>MOV DWORD PTR DS:[4037AC],0
00402263 B8 01000000 MOV EAX,1
00402268 0FA2 CPUID
0040226A F7C2 00008000 TEST EDX,800000
00402270 74 0A JE SHORT unsafedi.0040227C
00402272 C705 AC374000 01>MOV DWORD PTR DS:[4037AC],1
0040227C 6A 00 PUSH 0
0040227E E8 33020000 CALL unsafedi.004024B6
00402283 A3 40B96000 MOV DWORD PTR DS:[60B940],EAX ; unsafedi.00402259
00402288 68 9C224000 PUSH unsafedi.0040229C
0040228D 6A 00 PUSH 0
0040228F 6A 67 PUSH 67
00402291 FF35 40B96000 PUSH DWORD PTR DS:[60B940]
00402297 E8 7A020000 CALL unsafedi.00402516
0040229C C8 000000 ENTER 0,0
004022A0 817D 0C 11010000 CMP DWORD PTR SS:[EBP+C],111
...
...
2. Imports redirection
Now we need to fix imports. Following first call at 40227E we will find import jumps:
0040249E $-FF25 B8C06000 JMP DWORD PTR DS:[60C0B8]
004024A4 $-FF25 BCC06000 JMP DWORD PTR DS:[60C0BC]
004024AA $-FF25 C0C06000 JMP DWORD PTR DS:[60C0C0]
004024B0 $-FF25 C4C06000 JMP DWORD PTR DS:[60C0C4]
004024B6 $-FF25 C8C06000 JMP DWORD PTR DS:[60C0C8]
004024BC $-FF25 CCC06000 JMP DWORD PTR DS:[60C0CC]
004024C2 $-FF25 D0C06000 JMP DWORD PTR DS:[60C0D0]
004024C8 $-FF25 D4C06000 JMP DWORD PTR DS:[60C0D4]
004024CE $-FF25 D8C06000 JMP DWORD PTR DS:[60C0D8]
004024D4 $-FF25 DCC06000 JMP DWORD PTR DS:[60C0DC]
004024DA $-FF25 E0C06000 JMP DWORD PTR DS:[60C0E0]
004024E0 $-FF25 E4C06000 JMP DWORD PTR DS:[60C0E4]
004024E6 $-FF25 E8C06000 JMP DWORD PTR DS:[60C0E8]
004024EC $-FF25 F0C06000 JMP DWORD PTR DS:[60C0F0]
004024F2 $-FF25 F4C06000 JMP DWORD PTR DS:[60C0F4]
004024F8 $-FF25 F8C06000 JMP DWORD PTR DS:[60C0F8]
004024FE $-FF25 FCC06000 JMP DWORD PTR DS:[60C0FC]
00402504 $-FF25 00C16000 JMP DWORD PTR DS:[60C100]
0040250A $-FF25 04C16000 JMP DWORD PTR DS:[60C104]
00402510 $-FF25 08C16000 JMP DWORD PTR DS:[60C108]
00402516 $-FF25 0CC16000 JMP DWORD PTR DS:[60C10C]
0040251C $-FF25 10C16000 JMP DWORD PTR DS:[60C110]
00402522 $-FF25 18C16000 JMP DWORD PTR DS:[60C118]
It is small target and there are just few imports. If you follow first jump you will see:
108875DC EB 01 JMP SHORT 108875DF
108875DE 93 XCHG EAX,EBX
108875DF 68 2C020000 PUSH 22C
108875E4 EB 01 JMP SHORT 108875E7
108875E6 98 CWDE
108875E7 68 60BEE977 PUSH 77E9BE60
108875EC -E9 AC3E6167 JMP kernel32.77E9B49D
...
...
Jump leads to allocated block where import is little emulated and obfuscated. In dump, we can see import table:
0060C050 46 C1 20 00 5C C1 20 00 6C C1 20 00 74 C1 20 00 F. .. .l. .t. .
0060C060 82 C1 20 00 96 C1 20 00 A4 C1 20 00 B2 C1 20 00 .. ... ... ... .
0060C070 C0 C1 20 00 D0 C1 20 00 DC C1 20 00 EA C1 20 00 .. ... ... ... .
0060C080 F6 C1 20 00 00 00 00 00 06 C2 20 00 14 C2 20 00 .. ....... ... .
0060C090 2A C2 20 00 38 C2 20 00 46 C2 20 00 52 C2 20 00 *. .8. .F. .R. .
0060C0A0 62 C2 20 00 76 C2 20 00 88 C2 20 00 00 00 00 00 b. .v. ... .....
0060C0B0 98 C2 20 00 00 00 00 00 DC 75 88 10 F1 75 88 10 .. ......u...u..
0060C0C0 FF 75 88 10 85 20 87 10 FC 33 87 10 09 76 88 10 .u... ...3...v..
0060C0D0 0E 76 88 10 1C 76 88 10 32 76 88 10 40 76 88 10 .v...v..2v..@v..
0060C0E0 52 76 88 10 60 76 88 10 72 76 88 10 00 00 00 00 Rv..`v..rv......
0060C0F0 77 76 88 10 8D 76 88 10 9B 76 88 10 A0 76 88 10 wv...v...v...v..
0060C100 A5 76 88 10 B3 76 88 10 B8 76 88 10 C1 76 88 10 .v...v...v...v..
0060C110 D7 76 88 10 00 00 00 00 E9 76 88 10 00 00 00 00 .v.......v......
3. Fixing imports
Imports can be fixed very easy too. First remove that tracing option in Olly, set it to "Stop at entry of self-extractor." and then restart target in olly. Then set bp on the end of GetModuleHandleA api and run:
77E7ACD9 > 837C24 04 00 CMP DWORD PTR SS:[ESP+4],0
77E7ACDE 0F84 37010000 JE kernel32.77E7AE1B
77E7ACE4 FF7424 04 PUSH DWORD PTR SS:[ESP+4]
77E7ACE8 E8 F8050000 CALL kernel32.77E7B2E5
77E7ACED 85C0 TEST EAX,EAX
77E7ACEF 74 08 JE SHORT kernel32.77E7ACF9
77E7ACF1 FF70 04 PUSH DWORD PTR DS:[EAX+4]
77E7ACF4 E8 27060000 CALL kernel32.GetModuleHandleW
77E7ACF9 C2 0400 RETN 4 <---------------------------- Bp here!!!
Remove bp and place now meory brekpoint on access to that import table in dump (below snippet). Jut select all bytes from 60C050 to 60C11C and place mem. bp. Then run.
0060C050 46 C1 20 00 5C C1 20 00 6C C1 20 00 74 C1 20 00 F. .. .l. .t. .
0060C060 82 C1 20 00 96 C1 20 00 A4 C1 20 00 B2 C1 20 00 .. ... ... ... .
0060C070 C0 C1 20 00 D0 C1 20 00 DC C1 20 00 EA C1 20 00 .. ... ... ... .
0060C080 F6 C1 20 00 00 00 00 00 06 C2 20 00 14 C2 20 00 .. ....... ... .
0060C090 2A C2 20 00 38 C2 20 00 46 C2 20 00 52 C2 20 00 *. .8. .F. .R. .
0060C0A0 62 C2 20 00 76 C2 20 00 88 C2 20 00 00 00 00 00 b. .v. ... .....
0060C0B0 98 C2 20 00 00 00 00 00 46 C1 20 00 5C C1 20 00 .. .....F. .. .
0060C0C0 6C C1 20 00 74 C1 20 00 82 C1 20 00 96 C1 20 00 l. .t. ... ... .
0060C0D0 A4 C1 20 00 B2 C1 20 00 C0 C1 20 00 D0 C1 20 00 .. ... ... ... .
0060C0E0 DC C1 20 00 EA C1 20 00 F6 C1 20 00 00 00 00 00 .. ... ... .....
0060C0F0 06 C2 20 00 14 C2 20 00 2A C2 20 00 38 C2 20 00 .. ... .*. .8. .
0060C100 46 C2 20 00 52 C2 20 00 62 C2 20 00 76 C2 20 00 F. .R. .b. .v. .
0060C110 88 C2 20 00 00 00 00 00 98 C2 20 00 .. ....... .
First break is where protector takes dword from table:
107D4D16 8B18 MOV EBX,DWORD PTR DS:[EAX]
Second stop is where it writes new pointer in table, this is point of our interest:
107D6D00 8907 MOV DWORD PTR DS:[EDI],EAX
You can remove mem. bp now.
We are at the place where new pointer is written. But check stack window:
0012FF74 0060C0B8 unsafedi.0060C0B8
0012FF78 0060C000 unsafedi.0060C000
0012FF7C 107B0000
0012FF80 0012FF94
0012FF84 0060C148 unsafedi.0060C148
0012FF88 00400000 ASCII "MZP"
0012FF8C 77E7B534 RETURN to kernel32.77E7B534 from kernel32.77E7A2F2
0012FF90 77E9B493 kernel32.OutputDebugStringA
In stack you can see what API is redirected, in this case it is OutputDebugStringA. We can change few bytes in protector code in order to trick protector. But we need little place for injection so I will just NOP couple junk opcodes to make space. Scroll up and check:
107D6CF7 EB 02 JMP SHORT 107D6CFB
107D6CF9 CD 20 INT 20
107D6CFB 58 POP EAX ; unsafedi.0060C0B8
107D6CFC EB 02 JMP SHORT 107D6D00
107D6CFE 0FE889 077C03EB PSUBSB MM1,QWORD PTR DS:[ECX+EB037C07]
107D6D05 03E9 ADD EBP,ECX
107D6D07 ^74 FB JE SHORT 107D6D04
107D6D09 61 POPAD
After patching junk code:
107D6CF7 90 NOP
107D6CF8 90 NOP
107D6CF9 90 NOP
107D6CFA 90 NOP
107D6CFB 58 POP EAX ; unsafedi.0060C0B8
107D6CFC 90 NOP
107D6CFD 90 NOP
107D6CFE 90 NOP
107D6CFF 90 NOP
107D6D00 8907 MOV DWORD PTR DS:[EDI],EAX
I will move POP EAX to the top and inject line that will give to EAX correct api from stack:
107D6CF7 58 POP EAX ; unsafedi.0060C0B8
107D6CF8 8B4424 1C MOV EAX,DWORD PTR SS:[ESP+1C] ; kernel32.OutputDebugStringA
107D6CFC 90 NOP
107D6CFD 90 NOP
107D6CFE 90 NOP
107D6CFF 90 NOP
107D6D00 8907 MOV DWORD PTR DS:[EDI],EAX
And final touch, right click on 107D6CF8 line and select "New origin here", and injection is done.
To stop on OEP, go to 402259 and place bp there and run. When you break, check import jumps:
0040249E $-FF25 B8C06000 JMP DWORD PTR DS:[60C0B8] ; kernel32.OutputDebugStringA
004024A4 $-FF25 BCC06000 JMP DWORD PTR DS:[60C0BC] ; kernel32.CreateThread
004024AA $-FF25 C0C06000 JMP DWORD PTR DS:[60C0C0] ; kernel32.Sleep
004024B0 $-FF25 C4C06000 JMP DWORD PTR DS:[60C0C4]
004024B6 $-FF25 C8C06000 JMP DWORD PTR DS:[60C0C8]
004024BC $-FF25 CCC06000 JMP DWORD PTR DS:[60C0CC] ; kernel32.CloseHandle
004024C2 $-FF25 D0C06000 JMP DWORD PTR DS:[60C0D0] ; kernel32.CreateFileA
004024C8 $-FF25 D4C06000 JMP DWORD PTR DS:[60C0D4] ; kernel32.GetFileSize
004024CE $-FF25 D8C06000 JMP DWORD PTR DS:[60C0D8] ; kernel32.VirtualAlloc
004024D4 $-FF25 DCC06000 JMP DWORD PTR DS:[60C0DC] ; kernel32.ReadFile
004024DA $-FF25 E0C06000 JMP DWORD PTR DS:[60C0E0] ; kernel32.VirtualFree
004024E0 $-FF25 E4C06000 JMP DWORD PTR DS:[60C0E4] ; kernel32.WriteFile
004024E6 $-FF25 E8C06000 JMP DWORD PTR DS:[60C0E8] ; kernel32.GetTickCount
004024EC $-FF25 F0C06000 JMP DWORD PTR DS:[60C0F0] ; USER32.CharLowerA
004024F2 $-FF25 F4C06000 JMP DWORD PTR DS:[60C0F4] ; USER32.SendDlgItemMessageA
004024F8 $-FF25 F8C06000 JMP DWORD PTR DS:[60C0F8] ; USER32.MoveWindow
004024FE $-FF25 FCC06000 JMP DWORD PTR DS:[60C0FC] ; USER32.MessageBoxA
00402504 $-FF25 00C16000 JMP DWORD PTR DS:[60C100] ; USER32.LoadIconA
0040250A $-FF25 04C16000 JMP DWORD PTR DS:[60C104] ; USER32.GetWindowRect
00402510 $-FF25 08C16000 JMP DWORD PTR DS:[60C108] ; USER32.GetDesktopWindow
00402516 $-FF25 0CC16000 JMP DWORD PTR DS:[60C10C] ; USER32.DialogBoxParamA
0040251C $-FF25 10C16000 JMP DWORD PTR DS:[60C110] ; USER32.SendMessageA
00402522 $-FF25 18C16000 JMP DWORD PTR DS:[60C118]
Two API's are wrong !?! Last jump at 402522 is good, but those two at the begining lead to protector's code. What's the problem? SVKP uses another procedures for those two API's. I placed memory bp's on these two addresses and found that missing two API's are:
004024B0 .-FF25 0CF06100 JMP DWORD PTR DS:[<&kernel32.ExitProcess>; kernel32.ExitProcess
004024B6 $-FF25 10F06100 JMP DWORD PTR DS:[<&kernel32.GetModuleHa>; kernel32.GetModuleHandleA
Only thing that is left is to dump and rebuild imports with ImpREC. Dumped file is lot bigger than packed, it's data section is pretty big, author of app coded it like that so I will just leave it. But I removed SVKP secion, the last one.
-
Level : intermediate
=======================
Thinstall 2.521 - manually unpacking
=======================
1. Introduction
Hi folks and welcome to new unpacking tutorial! Today we will see how can Thinstall with double process protection be easily unpacked. I was losing my nerves with this one, but after some time I realised that protection can be tricked with cheap trick. There is one tutorial for single process Thinstall on ARTEAM written by MaDMAn_H3rCul3s which was helpfull to me, so you can also check that tutorial too.
Tools and target:
- OllyDbg 1.10
- ImpREC
- LordPE
2. Killing Debug Blocker and finding OEP
As I sad, thinstall has double process protection which is acting like debug blocker feature and it doesn't alowes us to debug process of unpacking. Thinstall own debugger (first process) is creating second process, a debugee, and that second proces is performing unpacking. We need to debug that process instead first one. I was busting my ass trying to figure out how to god damn detach processes, but thanks to my pathetic knowledge about debugging I couldn't find the way. But I was thinking a little; since thinstall can be single protection too, maybe there is way to force double process protection to single one? I was examning this thinstall target and notice that debugger process doesn't use ReadProcessMemory and WriteProcessMemory api's, so debugger proces is just for preventing debugging and it doesn't do anything else. Second process is one that is doing all job. And I found easy way to force thinstal to think that it is single process protection. Lets go :)
First, use some plugin to hide Olly from IsDebuggerPresent API because thinstall checks this one million times. Open target in Olly and place breakpoint on CreateProcessA. Run target and when you break in kernel return to user code. You will find your self in double process protection loop. Bellow snippet is that whole block of code. You can see that we are in some allocated memory block. Since we are in CreateProcessA part, double process protection has already started. What we need is to break before that API is called. Place hardware bp on execution on this first line below and reastart Olly. Run and you should break at start:
7FF8600A PUSH EBP <-------------------- START!!! Place hw bp on execution here and resart to break!!!
7FF8600B MOV EBP,ESP
7FF8600D SUB ESP,0E0
...
cut
...
7FF86100 TEST EAX,EAX
7FF86102 JNZ SHORT 7FF8610C
7FF86104 PUSH 0
7FF86106 CALL DWORD PTR DS:[7FF972D4] ; kernel32.ExitProcess
7FF8610C CMP DWORD PTR DS:[7FFA6FE8],0
7FF86113 JE SHORT 7FF86124
7FF86115 CALL DWORD PTR DS:[7FFA6FE8] ; kernel32.IsDebuggerPresent
7FF8611B TEST EAX,EAX
7FF8611D JE SHORT 7FF86124
7FF8611F CALL 7FF6ED69
7FF86124 PUSH DWORD PTR SS:[EBP-B4]
7FF8612A PUSH 7FF9CEFC ; ASCII "%d.df"
7FF8612F LEA EAX,DWORD PTR SS:[EBP-CC]
7FF86135 PUSH EAX
...
cut
...
7FF86220 PUSH 0
7FF86222 PUSH 0
7FF86224 PUSH 0
7FF86226 PUSH 7FF85D12
7FF8622B PUSH 0
7FF8622D PUSH 0
7FF8622F CALL DWORD PTR DS:[7FF97218] ; kernel32.CreateThread
7FF86235 LEAVE
7FF86236 RETN
When you stop at hw bp, remove it and we can go to fooling thinstall. Check this part imidiatley below our bp:
7FF86047 CALL DWORD PTR DS:[7FF9709C] ; kernel32.GetVersionExA
7FF8604D MOV EAX,DWORD PTR DS:[7FFA69AC]
7FF86052 AND EAX,2000000
7FF86057 TEST EAX,EAX
7FF86059 JE 7FF86212
Here protector calls GetVersionExA to check does our system sopports something. It seams that it checks does system support IsDebuggerPresent API. That API is supported only by NT windows family. It seams that thinstall can have double process only on NT computers !?! Anyway, to trick thinstall, change JE to JMP or change Z flag to 1. This way jump will be executed and it will jump across whole double protection procedure, here:
7FF86212 MOV EAX,DWORD PTR DS:[7FFA69AC]
7FF86217 AND EAX,4000008
7FF8621C TEST EAX,EAX
7FF8621E JE SHORT 7FF86235
7FF86220 PUSH 0
7FF86222 PUSH 0
7FF86224 PUSH 0
7FF86226 PUSH 7FF85D12
7FF8622B PUSH 0
7FF8622D PUSH 0
7FF8622F CALL DWORD PTR DS:[7FF97218] ; kernel32.CreateThread
7FF86235 LEAVE
7FF86236 RETN
Ok, debug blocker is killed and we have to find OEP now. I sow this part in MaDMAn_H3rCul3s tutorial. Just search all string references and find this string
ASCII "APISPY: Calling EXE Entry Point %x
"
Double click on it and you will land:
7FF52C50 PUSH 7FF97660 ; ASCII "APISPY: Calling EXE Entry Point %x
"
7FF52C55 CALL 7FF54550
7FF52C5A POP ECX
7FF52C5B POP ECX
7FF52C5C CALL DWORD PTR SS:[EBP-3B8] <------------ Place bp here and run!!!
Place bp on that CALL below because that is OEp of packed program. Run and enter call:
004010E3 PUSH 0
004010E5 CALL Thinstal.00401786
004010EA MOV DWORD PTR DS:[406D30],EAX
004010EF PUSH 0
004010F1 PUSH Thinstal.0040110B
004010F6 PUSH 0
004010F8 PUSH 1
004010FA PUSH DWORD PTR DS:[406D30]
00401100 CALL Thinstal.004016FC
00401105 PUSH EAX
00401106 CALL Thinstal.0040177A
0040110B PUSH EBP
0040110C MOV EBP,ESP
0040110E PUSHAD
0040110F MOV EAX,DWORD PTR SS:[EBP+8]
00401112 MOV DWORD PTR DS:[406D44],EAX
00401117 CMP DWORD PTR SS:[EBP+C],110
0040111E JNZ Thinstal.004011FD
00401124 PUSH Thinstal.00406BA1 ; ASCII "MoleBox Pro"
00401129 PUSH DWORD PTR DS:[406D44]
0040112F CALL Thinstal.00401762 ; JMP to USER32.SetWindowTextA
00401134 PUSH Thinstal.0040604C ; ASCII "-= Del MoleBox Pro Sign =-"
00401139 PUSH 69
0040113B PUSH DWORD PTR DS:[406D44]
00401141 CALL Thinstal.00401756 ; JMP to USER32.SetDlgItemTextA
00401146 PUSH Thinstal.004060B0 ; ASCII "<a href="http://www.kanxue.cn">http://www.kanxue.cn</a>";
0040114B PUSH 68
...
...
And there it is ;)
3. Problem with imports
If you follow this line:
0040112F CALL Thinstal.00401762 ; JMP to USER32.SetWindowTextA
you will find import jumps:
004016F6 JMP DWORD PTR DS:[4050AC] ; USER32.CallWindowProcA
004016FC JMP DWORD PTR DS:[4050B0]
00401702 JMP DWORD PTR DS:[4050B4] ; USER32.EndDialog
00401708 JMP DWORD PTR DS:[4050B8] ; USER32.GetActiveWindow
0040170E JMP DWORD PTR DS:[4050BC] ; USER32.GetCapture
00401714 JMP DWORD PTR DS:[4050C0] ; USER32.GetCursorPos
0040171A JMP DWORD PTR DS:[4050C4] ; USER32.GetDlgCtrlID
00401720 JMP DWORD PTR DS:[4050C8] ; USER32.GetDlgItem
00401726 JMP DWORD PTR DS:[4050A8] ; USER32.GetParent
0040172C JMP DWORD PTR DS:[4050A4] ; USER32.GetWindowRect
00401732 JMP DWORD PTR DS:[4050A0] ; USER32.InvalidateRect
00401738 JMP DWORD PTR DS:[40509C] ; USER32.MessageBoxA
0040173E JMP DWORD PTR DS:[405098] ; USER32.PtInRect
00401744 JMP DWORD PTR DS:[405094] ; USER32.ReleaseCapture
0040174A JMP DWORD PTR DS:[405090]
00401750 JMP DWORD PTR DS:[40508C] ; USER32.SetCapture
00401756 JMP DWORD PTR DS:[405088] ; USER32.SetDlgItemTextA
0040175C JMP DWORD PTR DS:[405080] ; USER32.SetWindowLongA
00401762 JMP DWORD PTR DS:[405084] ; USER32.SetWindowTextA
00401768 JMP DWORD PTR DS:[405044]
0040176E JMP DWORD PTR DS:[405048]
00401774 JMP DWORD PTR DS:[40504C]
0040177A JMP DWORD PTR DS:[405050]
00401780 JMP DWORD PTR DS:[405054]
00401786 JMP DWORD PTR DS:[405058]
0040178C JMP DWORD PTR DS:[40505C] ; kernel32.GlobalAlloc
00401792 JMP DWORD PTR DS:[405060] ; kernel32.GlobalFree
00401798 JMP DWORD PTR DS:[405064] ; kernel32.GlobalLock
0040179E JMP DWORD PTR DS:[405068] ; kernel32.GlobalUnlock
004017A4 JMP DWORD PTR DS:[40506C]
004017AA JMP DWORD PTR DS:[405070]
004017B0 JMP DWORD PTR DS:[405038] ; kernel32.lstrcpyA
004017B6 JMP DWORD PTR DS:[40503C] ; kernel32.lstrlenA
004017BC JMP DWORD PTR DS:[405078]
004017C2 JMP DWORD PTR DS:[40501C] ; GDI32.CreateFontIndirectA
004017C8 JMP DWORD PTR DS:[405018] ; GDI32.GetObjectA
004017CE JMP DWORD PTR DS:[405014] ; GDI32.GetStockObject
004017D4 JMP DWORD PTR DS:[405008] ; GDI32.SelectObject
004017DA JMP DWORD PTR DS:[40500C] ; GDI32.SetBkMode
004017E0 JMP DWORD PTR DS:[405010] ; GDI32.SetTextColor
004017E6 JMP DWORD PTR DS:[405000] ; comdlg32.GetOpenFileNameA
As you can see, some of them do not lead to API's. Thinstall has some kind of redirection/emulation. Altough in MaDMAn_H3rCul3s tutorial imports was easy to fix, here is that problem harder. Open ImpREC and find imports. You will see that there is some invalid ones (13 in this case). First, DO NOT USE "Trace level 1" option in ImpREC, or any other! You will get incorrect imports! I have traced like lunatic through protector code in hope that I will found where imports are written, but my memory and hardware breakpoints was ignored and I just couldn't find redirection algo. That means we will have to manually fix imports. Not really manually, ImpREC and Olly will help us, but this approach will take some time.
This part reminds me to armadillo's redirection. Let's take a look. Check this jump in Olly:
004016FC JMP DWORD PTR DS:[4050B0]
Select it with left click and press ENTER to follow it in disassembler:
7FF5FE17 PUSH EBP
7FF5FE18 MOV EBP,ESP
7FF5FE1A PUSH ECX
7FF5FE1B PUSH DWORD PTR SS:[EBP+18]
7FF5FE1E PUSH DWORD PTR SS:[EBP+14]
7FF5FE21 PUSH DWORD PTR SS:[EBP+10]
7FF5FE24 PUSH DWORD PTR SS:[EBP+C]
7FF5FE27 PUSH DWORD PTR SS:[EBP+8]
7FF5FE2A CALL 7FF5E28D
7FF5FE2F POP ECX
7FF5FE30 PUSH EAX
7FF5FE31 CALL DWORD PTR DS:[7FF97358] ; USER32.DialogBoxParamA
7FF5FE37 MOV DWORD PTR SS:[EBP-4],EAX
7FF5FE3A MOV EAX,DWORD PTR SS:[EBP-4]
7FF5FE3D LEAVE
7FF5FE3E RETN 14
There, dou you see? There is some protectors code and then you can see call to DialogBoxParamA. DialogBoxParamA is API that is stolen.
Let's see another example. This one is more complex, but you must get feeling. check this jump:
00401768 JMP DWORD PTR DS:[405044]
Press ENTER to follow it:
7FF63A61 MOV EAX,7FF94A55
7FF63A66 CALL 7FF89F08
7FF63A6B SUB ESP,54
7FF63A6E MOV EAX,DWORD PTR SS:[EBP+8]
7FF63A71 CMP EAX,DWORD PTR DS:[7FFA3480]
7FF63A77 JNZ SHORT 7FF63A91
7FF63A79 PUSH DWORD PTR SS:[EBP+8]
7FF63A7C CALL DWORD PTR DS:[7FF970B0] ; kernel32.CloseHandle
7FF63A82 OR DWORD PTR DS:[7FFA3480],FFFFFFFF
7FF63A89 PUSH 1
7FF63A8B POP EAX ; 7FF52C62
7FF63A8C JMP 7FF63C57
7FF63A91 PUSH 1
7FF63A93 PUSH 0
7FF63A95 PUSH DWORD PTR SS:[EBP+8]
7FF63A98 CALL 7FF60AC2
7FF63A9D ADD ESP,0C
7FF63AA0 MOV DWORD PTR SS:[EBP-10],EAX
7FF63AA3 CMP DWORD PTR SS:[EBP-10],0
7FF63AA7 JE 7FF63BD4
7FF63AAD MOV EAX,DWORD PTR DS:[7FFA3154]
7FF63AB2 MOV EAX,DWORD PTR DS:[EAX]
7FF63AB4 AND EAX,100000
7FF63AB9 NEG EAX
7FF63ABB SBB EAX,EAX
7FF63ABD NEG EAX
7FF63ABF MOVZX EAX,AL
7FF63AC2 TEST EAX,EAX
7FF63AC4 JE SHORT 7FF63AD5
7FF63AC6 PUSH DWORD PTR SS:[EBP+8]
7FF63AC9 PUSH 7FF9A114 ; ASCII "CloseHandle (internal) : h=%x
"
7FF63ACE CALL 7FF54550
7FF63AD3 POP ECX ; 7FF52C62
7FF63AD4 POP ECX ; 7FF52C62
7FF63AD5 MOV EAX,DWORD PTR SS:[EBP-10]
7FF63AD8 CMP DWORD PTR DS:[EAX],-1
7FF63ADB JE SHORT 7FF63AEE
7FF63ADD MOV EAX,DWORD PTR SS:[EBP-10]
7FF63AE0 PUSH DWORD PTR DS:[EAX]
7FF63AE2 CALL DWORD PTR DS:[7FF970B0] ; kernel32.CloseHandle
7FF63AE8 MOV EAX,DWORD PTR SS:[EBP-10]
7FF63AEB OR DWORD PTR DS:[EAX],FFFFFFFF
7FF63AEE CALL DWORD PTR DS:[7FF970A8] ; ntdll.RtlGetLastWin32Error
7FF63AF4 MOV DWORD PTR SS:[EBP-14],EAX
7FF63AF7 MOV EAX,DWORD PTR SS:[EBP-10]
7FF63AFA MOV EAX,DWORD PTR DS:[EAX+4]
7FF63AFD MOV EAX,DWORD PTR DS:[EAX+28]
7FF63B00 SHR EAX,13
7FF63B03 AND EAX,3
7FF63B06 CMP EAX,2
7FF63B09 JNZ SHORT 7FF63B60
7FF63B0B MOV EAX,DWORD PTR SS:[EBP-10]
7FF63B0E MOV EAX,DWORD PTR DS:[EAX+4]
7FF63B11 CMP DWORD PTR DS:[EAX+44],2
7FF63B15 JNZ SHORT 7FF63B60
7FF63B17 AND DWORD PTR SS:[EBP-1C],0
7FF63B1B LEA EAX,DWORD PTR SS:[EBP-1C]
7FF63B1E MOV DWORD PTR SS:[EBP-5C],EAX
7FF63B21 AND DWORD PTR SS:[EBP-4],0
7FF63B25 PUSH DWORD PTR SS:[EBP-5C]
7FF63B28 MOV EAX,DWORD PTR SS:[EBP-10]
7FF63B2B MOV ECX,DWORD PTR DS:[EAX+4]
7FF63B2E CALL 7FF609E1
7FF63B33 MOV DWORD PTR SS:[EBP-28],EAX
7FF63B36 MOV EAX,DWORD PTR SS:[EBP-28] ; ntdll.77F6D6B0
7FF63B39 MOV EAX,DWORD PTR DS:[EAX]
7FF63B3B MOV DWORD PTR SS:[EBP-2C],EAX
7FF63B3E PUSH DWORD PTR SS:[EBP-2C]
7FF63B41 CALL DWORD PTR DS:[7FF971A8] ; kernel32.DeleteFileA
7FF63B47 OR DWORD PTR SS:[EBP-4],FFFFFFFF
7FF63B4B LEA ECX,DWORD PTR SS:[EBP-1C]
7FF63B4E CALL 7FF86F15
7FF63B53 MOV EAX,DWORD PTR SS:[EBP-10]
7FF63B56 MOV EAX,DWORD PTR DS:[EAX+4]
7FF63B59 MOV DWORD PTR DS:[EAX+44],3
7FF63B60 PUSH 7FFA6BB8
7FF63B65 CALL DWORD PTR DS:[7FF97160] ; ntdll.RtlEnterCriticalSection
7FF63B6B MOV EAX,DWORD PTR DS:[7FFA6BF8]
7FF63B70 MOV DWORD PTR SS:[EBP-50],EAX
7FF63B73 LEA EAX,DWORD PTR SS:[EBP+8]
7FF63B76 PUSH EAX
7FF63B77 LEA EAX,DWORD PTR SS:[EBP-20]
7FF63B7A PUSH EAX
7FF63B7B MOV ECX,DWORD PTR SS:[EBP-50]
7FF63B7E CALL 7FF6718D
7FF63B83 MOV EAX,DWORD PTR SS:[EBP-20] ; ntdll.77F79005
7FF63B86 MOV DWORD PTR SS:[EBP-58],EAX
7FF63B89 MOV EAX,DWORD PTR DS:[7FFA6BF8]
7FF63B8E MOV DWORD PTR SS:[EBP-54],EAX
7FF63B91 PUSH DWORD PTR SS:[EBP-58] ; ntdll.77FA34DE
7FF63B94 LEA EAX,DWORD PTR SS:[EBP-24]
7FF63B97 PUSH EAX
7FF63B98 MOV ECX,DWORD PTR SS:[EBP-54] ; <&KERNEL32.GetModuleFileNameA>
7FF63B9B CALL 7FF66C85
7FF63BA0 MOV EAX,DWORD PTR SS:[EBP+8]
7FF63BA3 CMP EAX,DWORD PTR DS:[7FFA3484]
7FF63BA9 JNZ SHORT 7FF63BB9
7FF63BAB AND DWORD PTR DS:[7FFA6C1C],0
7FF63BB2 OR DWORD PTR DS:[7FFA3484],FFFFFFFF
7FF63BB9 PUSH 7FFA6BB8
7FF63BBE CALL DWORD PTR DS:[7FF9715C] ; ntdll.RtlLeaveCriticalSection
7FF63BC4 PUSH 0
7FF63BC6 CALL DWORD PTR DS:[7FF97044] ; ntdll.RtlSetLastWin32Error
7FF63BCC PUSH 1
7FF63BCE POP EAX ; 7FF52C62
7FF63BCF JMP 7FF63C57
7FF63BD4 PUSH DWORD PTR SS:[EBP+8]
7FF63BD7 CALL 7FF682DF
7FF63BDC POP ECX ; 7FF52C62
7FF63BDD MOV DWORD PTR SS:[EBP-18],EAX
7FF63BE0 CMP DWORD PTR SS:[EBP-18],0
7FF63BE4 JNZ SHORT 7FF63C54
7FF63BE6 PUSH DWORD PTR SS:[EBP+8]
7FF63BE9 CALL DWORD PTR DS:[7FF970B0] ; kernel32.CloseHandle
7FF63BEF MOV DWORD PTR SS:[EBP-18],EAX
7FF63BF2 MOV EAX,DWORD PTR DS:[7FFA3154]
7FF63BF7 MOV EAX,DWORD PTR DS:[EAX]
7FF63BF9 AND EAX,100000
7FF63BFE NEG EAX
7FF63C00 SBB EAX,EAX
7FF63C02 NEG EAX
7FF63C04 MOVZX EAX,AL
7FF63C07 TEST EAX,EAX
7FF63C09 JNZ SHORT 7FF63C28
7FF63C0B CMP DWORD PTR SS:[EBP-18],0
7FF63C0F JNZ SHORT 7FF63C54
7FF63C11 MOV EAX,DWORD PTR DS:[7FFA3154]
7FF63C16 MOV EAX,DWORD PTR DS:[EAX]
7FF63C18 AND EAX,8
7FF63C1B NEG EAX
7FF63C1D SBB EAX,EAX
7FF63C1F NEG EAX
7FF63C21 MOVZX EAX,AL
7FF63C24 TEST EAX,EAX
7FF63C26 JE SHORT 7FF63C54
7FF63C28 CMP DWORD PTR SS:[EBP-18],0
7FF63C2C JNZ SHORT 7FF63C37
7FF63C2E MOV DWORD PTR SS:[EBP-60],7FF99900 ; ASCII "*** failed"
7FF63C35 JMP SHORT 7FF63C3E
7FF63C37 MOV DWORD PTR SS:[EBP-60],7FF9A10C ; ASCII "success"
7FF63C3E PUSH DWORD PTR SS:[EBP-60]
7FF63C41 PUSH DWORD PTR SS:[EBP-18]
7FF63C44 PUSH DWORD PTR SS:[EBP+8]
7FF63C47 PUSH 7FF9A0E0 ; ASCII "CloseHandle (extern) hObject=%x -> %d %s
"
7FF63C4C CALL 7FF54550
7FF63C51 ADD ESP,10
7FF63C54 MOV EAX,DWORD PTR SS:[EBP-18]
7FF63C57 MOV ECX,DWORD PTR SS:[EBP-C]
7FF63C5A MOV DWORD PTR FS:[0],ECX
7FF63C61 LEAVE
7FF63C62 RETN 4
This is not simple, isn't it :) Ha, there is lot of API calls here, but which one is right one !?! Check this:
ntdll.RtlGetLastWin32Error <-------- Forget these ones ntdll.dll. Their purpose is for some internal usage.
ntdll.RtlEnterCriticalSection
ntdll.RtlLeaveCriticalSection
ntdll.RtlSetLastWin32Error
kernel32.DeleteFileA <-------------- It could be this one.
kernel32.CloseHandle <-------------- Or it could be this one.
So it could be DeleteFileA or CloseHandle. But again, thinstall will help us. Check these strings:
"CloseHandle (internal) : h=%x
"
"CloseHandle (extern) hObject=%x -> %d %s
"
These strings are telling you that CloseHandle is API that is stolen. As you can see, it is not easy and you will spend some time. But notice that right API is always close to RETN than others, ntdll.dll ones are for internal usage of protector and text strings will give you help. And finally, you can always run file after fixing and check in stack do they have right parameters.
In Olly you will check that you didn't make something wrong, but for repairing these imports use ImpREC. When you find IAT, click to show invalid one, than on one-by-one right click on each and select Disassemble/Hex view. There you will see same disassmebler view as in olly (only without strings) and when you see API that you think it is right one, right click on name and in popup menu click "Get Import". API's that I found from the beggining to the end are:
CreateThread
ExitThread
CloseHandle
CopyFileA
CreateFileA
ExitProcess
GetFileSize
GetModuleHandleA
ReadFile
WriteFile
ShellExecuteA
SendMessageA
DialogBoxParamA
That's it, lucky that unpackme is small. For larger number of imports we could try to make some script or more generic approach.
I was going to dump file with LordPE but I get error message "Couldn't grab process memory :(", then I swiched to Intelli's engine but after fixing imports dump would just exit and also it lost icon. So I dumped it with OllyDump, fixed imports with ImpREC and dump worked fine.
4. Finall words
That's it :) ! We have unpacked double process Thinstall and it even wasn't so hard.
If somedy figure out how to fix imports with elegant patching, it would be nice thing to write small update for this tutorial. Btw, it is interesting how protected file has only one section.
In archive you will find target, dump, tutorial, and tree for ImpREC so you can check your dump.
Greets goes to detten, knight, thorpe, TDC, boro... all biw users, arteam and crackmes.de members. Thanks to MaDMAn_H3rCul3s for his tutorial and Teddy Rogers for his great site with unpackmes.
This small tutorial is just update for my Thinstall unpacking tutorial , but this text can be usefull for lot of other packers/protectors (ASPack, UPX, NsPack, Neolite...) that doesn't destroy IAT information. It's very simple, there is way how to repair IAT without using ImpREC. It takes little more time , but you get smaller and cleaner dump.
Actually, in Thinstall it takes much less time for fixing since we doesn't have to trace and find imports. Thinstall uses WriteProcessMemory API to write values in thunks and I didn't found that when I was unpacking it for the first time.
But, this is not subject of this small tutorial. Let's see that on example. Check that mine tutorial to see how to kill Debug Blocker and how to find OEP. When you read that (or if you already know that) continue with this text. Are you ready? Yes, OK: Whole point is that Thinstall desn't destroy import information (thunks, ordinals and image descriptors) and if we prevent filling thunks with imports, we can get perfect dump. Let's see:
In our example, we kill debug blocker. Before that we sow that one import jump is pointing to his thunk:
004016F6 JMP DWORD PTR DS:[4050AC] ; USER32.CallWindowProcA
Place memory bp on access on 4050AC and run untill you stop at code that is filling that block with bytes:
7FF89FF3 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS> <------ This one is filling!!!
7FF89FF5 FF2495 08A1F87F JMP DWORD PTR DS:[EDX*4+7FF8A108]
7FF89FFC 8BC7 MOV EAX,EDI ; Thinstal.004050AC
7FF89FFE BA 03000000 MOV EDX,3
7FF8A003 83E9 04 SUB ECX,4
7FF8A006 72 0C JB SHORT 7FF8A014
7FF8A008 83E0 03 AND EAX,3
7FF8A00B 03C8 ADD ECX,EAX
7FF8A00D FF2485 20A0F87F JMP DWORD PTR DS:[EAX*4+7FF8A020]
7FF8A014 FF248D 18A1F87F JMP DWORD PTR DS:[ECX*4+7FF8A118]
7FF8A01B 90 NOP
7FF8A01C -FF248D 9CA0F87F JMP DWORD PTR DS:[ECX*4+7FF8A09C]
7FF8A023 90 NOP
7FF8A024 30A0 F87F5CA0 XOR BYTE PTR DS:[EAX+A05C7FF8],AH
7FF8A02A F8 CLC
Place bp below and run then check dump. You will see whole virgin IAT block that is waiting for filling thunks with API's:
00404FFC 14 55 00 00 00 00 00 00 DE 54 00 00 .U.......T..
0040500C EE 54 00 00 FA 54 00 00 CC 54 00 00 BE 54 00 00 .T...T...T...T..
0040501C A8 54 00 00 00 00 00 00 E8 55 00 00 F8 55 00 00 .T.......U...U..
0040502C 06 56 00 00 16 56 00 00 2A 56 00 00 66 54 00 00 .V...V..*V..fT..
0040503C 72 54 00 00 32 56 00 00 BC 53 00 00 CA 53 00 00 rT..2V...S...S..
0040504C D6 53 00 00 E4 53 00 00 F2 53 00 00 00 54 00 00 .S...S...S...T..
0040505C 14 54 00 00 22 54 00 00 30 54 00 00 3E 54 00 00 .T.."T..0T..>T..
0040506C 4E 54 00 00 5A 54 00 00 00 00 00 00 8C 54 00 00 NT..ZT.......T..
0040507C 00 00 00 00 8C 53 00 00 9E 53 00 00 7A 53 00 00 .....S...S..zS..
0040508C 6C 53 00 00 5C 53 00 00 4A 53 00 00 3E 53 00 00 lS..S..JS..>S..
0040509C 30 53 00 00 1E 53 00 00 0E 53 00 00 02 53 00 00 0S...S...S...S..
004050AC 84 52 00 00 96 52 00 00 A8 52 00 00 B4 52 00 00 .R...R...R...R..
004050BC C6 52 00 00 D4 52 00 00 E4 52 00 00 F4 52 00 00 .R...R...R...R..
004050CC 00 00 00 00 CE 55 00 00 B4 55 00 00 A2 55 00 00 .....U...U...U..
004050DC 92 55 00 00 7A 55 00 00 6A 55 00 00 46 55 00 00 .U..zU..jU..FU..
004050EC 36 55 00 00 5C 55 00 00 00 00 00 00 00 00 00 00 6U..U..........
004050FC 00 00 00 00 0C 52 00 00 00 00 00 00 00 00 00 00 .....R..........
0040510C B0 53 00 00 80 50 00 00 B0 51 00 00 00 00 00 00 .S...P...Q......
0040511C 00 00 00 00 7E 54 00 00 24 50 00 00 04 52 00 00 ....~T..$P...R..
0040512C 00 00 00 00 00 00 00 00 9C 54 00 00 78 50 00 00 .........T..xP..
0040513C 94 51 00 00 00 00 00 00 00 00 00 00 0A 55 00 00 .Q...........U..
0040514C 08 50 00 00 8C 51 00 00 00 00 00 00 00 00 00 00 .P...Q..........
0040515C 28 55 00 00 00 50 00 00 5C 52 00 00 00 00 00 00 (U...P..R......
0040516C 00 00 00 00 DE 55 00 00 D0 50 00 00 00 00 00 00 .....U...P......
0040517C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040518C 14 55 00 00 00 00 00 00 DE 54 00 00 EE 54 00 00 .U.......T...T..
0040519C FA 54 00 00 CC 54 00 00 BE 54 00 00 A8 54 00 00 .T...T...T...T..
004051AC 00 00 00 00 E8 55 00 00 F8 55 00 00 06 56 00 00 .....U...U...V..
004051BC 16 56 00 00 2A 56 00 00 66 54 00 00 72 54 00 00 .V..*V..fT..rT..
004051CC 32 56 00 00 BC 53 00 00 CA 53 00 00 D6 53 00 00 2V...S...S...S..
004051DC E4 53 00 00 F2 53 00 00 00 54 00 00 14 54 00 00 .S...S...T...T..
004051EC 22 54 00 00 30 54 00 00 3E 54 00 00 4E 54 00 00 "T..0T..>T..NT..
004051FC 5A 54 00 00 00 00 00 00 8C 54 00 00 00 00 00 00 ZT.......T......
0040520C 8C 53 00 00 9E 53 00 00 7A 53 00 00 6C 53 00 00 .S...S..zS..lS..
0040521C 5C 53 00 00 4A 53 00 00 3E 53 00 00 30 53 00 00 S..JS..>S..0S..
0040522C 1E 53 00 00 0E 53 00 00 02 53 00 00 84 52 00 00 .S...S...S...R..
0040523C 96 52 00 00 A8 52 00 00 B4 52 00 00 C6 52 00 00 .R...R...R...R..
0040524C D4 52 00 00 E4 52 00 00 F4 52 00 00 00 00 00 00 .R...R...R......
0040525C CE 55 00 00 B4 55 00 00 A2 55 00 00 92 55 00 00 .U...U...U...U..
0040526C 7A 55 00 00 6A 55 00 00 46 55 00 00 36 55 00 00 zU..jU..FU..6U..
0040527C 5C 55 00 00 00 00 00 00 13 00 43 61 6C 6C 57 69 U........CallWi
0040528C 6E 64 6F 77 50 72 6F 63 41 00 8A 00 44 69 61 6C ndowProcA...Dial
0040529C 6F 67 42 6F 78 50 61 72 61 6D 41 00 AD 00 45 6E ogBoxParamA...En
004052AC 64 44 69 61 6C 6F 67 00 D0 00 47 65 74 41 63 74 dDialog...GetAct
004052BC 69 76 65 57 69 6E 64 6F 77 00 D5 00 47 65 74 43 iveWindow...GetC
004052CC 61 70 74 75 72 65 00 00 EC 00 47 65 74 43 75 72 apture....GetCur
004052DC 73 6F 72 50 6F 73 00 00 F1 00 47 65 74 44 6C 67 sorPos....GetDlg
004052EC 43 74 72 6C 49 44 00 00 F2 00 47 65 74 44 6C 67 CtrlID....GetDlg
004052FC 49 74 65 6D 00 00 24 01 47 65 74 50 61 72 65 6E Item..$.GetParen
0040530C 74 00 48 01 47 65 74 57 69 6E 64 6F 77 52 65 63 t.H.GetWindowRec
0040531C 74 00 63 01 49 6E 76 61 6C 69 64 61 74 65 52 65 t.c.InvalidateRe
0040532C 63 74 00 00 9D 01 4D 65 73 73 61 67 65 42 6F 78 ct....MessageBox
0040533C 41 00 C2 01 50 74 49 6E 52 65 63 74 00 00 D2 01 A...PtInRect....
0040534C 52 65 6C 65 61 73 65 43 61 70 74 75 72 65 00 00 ReleaseCapture..
0040535C E2 01 53 65 6E 64 4D 65 73 73 61 67 65 41 00 00 ..SendMessageA..
0040536C EB 01 53 65 74 43 61 70 74 75 72 65 00 00 F8 01 ..SetCapture....
0040537C 53 65 74 44 6C 67 49 74 65 6D 54 65 78 74 41 00 SetDlgItemTextA.
0040538C 1D 02 53 65 74 57 69 6E 64 6F 77 4C 6F 6E 67 41 ..SetWindowLongA
0040539C 00 00 22 02 53 65 74 57 69 6E 64 6F 77 54 65 78 ..".SetWindowTex
004053AC 74 41 00 00 75 73 65 72 33 32 2E 64 6C 6C 00 00 tA..user32.dll..
004053BC >1A 00 43 6C 6F 73 65 48 61 6E 64 6C 65 00 24 00 ..CloseHandle.$.
004053CC >43 6F 70 79 46 69 6C 65 41 00 30 00 43 72 65 61 CopyFileA.0.Crea
004053DC >74 65 46 69 6C 65 41 00 80 00 45 78 69 74 50 72 teFileA...ExitPr
004053EC >6F 63 65 73 73 00 F5 00 47 65 74 46 69 6C 65 53 ocess...GetFileS
004053FC 69 7A 65 00 09 01 47 65 74 4D 6F 64 75 6C 65 48 ize...GetModuleH
0040540C 61 6E 64 6C 65 41 00 00 67 01 47 6C 6F 62 61 6C andleA..g.Global
0040541C 41 6C 6C 6F 63 00 6E 01 47 6C 6F 62 61 6C 46 72 Alloc.n.GlobalFr
0040542C 65 65 00 00 72 01 47 6C 6F 62 61 6C 4C 6F 63 6B ee..r.GlobalLock
0040543C 00 00 79 01 47 6C 6F 62 61 6C 55 6E 6C 6F 63 6B ..y.GlobalUnlock
0040544C 00 00 F7 01 52 65 61 64 46 69 6C 65 00 00 9E 02 ....ReadFile....
0040545C 57 72 69 74 65 46 69 6C 65 00 BB 02 6C 73 74 72 WriteFile...lstr
0040546C 63 70 79 41 00 00 BF 02 6C 73 74 72 6C 65 6E 41 cpyA....lstrlenA
0040547C 00 00 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C 00 00 ..kernel32.dll..
0040548C 67 00 53 68 65 6C 6C 45 78 65 63 75 74 65 41 00 g.ShellExecuteA.
0040549C 73 68 65 6C 6C 33 32 2E 64 6C 6C 00 2F 00 43 72 shell32.dll./.Cr
004054AC 65 61 74 65 46 6F 6E 74 49 6E 64 69 72 65 63 74 eateFontIndirect
004054BC 41 00 CD 00 47 65 74 4F 62 6A 65 63 74 41 00 00 A...GetObjectA..
004054CC DD 00 47 65 74 53 74 6F 63 6B 4F 62 6A 65 63 74 ..GetStockObject
004054DC 00 00 2E 01 53 65 6C 65 63 74 4F 62 6A 65 63 74 ....SelectObject
004054EC 00 00 35 01 53 65 74 42 6B 4D 6F 64 65 00 59 01 ..5.SetBkMode.Y.
004054FC 53 65 74 54 65 78 74 43 6F 6C 6F 72 00 00 67 64 SetTextColor..gd
0040550C 69 33 32 2E 64 6C 6C 00 0A 00 47 65 74 4F 70 65 i32.dll...GetOpe
0040551C 6E 46 69 6C 65 4E 61 6D 65 41 00 00 63 6F 6D 64 nFileNameA..comd
0040552C 6C 67 33 32 2E 64 6C 6C 00 00 AC 00 77 61 76 65 lg32.dll....wave
0040553C 4F 75 74 43 6C 6F 73 65 00 00 B5 00 77 61 76 65 OutClose....wave
0040554C 4F 75 74 47 65 74 50 6F 73 69 74 69 6F 6E 00 00 OutGetPosition..
0040555C B8 00 77 61 76 65 4F 75 74 4F 70 65 6E 00 B9 00 ..waveOutOpen...
0040556C 77 61 76 65 4F 75 74 50 61 75 73 65 00 00 BA 00 waveOutPause....
0040557C 77 61 76 65 4F 75 74 50 72 65 70 61 72 65 48 65 waveOutPrepareHe
0040558C 61 64 65 72 00 00 BB 00 77 61 76 65 4F 75 74 52 ader....waveOutR
0040559C 65 73 65 74 00 00 BC 00 77 61 76 65 4F 75 74 52 eset....waveOutR
004055AC 65 73 74 61 72 74 00 00 C0 00 77 61 76 65 4F 75 estart....waveOu
004055BC 74 55 6E 70 72 65 70 61 72 65 48 65 61 64 65 72 tUnprepareHeader
004055CC 00 00 C1 00 77 61 76 65 4F 75 74 57 72 69 74 65 ....waveOutWrite
004055DC 00 00 77 69 6E 6D 6D 2E 64 6C 6C 00 46 00 43 72 ..winmm.dll.F.Cr
004055EC 65 61 74 65 54 68 72 65 61 64 00 00 81 00 45 78 eateThread....Ex
004055FC 69 74 54 68 72 65 61 64 00 00 07 02 52 65 73 75 itThread....Resu
0040560C 6D 65 54 68 72 65 61 64 00 00 53 02 53 65 74 54 meThread..S.SetT
0040561C 68 72 65 61 64 50 72 69 6F 72 69 74 79 00 60 02 hreadPriority.`.
0040562C 53 6C 65 65 70 00 62 02 53 75 73 70 65 6E 64 54 Sleep.b.SuspendT
0040563C 68 72 65 61 64 hread
Now, binary copy all that block and find OEP. Check then that IAT block and you will see changes - API's are placed in thunks, also some pointers to protectors code (API's that we need to fix). In my tutorial I traced every single invalid import but I didn't thinking. There is easier way. Binary paste that block that you have copied before into same place, ei. IAT block must look again like virgin one, untached above. Then dump. Dump will crush. Reason of crushing is just that IAT information is incorrect in PE header because that is old info for Thinstall IAT. We just need to enter correct value with LordPE. Open LordPE and load dump in PE editor. Click directories button, check ImportTable, it has:
RVA: 0
Size: 0
We just need to enter correct values. Now you need to read some PE format tutorial (goppit wrote very good one) and you'll know what to do. RVA must point to image_import_descriptors and Size is size of all image_import_descriptors (plus zero terminating one). IAT is this:
004050FC 0C 52 00 00 00 00 00 00 00 00 00 00 .R..........
0040510C B0 53 00 00 80 50 00 00 B0 51 00 00 00 00 00 00 .S...P...Q......
0040511C 00 00 00 00 7E 54 00 00 24 50 00 00 04 52 00 00 ....~T..$P...R..
0040512C 00 00 00 00 00 00 00 00 9C 54 00 00 78 50 00 00 .........T..xP..
0040513C 94 51 00 00 00 00 00 00 00 00 00 00 0A 55 00 00 .Q...........U..
0040514C 08 50 00 00 8C 51 00 00 00 00 00 00 00 00 00 00 .P...Q..........
0040515C 28 55 00 00 00 50 00 00 5C 52 00 00 00 00 00 00 (U...P..R......
0040516C 00 00 00 00 DE 55 00 00 D0 50 00 00 00 00 00 00 .....U...P......
0040517C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
So enter in LordPE:
RVA: 00005100
Size: 0000008B
Save changes.
Run dump, it should work perfect :)
-
======================
ExeShield 3.7 - manually unpacking
======================
This tutorial will describe manually unpacking ExeShield 3.7 protector.
1. Introduction words
Hi and welcome to a new unpacking tutorial! This time we will have a very easy job.
ExeShield is maybe the easiest protector that I have seen and that includes freeware protectors too. It supports dongle(hardware key) protection but this tutorial won't discuss about that part, only software protection. And that software protection is very weak. Also notice that if you scan protected file with PEiD, you will get incorrect scan result "PECompact 2.x -> Jeremy Collake". Don't be surprised, a lot of protectors today have fake first bytes in order to confuse non-experienced crackers. Or maybe ExeShield just uses PECompact for compression.
You will need next tools for this tutorial.
- OllyDbg 1.10
2. Unpacking
If you run protected target you will see that is creates one hidden file in the same folder. That file is our crippled executable. If you open it in olly you'll see that first half of code section is filled with zeros. That half of code will be written in memory directly. And that is main protection trick in ExeStealth. From anti-debug tricks, protector only has FindWindowA check that is searching for OLLYDBG windows. Ok, let's unpack our target RICHEDIT.EXE.
Load it in olly and in olly debug options, under events tab, check "Break on new module (dll)" and press F9 untill you see in modules window that user32.dll is loaded in memory:
Executable modules
Base Size Entry Name File version Path
00400000 005C9000 00401000 RICHEDIT D:xxxxxxExeShieldRICHEDIT.EXE
77D40000 0008C000 77D53A05 user32 5.1.2600.1561 (x C:WINDOWSsystem32user32.dll <----- It's red in olly!
77E60000 000E6000 77E7ADB3 kernel32 5.1.2600.1560 (x C:WINDOWSsystem32kernel32.dll
77F50000 000A7000 ntdll 5.1.2600.1106 (x C:WINDOWSSystem32ntdll.dll
That means user32.dll is loaded now in memory so uncheck "Break on new module (dll)" in Olly and place bp's on FindWindowA and WriteProcessMemory. Now keep pressing F9 untill you see in stack window OLLYDBG name:
000CD7E4 00470612 /CALL to FindWindowA from RICHEDIT.0047060D
000CD7E8 004749A0 |Class = "OLLYDBG"
000CD7EC 00000000 Title = NULL
FindWindowA function here checks for OLLYDBG. To kill that, press Ctrl+F9 to return to code and just set EAX=0 and continue with F9. You may see couple times OLLYDBG in stack and every time you just need to set EAX=0 when you exit that API.
Then you will see one ugly nag window (I forgot to uncheck this option). Continue and you will soon break on WriteProcessMemory API, check stack:
0130E19C 0046F417 /CALL to WriteProcessMemory from RICHEDIT.0046F412
0130E1A0 000000B4 |hProcess = 000000B4 (window)
0130E1A4 00401000 |Address = 401000 <-------------------------------- Where to write!
0130E1A8 0047BB00 |Buffer = RICHEDIT.0047BB00 <---------------------- Where are original bytes stored!
0130E1AC 00001ABF |BytesToWrite = 1ABF (6847.) <--------------------- How many there is bytes, size!
0130E1B0 0136FF84 pBytesWritten = 0136FF84
And this is what we where waiting for. Protector has created that temporary file in same dir, "xshld4997.tmp" in my case and it created process from it. Then it will write missing bytes with our API function to that process. We can see where missing bytes are (in buffer) , it's size and where it will write them. Now, copy that "xshld4997.tmp" file in the same folder and you will get new one "Copy of xshld4997.tmp". Rename it's extension to exe so you get "Copy of xshld4997.exe" file. Open it another Olly instance and you'll see that it is full of zeroes from 401000 to 40269E. We gona just paste those "stolen bytes" from buffer in our first Olly. In first olly follow this buffer in disassembler (right click and select that option):
0130E1A8 0047BB00 |Buffer = RICHEDIT.0047BB00 <---- Follow in disassembler window!
You will land here:
0047BB00 PUSH EBP
0047BB01 MOV EBP,ESP
0047BB03 ADD ESP,-20
0047BB06 PUSH 7D0
0047BB0B PUSH DWORD PTR DS:[4041E8]
0047BB11 CALL RICHEDIT.0047D4A6
0047BB16 MOV DWORD PTR DS:[404083],EAX
0047BB1B JMP SHORT RICHEDIT.0047BB2D
0047BB1D PUSH ESP
...
...
Select all code from 0047BB00 to 0047D596 and binary copy it. That is actually whole code section of our packed target. Now just paste it in second olly from 401000 to 402A96 (you'll see that it matches) and save changes to that file. Now run that "Copy of xshld4997.exe" and you will see that it works normally :) And that is all! There are no fixing imports or finding OEP, repaired file is perfect copy of original non-packed file.
3. Final words
Is this was easy or not!?! ExeStealth has one option for inserting markers for runtime encryption/decryption code, but that option is not available in the trial version so I couldn't test that. But probably it's nothing special.
From what I noticed, ExeShield always steal part of first section and then it writes it in memory. That first section doesn't have to be code, it can be data or imports. You have such example on one unpackme (UnPackMe_ExeShield3.6.c.exe) In that example I couldn't save changes in my "copy of xshl..." file from some reson so just dumped it to disk and dump worked. Again no IAT and OEP problems.
-
========================
MoleBoxPro 2.2.5 - manually unpacking
========================
This tutorial will cover unpacking of MoleBoxPro 2.2.5 packer. This packer is particulary interesting because it supports packing an application with all its data files (dll's , etc..) into a single executable file.
1. Introduction
Hi folks and welcome to new tutorial! This time we will deal with MoleBoxPro 2.2.5.
MoleBox is more simple packer than protector because it doesn't have any kind of anti-debug meashures except one CRC check. But MoleBox support packing all kind of files that comes along with main executable into one file. That include dll's too. When we start exe that is actually packed aplication, MoleBox extracts packed dll's into temp folder. From what I noticed it doesn't extract whole dll, just small crippled part. Also that dll doesn't have original name as before packing, but some random name (on every execution name is little different) like MBX@7A0@7C14B8.###. Original dll is loaded into memory under that name. So you see our problem, if we remove MoleBox envelope, we will also remove nedded dll's. Because that we need to dump those dll too and repair them.
For this tutorial I have packed my windows XP regedit.exe together with one dll from system folder that regedit needs. When you unpack this file, you will need to dump that dll from memory and repair it.
The tools that we need are:
- OllyDbg 1.10
- LordPE
- ImpREC
2. OEP and IAT
In olly options ignore all exceptions and if our packed file stop on some just add it to custom ones. Load RegPack.exe (packed regedit.exe) in Olly:
010642EC PUSHAD
010642ED CALL RegPack.01064341
010642F2 RCR DWORD PTR DS:[EDI+1ED8A3E2],1E
010642F9 POPAD
010642FA XOR EBX,DWORD PTR DS:[EDI-2B]
010642FD HLT
010642FE PUSH EBP
010642FF LOOPDE SHORT RegPack.01064334
01064301 NOP
01064302 PUSH ES
01064303 RETN
01064305 XCHG EAX,EBP
...
...
That is our EntryPoint. Press F7 three times and you will see:
01064290 CALL RegPack.01063E70
01064295 POP EAX
01064296 CALL RegPack.01064610
0106429B POP EAX
0106429C MOV DWORD PTR SS:[ESP+1C],EAX
010642A0 POPAD
010642A1 JMP EAX <------------------------- Jump to OEP!!!
010642A3 INT3
010642A4 INT3
010642A5 INT3
010642A6 INT3
010642A7 INT3
010642A8 INT3
010642A9 INT3
010642AA INT3
010642AB INT3
010642AC INT3
010642AD INT3
010642AE INT3
010642AF INT3
010642B0 ADD BYTE PTR DS:[EAX],AL
010642B2 ADD BYTE PTR DS:[EAX],AL
...
...
And you have found jump to OEP! JMP EAX is our jump. Place bp there , run , remove bp and execute that jump with F7 (you will probably see one "EntryPoint warning" from olly, just click OK):
01008AC5 PUSH EBP
01008AC6 MOV EBP,ESP
01008AC8 SUB ESP,24
01008ACB PUSH EBX
01008ACC PUSH ESI
01008ACD PUSH EDI
01008ACE CALL DWORD PTR DS:[10011D0] ; [GetThreadLocale
01008AD4 XOR EBX,EBX
01008AD6 PUSH EBX
01008AD7 CALL DWORD PTR DS:[10011D4] <---------- Missing import!!!
01008ADD MOV DWORD PTR DS:[10164F0],EAX
01008AE2 LEA EAX,DWORD PTR SS:[EBP-8]
...
...
This is our OEP. First problem that we have is missing imports. MoleBox has replaced some imports with it's own code. I don't think that is protection meashure, probably those imports are one that original file was used to interact with files that main exe needs (dll's and rest of files that are now packed in MoleBox package) like CreateFileA, LoadLibraryA, etc... So we need to fix that imports. Check code snippet above, at 01008AD7 we have one missing import. Foolow that DS:[10011D4] address in dump window:
010011D4 AC E3 06 01 C8 25 E7 77 EE E4 E7 77 D2 B6 E7 77 .....%.w...w...w
010011E4 D5 A5 E7 77 0C E6 E7 77 08 E0 06 01 CB 60 E7 77 ...w...w.....`.w
010011F4 73 3B E7 77 1A E1 E7 77 EA E0 E7 77 22 3B E7 77 s;.w...w...w";.w
...
...
You can see that most of other addresses point to some dll's, but our points to MoleBox SFX section. We will not dump until we fix this. Place hardware breakpoint on access, on first byte at 010011D4 address in dump. Now restart packed target in olly and keep pressing Shift+F9 untill you stop here:
0106A152 JE RegPack.0106A1DF <-------------- You are here!!!
0106A158 MOV EAX,DWORD PTR SS:[EBP-8]
0106A15B MOV ECX,DWORD PTR DS:[EAX]
0106A15D AND ECX,80000000
0106A163 TEST ECX,ECX
0106A165 JNZ SHORT RegPack.0106A18A
0106A167 MOV EDX,DWORD PTR SS:[EBP-8]
0106A16A MOV EAX,DWORD PTR SS:[EBP+C]
0106A16D ADD EAX,DWORD PTR DS:[EDX]
0106A16F MOV DWORD PTR SS:[EBP-20],EAX
0106A172 MOV ECX,DWORD PTR SS:[EBP-20]
0106A175 ADD ECX,2
0106A178 PUSH ECX
0106A179 MOV EDX,DWORD PTR SS:[EBP-10]
0106A17C PUSH EDX
0106A17D CALL DWORD PTR DS:[1075994] ; kernel32.GetProcAddress
0106A183 MOV ECX,DWORD PTR SS:[EBP-18]
0106A186 MOV DWORD PTR DS:[ECX],EAX
0106A188 JMP SHORT RegPack.0106A1B6
0106A18A MOV EDX,DWORD PTR SS:[EBP-8]
0106A18D MOV EAX,DWORD PTR DS:[EDX]
0106A18F AND EAX,0FFFF
0106A194 MOV DWORD PTR SS:[EBP-28],EAX
0106A197 MOV ECX,DWORD PTR SS:[EBP-28]
0106A19A PUSH ECX
0106A19B MOV EDX,DWORD PTR SS:[EBP-10]
0106A19E PUSH EDX
0106A19F CALL DWORD PTR DS:[1075994] ; kernel32.GetProcAddress
0106A1A5 MOV DWORD PTR SS:[EBP-24],EAX
0106A1A8 CMP DWORD PTR SS:[EBP-24],0
0106A1AC JE SHORT RegPack.0106A1B6
0106A1AE MOV EAX,DWORD PTR SS:[EBP-18]
0106A1B1 MOV ECX,DWORD PTR SS:[EBP-24]
0106A1B4 MOV DWORD PTR DS:[EAX],ECX
0106A1B6 MOV EDX,DWORD PTR SS:[EBP-C]
0106A1B9 AND EDX,0FF <---------------------- Here MoleBox check imports!
0106A1BF TEST EDX,EDX <--------------------- Place hardware bp on execution here!!
0106A1C1 JE SHORT RegPack.0106A1DA <-------- "Magic jump" !!!
0106A1C3 MOV EAX,DWORD PTR SS:[EBP-1C]
0106A1C6 PUSH EAX
0106A1C7 MOV ECX,DWORD PTR DS:[1074BB8]
0106A1CD PUSH ECX
0106A1CE MOV EDX,DWORD PTR SS:[EBP-18]
0106A1D1 PUSH EDX
0106A1D2 CALL RegPack.0106A8A0
0106A1D7 ADD ESP,0C
0106A1DA JMP RegPack.0106A13A
...
...
First remove hardware breakpoint that you had placed before (click "Debug" menu, "Hardware breakpoints" and delete it). When you break here, you are in procedure that filling thunks with imports. MoleBox first find API with GetProcAddress and place it import table. Then it checks does that API needs to be substituted with MoleBox own code. That check is at 0106A1BF. If API doesn't need to be replaced , jump at 0106A1C1 will be executed and API will stay in thunk. So, to prevent API replacing you need to change JE to JMP. But now is too late because some jumps are already replaced, so place hardware breakpoint on execution on 0106A1BF line and restart our packed target. Now first go to our JMP EAX oep jump and place bp there, then run target untill you break on our API check:
0106A1BF TEST EDX,EDX
0106A1C1 JE SHORT RegPack.0106A1DA
Remove hw breakpoint and change JE to JMP:
0106A1BF TEST EDX,EDX
0106A1C1 JMP SHORT RegPack.0106A1DA
There! Imports are fixed now :) just run to Break at our OEP jump and then go to OEP:
01008AC5 PUSH EBP
01008AC6 MOV EBP,ESP
01008AC8 SUB ESP,24
01008ACB PUSH EBX
01008ACC PUSH ESI
01008ACD PUSH EDI
01008ACE CALL DWORD PTR DS:[10011D0] ; [GetThreadLocale
01008AD4 XOR EBX,EBX
01008AD6 PUSH EBX ; /pModule = ""
01008AD7 CALL DWORD PTR DS:[10011D4] ; GetModuleHandleW
01008ADD MOV DWORD PTR DS:[10164F0],EAX ; RegPack.01008AC5
01008AE2 LEA EAX,DWORD PTR SS:[EBP-8]
01008AE5 PUSH EAX ; /pInitEx = RegPack.01008AC5
...
...
As you see, API is now there. Dump file with LordPE and use ImpREC to grab imported API's. There shouldn't be neither one invalid import. Fix dumped file. Don't close ImpREC yet, check names of modules. One module should have name simmilar to "mbx@1dc@7c14b0.###". That is dll that is packed within main exe. You will find that file in temp folder, but you don't need it. What you need is to dump that file from memory, just write it's name down. You can close ImpREC but leave Olly and LordPE.
If you try to run dumped file, you will get warning message that "mbx@1dc@7c14b0.###" is not found and exe cannot start without it. If you copy-paste that file from temp folder, you will get error message saying that some function cannot be found in it and again you cannot start dump. Problem is that file from temp folder is just crippled junk. We need to dump valid file from memory.
3. Dumping and repairing dll's
There can be lot of this files, in our example we have only one, but procedure is same for all files. In LordPE click on our RegPack.exe process, go to bottom window and right click on our file that is in temp folder (c:docume~...mbx@1dc@7c14b0.###) and do full dump. Save dump as dumped.dll. For more that one temp file you would just save them as dumped1.dll, dumped2.dll, etc... so you don't overwrite them.
Ok, you dumped file but you need to fix it's IAT. For that you must know OEP of that file. That information you will find in it's PE header. Open memory map window and double click on it's PE header:
76CC0000 00001000 MBX@1DC@ PE header Imag R RWE <-- Double click!!!
76CC1000 0000C000 MBX@1DC@ .text code,exports Imag R RWE
76CCD000 00001000 MBX@1DC@ .data data Imag R RWE
76CCE000 00001000 MBX@1DC@ .rsrc resources Imag R RWE
76CCF000 00001000 MBX@1DC@ .reloc Imag R RWE
76CD0000 00001000 MBX@1DC@ _BOX_ SFX,relocati Imag R RWE
When you see PE header dump, scroll down to:
76CC0100 0B01 DW 010B ; MagicNumber = PE32
76CC0102 07 DB 07 ; MajorLinkerVersion = 7
76CC0103 00 DB 00 ; MinorLinkerVersion = 0
76CC0104 00100000 DD 00001000 ; SizeOfCode = 1000 (4096.)
76CC0108 00000000 DD 00000000 ; SizeOfInitializedData = 0
76CC010C 00F00000 DD 0000F000 ; SizeOfUninitializedData = F000 (61440.)
76CC0110 00000100 DD 00010000 ; AddressOfEntryPoint = 10000 <----------- OEP!!!
76CC0114 00100000 DD 00001000 ; BaseOfCode = 1000
76CC0118 00D00000 DD 0000D000 ; BaseOfData = D000
76CC011C 0000CC76 DD 76CC0000 ; ImageBase = 76CC0000 <--------------- Image base!!!
There you see where OEP is and what is Image Base. But we have problem now. It sems that dll is packed and we cannot find reall OEP in PE header. After spending some time I found what is going on. Remember this OEP, 76cd0000=76cc0000+10000 and restart Olly. Place bp on CreateFileA and run target untill you see in stack window that MoleBox has create our temp file:
0006FC8C 0106B93B /CALL to CreateFileA from RegPack.0106B935
0006FC90 007C1838 |FileName = "C:DOCUME~1HAGGAR~1LOCALS~1TEMPMBX@36C@7C14B0. ###"
0006FC94 40000000 |Access = GENERIC_WRITE
0006FC98 00000000 |ShareMode = 0
0006FC9C 00000000 |pSecurity = NULL
0006FCA0 00000002 |Mode = CREATE_ALWAYS
0006FCA4 00000000 |Attributes = 0
0006FCA8 00000000 hTemplateFile = NULL
Return to MoleBox code and you will see some WriteFile API's. Place bp below them on next line:
...
...
0106B998 CALL DWORD PTR DS:[1075A20] ; kernel32.WriteFile
0106B99E PUSH DWORD PTR SS:[EBP-4C]
0106B9A1 CALL DWORD PTR DS:[1075944] ; kernel32.FlushFileBuffers
0106B9A7 PUSH DWORD PTR SS:[EBP-4C]
0106B9AA CALL DWORD PTR DS:[1075910] ; kernel32.CloseHandle
0106B9B0 PUSH DWORD PTR SS:[EBP-24]
0106B9B3 CALL DWORD PTR DS:[10759C0] ; kernel32.LoadLibraryA
0106B9B9 MOV DWORD PTR SS:[EBP-A4],EAX
0106B9BF CMP DWORD PTR SS:[EBP-A4],0 <------------------------ Place bp here and run!!!
0106B9C6 JNZ SHORT RegPack.0106B9EC
You can conclude that our dll has some code inside now. Go to that OEP value that you sow in PE header, you know 77cd0000:
76CD0000 POP EAX <----------- This is OEP!!
76CD0001 PUSH CABEE8EC
76CD0006 PUSH 7C1718
76CD000B PUSH 44
76CD0010 PUSH EAX
76CD0011 PUSH 106AE82 <----- You see that it will return to main exe code!
76CD0016 RETN
76CD0017 NOP
76CD0018 NOP
76CD0019 NOP
76CD001A NOP
76CD001B NOP
76CD001C NOP
76CD001D NOP
76CD001E NOP
76CD001F NOP
...
...
You see that PUSH 106AE82 and then RETN ? It will remove to main exe code so follow that address and place hardware bp on execution, then run Olly. You will break there:
0106AE82 PUSH EBP <--------------------- Place hw bp on execution here and run, then remove bp!
0106AE83 MOV EBP,ESP
0106AE85 PUSH -1
0106AE87 PUSH RegPack.01072468
0106AE8C PUSH RegPack.010631B0
0106AE91 MOV EAX,DWORD PTR FS:[0]
0106AE97 PUSH EAX
0106AE98 MOV DWORD PTR FS:[0],ESP
0106AE9F PUSH ECX
0106AEA0 PUSH ECX
0106AEA1 SUB ESP,40
0106AEA4 PUSH EBX
0106AEA5 PUSH ESI
...
...
Remove bp from there. What is going on? It seams that MoleBox code that is in main exe will unpack this dll and then jump to it's original OEP. So we just need to find when it will jump to OEP or break at OEP. After using lot of memory bp's and tracing, I found that jump to OEP is just below this procedure:
0106B08B CALL DWORD PTR SS:[EBP-20] <------------ Here!!!
0106B08E MOV DWORD PTR SS:[EBP-1C],EAX
0106B091 MOV EAX,DWORD PTR SS:[EBP-1C]
0106B094 MOV ECX,DWORD PTR SS:[EBP-10]
0106B097 MOV DWORD PTR FS:[0],ECX
0106B09E POP EDI
0106B09F POP ESI
0106B0A0 POP EBX
0106B0A1 LEAVE
0106B0A2 RETN 18
And when you follow that CALL you will see our OEP:
76CC1190 PUSH EBP <----------------------- Dll OEP!!!
76CC1191 MOV EBP,ESP
76CC1193 PUSH EBX
76CC1194 MOV EBX,DWORD PTR SS:[EBP+8]
76CC1197 PUSH ESI
76CC1198 MOV ESI,DWORD PTR SS:[EBP+C]
76CC119B TEST ESI,ESI
76CC119D PUSH EDI
76CC119E MOV EDI,DWORD PTR SS:[EBP+10]
76CC11A1 JE 76CC4270
76CC11A7 CMP ESI,1
76CC11AA JE SHORT 76CC11B1
76CC11AC CMP ESI,2
...
...
Ok, let's stop for a second now. We have good dumped.exe, then non fixed dumped.dll, but we now have it's OEp and we can find it's imports with ImpREC. But we have one more problem, our dumped.dll has also some invalid imports due MoleBox API replacing. Ha ha, I hate this! But I don't have to repeat all over again. I need just to prevent import replacing so I will just place bp on main exe OEP jump and import "magic jump" that needs to be fixed:
010642A1 JMP EAX <---- OEP jump.
0106A1C1 POP EDI <---- "Magic jump" is still encrypted but you just place hw bp there.
Then run, change JE to JMP when you break to our jump, then run and you'll break on JMP EAX. That is it, you need just to repair IAT of dumped.dll and that is easy since you know dll OEP=1190 and ImageBase=76cc0000. Open ImpREC, attach to RegPack.exe, click on "Pick DLL" button and there select mbx@... module. In OEP window enter 1190 and click "IAT aoutosearch" and then "Get imports". You will see 5 modules with all valid imports. Fix dumped.dll and close all tools. And that's it! Or not? He he, run dumped_.exe and you will get error message that mbx@......### is missing. So just rename your dll to that name which windows are complaining. Run dumped_.exe now and it will work perfect!
4. Final words
If you don't want that your dumped dll has that mbx@... name, you can change it to whatever you like, but then you must open dumped_.exe and find that name and change it to new one. You will find that name in import section ofcourse.
-
========================
Krypton 0.3 - (half)manually unpacking
========================
Krypton 0.3 is not bad freeware protector with very good import protection and code redirection. This tutorial will describe one of couple possible ways how to unpack target packed with Krypton 0.3.
1. Intruduction
Tools that you will need:
- OllyDbg 1.10 (OllyDump & OllyScript plugins)
- ImpREC
- LordPE
- HexEditor
Couple words about protector:
I had this Krypton 0.3 for a long time on my hard drive, but it wasn't looking much interesting. But when I tried to unpack one crackme, I sow that it isn't gona be so easy job like I tought. Protector doesn't have anti-debugger checks like IsDebuggerPresent or similar, it doesn't have any CRC checks, but it has nice obfuscation that makes tracing hard. So I gave up from tracing. It has very nice import code obfuscation also. Howewer, OEP is very easy to find.
You can find Krypton 0.3 and 0.2 versions at
کد:
http://www.exetools.com
if you want to play with it.
2. How to find OEP
It is very easy. I will describe how did I find that. I noticed that Krypton 0.3 allocates two memory blocks using VirtualAlloc API. First block is reserved for import obfuscation and second is one that will hold loader. So load our target in Olly, ignore all exceptions and place in command bar "bp VirtualAlloc+1". +1 because Krypton checks first byte for bp's. Hit Shift+F9 and you'll break in kernel32.dll:
77E7ABC6 MOV EBP,ESP <--------------------- You are here! Remove bp from here!!!
77E7ABC8 PUSH DWORD PTR SS:[EBP+14]
77E7ABCB PUSH DWORD PTR SS:[EBP+10]
77E7ABCE PUSH DWORD PTR SS:[EBP+C]
77E7ABD1 PUSH DWORD PTR SS:[EBP+8]
77E7ABD4 PUSH -1
77E7ABD6 CALL kernel32.VirtualAllocEx
77E7ABDB POP EBP
77E7ABDC RETN 10 <------------------------- Place new bp here and run!
Remove first bp and place new one at the end of function, then run again. When you stop on that bp, in EAX you will see value that is base address of memory block where imports will be obfuscated (at my computer it is 370000, but it can be different for you and for other targets). Hit Shift+F9 again and you will break at the same spot.
This time EAX will hold some value which is base address of second allocated block (at me it is 3B0000). This block will hold protectors loader code. Loader will unpack target and then it will jump to OEP. So you understand me now, in this block there is one JMP EDX which is our OEP jump and we need to find it. But, we cannot find it yet because that block is still empty, loader code isn't writen there yet. We need first let Krypton to fill that block with code and then search for our jump. How we gona know that block is written? After Krypton writes code there, then it will jump to it, morewer it will jump to the beggining of block. So you need just to place memory bp on access on the first byte in the 3B0000 block (3B0000 in my example). Place bp and press Shift+F9 two times, first time it will stop because writing data there and second because it executes opcodes there. And you should be here:
003B0000 CALL 003B316E <---------------- Start of block!
003B0005 POP EBP
003B0006 MOV EAX,EBP
003B0008 SUB EBP,403FCD
003B000E JMP SHORT 003B001C
003B0010 ADD BYTE PTR DS:[EAX],AL
003B0012 ADD BYTE PTR DS:[EAX],AL
003B0014 ADD BYTE PTR DS:[EAX],DL
003B0016 INC EAX
003B0017 ADD BYTE PTR DS:[EAX],AL
003B0019 ADD BYTE PTR DS:[EAX],AL
003B001B ADD BL,CH
003B001D INC EDX
003B001E FILD QWORD PTR DS:[ECX+4E]
...
...
And now you need only to find right JMP EDX. There are two JMP EDX in loader code and both jumps can be good. I noticed that when you pack file with all options, then second jump is one that will lead to OEP, but if you pack file using basic options it is first one that leads to OEP. To find that jupms right click on CPU window and search for command. Enter JMP EDX and hit OK. You will first land on first jump:
003B3811 MOV EAX,DWORD PTR DS:[ESI+EDX+10]
003B3815 CMP DWORD PTR DS:[ESI+EDX+8],EAX
003B3819 MOV DWORD PTR DS:[ESI+EDX+8],EAX
003B381D MOV ECX,DWORD PTR DS:[ESI+EDX+C]
003B3821 ADD ECX,DWORD PTR DS:[ESI+EDX+10]
003B3825 MOV EAX,DWORD PTR FS:[0]
003B382A MOV ESP,DWORD PTR DS:[EAX]
003B382C POP DWORD PTR FS:[0]
003B3832 CALL 003B3837
003B3837 POP EBP
003B3838 SUB EBP,4077FF
003B383E CMP BYTE PTR SS:[EBP+40948A],0FF
003B3845 JE SHORT 003B3858
003B3847 MOV ECX,DWORD PTR SS:[EBP+41CD7A]
003B384D MOV DWORD PTR SS:[ESP],ECX
003B3850 MOV EDX,DWORD PTR SS:[EBP+41CD81]
003B3856 JMP EDX <------------------------------- You are here!!!
003B3858 CALL 003B385D
003B385D POP EBP
003B385E SUB EBP,407825
003B3864 JMP SHORT 003B38A4
003B3866 FILD QWORD PTR DS:[ECX+4E]
003B3869 POP EAX
003B386A FISTP WORD PTR DS:[ECX+74]
003B386D IMUL BL
003B386F ADD EDI,EBX
003B3871 JNZ SHORT 003B3864
...
...
This will not be right jump in this case because our target is packed with all options, but if you are not sure place bp here. Press Ctrl+L to find second jump:
003B3F02 JMP EDX
...
...
Place bp on it and, Shift+F9 and you will break there:
003B3F02 JMP EDX ; target.00401000
Press F7 to execute jump and you will land on OEP. Remove analysis from module because code looks in mess with it:
00401000 PUSH 0
00401002 CALL target.00401484
00401007 MOV DWORD PTR DS:[4020E9],EAX ; target.00401600
0040100C MOV DWORD PTR DS:[4020F9],0
00401016 PUSH 0
00401018 PUSH 80
0040101D PUSH 3
0040101F PUSH 0
00401021 PUSH 3
00401023 PUSH C0000000
00401028 PUSH target.004020D7 ; ASCII "CRACKME3.KEY"
0040102D CALL target.004014A8
00401032 CMP EAX,-1
00401035 JNZ SHORT target.00401043
00401037 PUSH target.0040210E ; ASCII "CrackMe v3.0 "
OEP is found but now we have problem with IAT and some stolen code.
3. Stolen code
I will first explain how stolen code works. This is first example:
00401139 CALL DWORD PTR DS:[3C8DA7] <----------- Call to decryptor.
0040113F PUSH 0
00401141 PUSH 0
00401143 PUSH 8000
00401148 PUSH 8000
0040114D PUSH 6E
0040114F PUSH 0B4
00401154 PUSH 0CF0000
00401159 PUSH target.0040210E ; ASCII "CrackMe v3.0 "
0040115E PUSH target.00402128 ; ASCII "No need to disasm the code!"
00401163 PUSH 0
Abowe you can see one call to 3xxxxx block. On that line should be some PUSH DWORD[x] or MOV EXX DWORD[x] opcode (EXX is some register, x some value) but Krypton has removed that opcode and place call to decryptor block instead. When that line comes for execution, it will enter that call where Krypton will first write encryptor call below this first one and then decrypt original opcode:
00401139 PUSH DWORD PTR DS:[4020E9]
0040113F CALL DWORD PTR DS:[3C8DA7]
After execution original opcode, it will again enter to same block which will now encrypt abowe line and restore all like at the begining.
So what we can do with this kind of protection? My first idea was to dump whole block and attach it to main dumped file, but such file worked only on my machine. Then I decide to try find how Krypton decrypts that stolen code in order to maybe write some script that can make the same job after we found OEP. Well, this is not maybe the smartest way, but I started it and I wanted to finish it. First I had to make one script that was suposed to remove some junk opcodes so that I could actually understand something. Ok, find this CALL and place bp on it:
00401139 FF15 A78D3C00 CALL DWORD PTR DS:[3C8DA7]
After you break there, enter in ti and run "Krypton 0.3 - clear junk.txt" script. It will not remove all junk but you will be able to understand code. Then I started tracing to see how Krypton decrypts code. Below I'm shoving only important parts for decrypting, so read my comments:
[1] It pop 003B3F82 to EBP and then substract it with some constant (constant is same in every packed file) and we get some reference value that Krypton needs.
003B3F82 POP EBP
003B3F83 SUB EBP,407F4A
[2] Next part is test for Krypton to know does it have to decrypt code or to encrypt it. If DWORD at [EBP+409486] is 0, than it will set it to FFFFFFFF (encryption flag) and it will proceed to decryption part.
003B3FAA NOT DWORD[EBP+409486]
003B3FD0 MOV EDX,DWORD[EBP+409486]
003B3FFD TEST EDX,EDX ; Test DWORD.
003B4022 JE 003B5190 ; Jump to encryption part (if DWORD==FFFFFFFF).
[3] This part is not important for our script because here Krypton backups original opcodes at some place for later restoring after encryption.
003B408E MOV EDX,DWORD[ECX]
003B40B0 MOV DWORD[EBP+409493],EDX
003B40DE MOV EDX,[ECX+4]
003B4104 MOV DWORD[EBP+409497],EDX
003B4132 MOV EDX,DWORD[ECX+8]
003B415A MOV DWORD[EBP+40949B],EDX
003B4186 MOV EDX,DWORD[ECX+C]
003B41AE MOV DWORD[EBP+40949F],EDX
[4] Here it will write encryptor call below first decryptor call. This isn't important for us too.
003B41FA MOV WORD [ECX+6],15FF ; Now writing encryptor call below.
003B4221 MOV EDX,DWORD [ECX+2]
003B424B MOV DWORD [ECX+8],EDX
[5] Now, this is very interesting part for us. It will take that our reference value and add it with second constant to EAX. EAX is actually pointer now. It points to some internal table where Krypton holds information about all CALL's in exe that belong to decryptor/encryptor routine. Then it will set EBX=409482 (third constant) and add to it reference value.
003B429D LEA EAX,DWORD[EBP+4094B7] ; Pointer to encrypted value in internal tab.
003B42E5 MOV EBX,409482
003B430E ADD EBX,EBP
[6] From EAX address (that internal table) it will take first DWORD to EDX. Then it will decrypt it with EBX. If you check EDX now, you'll see that it has 40105B value and that value is address of first CALL in exe that points to decryption routine. Check it in CPU window and you'll see that. So what's going on here? Krypton will subtract address of our call 401139 with this on 40105B and it will check is result=0. Krypton want to find where is our CALL in his own table of calls.
003B435A MOV EDX,DWORD [EAX]
003B4384 XOR EDX,DWORD [EBX]
003B43CE SUB EDX,ECX
003B43EF TEST EDX,EDX ; Testing is it our call. Not yet.
003B4410 JE SHORT 003B448A ; If Yes than jump to [7].
003B4439 ADD EAX,0A ; If Not, take next value and go back to check next DWORD.
003B445D JMP 003B4338
[7] When it finds it, it will take another DWORD to EDX and then decrypt it with EBX. Then it will place bytes from EDX to [ECX] and that is address of our call. As you can see, here Krypton writes original bytes in our target on line where was that call to this whole decryptor routine.
003B44AD MOV EDX,DWORD [EAX+4]
003B44D6 XOR EDX,DWORD [EBX]
003B44F7 MOV DWORD [ECX],EDX
003B451B MOV DX,WORD [EAX+8]
003B453E XOR EDX,DWORD [EBX]
003B4564 MOV WORD [ECX+4],DX
003B46A5 RETN
And that is how Krypton is doing decryption. With RETN it jumps to decrypted original code:
00401139 FF35 E9204000 PUSH DWORD PTR DS:[4020E9]
0040113F FF15 A78D3C00 CALL DWORD PTR DS:[3C8DA7]
After execution of original code line, it will again enter in routine but this time is flag dword set to FFFFFFFF and it will jump to encryptor part of routine. That part is not interesting to us. And this is all that we must know for writing script that I will make. This something like keygening more than unpacking ;) Script is "Krypton 0.3 - Fix CODE.txt" and it will decrypt every Krypton 0.3 packed file after you reach OEP, but you must edit one-two parts in script. You can read comments in script itself. And now we going to IAT problem.
4. IAT problem
IAT is a bit difficult than code problem, but still it's not too hard. Thing is that Krypton has three options for IAT. First option is that IAT won't be touched at all and we don't need to fix it. Second way is basic IAT obfuscation and obfuscation is goes something like this. You have normal import jump which is redirected to that first allocated block:
00404xxx JMP DWORD PTR DS:[403yyy] ---> DS:[403yyy]=0037wwww
So this jump instead of API, it will jump to 370000 block where you can find this kind of obfuscation:
0037wwww ADD DWORD[0037uuuu],CONSTANT
0037wwww MOV EAX,DWORD[00370000]
0037wwww SUB DWORD[0037uuuu],CONSTANT
0037wwww JMP EAX
0037uuuu SOME_4_BYTES
or second variation:
0037wwww XOR DWORD[0037uuuu],CONSTANT
0037wwww MOV EAX,DWORD[00370000]
0037wwww XOR DWORD[0037uuuu],CONSTANT
0037wwww JMP EAX
0037uuuu SOME_4_BYTES
As you can see, it will take 4 bytes then XOR or ADD them to some constant and that value is our API valuue. Then it will give that value to EAX and encrypt 4 bytes again and jump to API. This is very easy to fix using script to something like:
0037wwww JMP API_function
0037wwww NOP
0037wwww NOP
0037wwww NOP
0037uuuu NOP
...
...
ImpREC then can easily find imports now. Script that will fix this kind of obfuscation is "Krypton 0.3 - IAT I.txt", it requires from you to enter base of block where imorts are. But our target has this kind of import obfuscation plus, it has one more layer of protection that is harder to repair. This second layer is almost indentical as that code encryption one.
Check this code in our target:
004013A0 FF15 AB8D3C00 CALL DWORD PTR DS:[3C8DAB]
004013A6 FF15 AB8D3C00 CALL DWORD PTR DS:[3C8DAB]
004013AC FF15 AB8D3C00 CALL DWORD PTR DS:[3C8DAB]
...
...
004014FC FF15 AB8D3C00 CALL DWORD PTR DS:[3C8DAB]
These are our import jumps. But they all jump to same location just like in code encryption case. Procedure is the same; Krypton will take address of first CALL and then it will search for same in his own table. When it finds it, it will decrypt one DWORD value that it actually pointer address somewhere to that 370000 block where import call must jump. And from there, after minor decryption, it jumps to API. There is no need to talk much about it, check comments below:
00401484 CALL DWORD PTR DS:[3C8DAB] ; Our first import call.
003B48C6 POP EBP
003B48C7 SUB EBP,40888E ; We get reference value here.
//--------------- Not important for us ----------------------------
003B48EF MOV EDX,DWORD [ECX]
003B4914 MOV DWORD [EBP+413107],EDX
003B4942 MOV EDX,DWORD PTR DS:[ECX+4]
003B4965 MOV DWORD PTR SS:[EBP+41310B],EDX
003B498C MOV EDX,DWORD PTR DS:[ECX+8]
003B49B2 MOV DWORD PTR SS:[EBP+41310F],EDX
003B49DF MOV EDX,DWORD PTR DS:[ECX+C]
003B4A03 MOV DWORD PTR SS:[EBP+413113],EDX
//---------------------------------------------------------------
003B4A53 LEA EAX,DWORD [EBP+41312B] ; EAX=pointer to encrypted reference value in tab.
003B4A78 MOV EBX,4130FB
003B4AA1 ADD EBX,EBP
LABEL_01:
003B4AEC MOV EDX,DWORD [EAX] ; Take enc. ref. value
003B4B13 XOR EDX,DWORD [EBX] ; Decrypt it.
003B4B5E SUB EDX,ECX ; Substract it to compare it does it matches with call position.
003B4B83 TEST EDX,ED
003B4BA4 JE SHORT 003B4C19 LABEL_02 ; If does, go to decrypting.
003B4BC8 ADD EAX,0A ; If not, try next one.
003B4BF0 JMP 003B4AC4 LABEL_01
LABEL_02:
003B4C3D MOV EDX,DWORD [EAX+4] ; Take value from tab.
003B4C5F XOR EDX,DWORD [EBX] ; Decrypt it.
003B4CA7 TEST DL,DL ; Some testing, I didn't examne this one. It is something
003B4CD1 JNZ SHORT 003B4D42 ; probably if we get negative value.
003B4CF5 MOV EDI,1 ; Some calcualtions.
003B4DAC SHR EDX,10
003B4DF5 MOV ECX,EDX
003B4E41 MOV DX,WORD [EAX+8]
003B4E66 XOR EDX,DWORD PTR DS:[EBX]
003B4E88 AND EDX,0FFFF
003B4EAF SHL EDX,10
003B4ED6 OR EDX,ECX
003B4F00 MOV EAX,EDX ; Pointer to import jump is at 00403214.
003B5022 TEST EDI,EDI
003B5048 JE 003B50D8
003B5146 JMP DWORD [EAX] ; Jump to obfuscated API.
And that's it. I was very shor but you need just rip this code and that's it. That is why this tutorial is called "half manually unpacking" :) Script that emulates and repairs import jumps is "Krypton 0.3 - IAT II.txt". You will need to edit one-two lines just like in code case.
5. Final words
So to unpack target.exe you need to run script for finding OEP, then edit script for code and run it, edit script for IAT II and run it, run IAT I script and enter address of IAT block, dump file and use ImpREC to rebuild IAT. Easy, ha ;)
What more to say? Oh, for finding OEP, you have "Krypton 0.3 - OEP finder.txt" script. There is more ways to unpack Krypton, probably faster ones, but when I started this work I wanted to finish it.
-
beria 0.07 public WIP - manually unpacking tutorial
--------------------------------------------------------------------------------
Beria is another small and simple freeware PE file protector. What interesting in beria is, that it works similar to well known armadillo. Well, only in some aspects. It is two process protection, a debugger and debuggee. But, beria doesn't have any kind of anty-debug tricks or code obfuscation, and that is very nice since we can learn how protectors that debug theirselfs works. This tutorial will just present one way how beria can be unpacked and that is how did I do done it.
Let's sad that this tutorial is at some intermediate level.
haggar 25.11.2005
..... >
-
1. Tools and misc
--------------------------------------------------------------------------------
These are tools that I used for unpacking:
- Windows XP (sorry guys, but I know only one way how to detach processes and that one needs XP)
- OllyDbg 1.10
- LordPE
- ImpREC
You can find them all very fast using google.
Target and unpacked file are included in archive.
--------------->