-
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.
--------------->
-
2. How beria works
--------------------------------------------------------------------------------
When you run protected target, beria envelope code will start another process from same file. This proces will unpack target in memory. However these two processes depends of each other. First process is called debugger (not "father" like yow will read in most of tutorials) because it monitors actions of second process. Actually it really debugs second one. Second process is called debuggee (often called a "son"), because he is one that is being debugged. Next question would be, what has to be debugged there. The answer is, what ever coder of protector wants. Usually, it is not much. Main purpose of debugger-debuggee is to prevent debugging of application by third person, that means you. How?
Now, I'm not expert so you cannot take my words for absolute truth, but from what I know application in Windows can be debugged by only one debugger (ring 3, I think that desn't rule for ring 0 like SoftICE) at time. That mean we cannot attach to second process and because that, we cannot see what is happening with second process. So that is first issue. This trick is called "Debug Blocker feature" in this kind of protections.
Second one is, that debuggee expect certain actions from debugger. As I sad, what ones, it depends from coder. I will try to explain what beria is doing.
Beria doesn't unpack whole file in memory, only blocks of 1000 bytes when packed application needs it. This is for preventing dumping. For example, second process will try to jump at OEP of file, but there will be no code. So debugge will call debugger process and debugger process will decrypt 1000 bytes code block and place it at that address around OEP in debugge. Then it will return controll to debuggee to continue normall execution of OEP code. If eventually debuggee lands on some address that is again empty, it will again call debugger and this one will again place there another 1000 block of code. Now you get picture, it's just copy-paste thing. This is how armadillo is working, only that armadillo destroys blocks of code after execution which are not nedded at that time. Armadillo decrypts and encrypts code back again. Beria doesn't encrypt code back, so when beria decrypts one block, it stays like that untill the termination of program. This makes unpacking beria little easier.
Second thing what debuggee in beria expects is import execution. This part is interesting and little harder to reverse. Second process, debuggee - now unpacked application, will eventually want to use some API. But beria didn't fill IAT with all API's at the start of unpacked target. Some API's will have to be loaded using LoadLibraryA and GetProcAddres functions. But here two processes will interact. Debuggee will tell to debugger that is needs some API on some offset. Debugger will then look in his table which API should be at that offset and then it will give to debugge DLL name which owns that API and name of that API. Then debuggee will use LoadLibraryA to load DLL in memory, and GetProcAddress to find address of that API in memory. Then it will load that API at offset in IAT where it should be and program will continue with execution.
And that is what beria is doing. Now you can conclude what we have to do to unpack our target. We need to decrypt whole image of application in memory, load all imports and detach processes. Then we need to dump second process file and repair dump. Sounds easy , ha :) Well, we know theory but we need to see what we can do in reall app.
One more thing, the way how we gonna debug applications that debugs itself. We cannot debug second process directly because it is already debugged with its own debugger, but we can debug it indirectly - by controling debugger (the "father"). And that is whole point of double processes.
========>
-
3. Examnig beria code
--------------------------------------------------------------------------------
I sad that debugger and debuggee comunicates with each other. How do they comunicate? The answer is - via exceptions! Yep, you use it all the time when you crack some target, even if you didn't think about that. The most know exception is breakpoint => INT3 => CC ! Debuggee is one that couse some exceptions and beria's debugger proces on a type of exception perform some action. Beria uses three kinds of exceptions:
Exception number Name
80000001 GUARD PAGE VIOLATION
80000003 BREAKPOINT
C0000005 ACCESS VIOLATION
GUARD PAGE VIOLATION:
Like I sad, beria decrypts blocks of 1000 bytes of code when debuggee needs it. Thing is that beria has used VirtualProtectEx function to protect debuggee with PAGE_GUARD parameter. From Win32 API reference we see:
"Pages in the region become guard pages. Any attempt to read from or write to a guard page causes the operating system to raise a STATUS_GUARD_PAGE exception, and turn off the guard page status. Guard pages thus act as a one-shot access alarm."
So when debuggee jump to some offset, like on OEP, it triggers this exception, stops, and from that point debugger takes control. Debugger will then check where exception happend and it going to write there 1000 bytes, also it will remove PAGE_GUARD protection from that block of code so debuggee can work. Then it gives control back to debuggee.
BREAKPOINT:
Do I need to explain this one ? :) Actually all exceptions to the same, they stop debuggee and he waits for debugger to take some action. Again, debugger checks where exception happened and on a base of that it takes some action.
ACCESS VIOLATION:
From API reference; The thread attempted to read from or write to a virtual address for which it does not have the appropriate access. This one is not important for us.
But debugger needs somehow to catch these exceptions from debuggee. For that he uses WaitForDebugEvent and ContinueDebugEvent API's. The WaitForDebugEvent function waits for a debugging event (exception) to occur in a process being debugged. The ContinueDebugEvent function enables a debugger to continue a thread that previously reported a debugging event. These two are main ones. Check API reference and you will find lot of info about debugging. Now we have some basic understanding and we can finally load target in Olly. So load packed.exe and scroll down. This is part of code that controls debuggee:
00401964 /PUSH -1
00401966 |PUSH packed.00404040
0040196B |CALL ESI <--------------- WaitForDebugEvent API catches exceptions from debuggee.
0040196D |TEST EAX,EAX
0040196F |JE packed.00401A19
00401975 |MOV EAX,DWORD PTR DS:[404040]
0040197A |DEC EAX
0040197B |CMP EAX,6
0040197E |JA packed.00401A05
00401984 |JMP DWORD PTR DS:[EAX*4+401AB8]
0040198B |CMP DWORD PTR DS:[40440C],EBP
00401991 |JNZ SHORT packed.00401A05
00401993 |MOV ECX,DWORD PTR DS:[40401C]
00401999 |MOV EDX,DWORD PTR DS:[4043C0]
0040199F |LEA EAX,DWORD PTR SS:[ESP+24]
004019A3 |PUSH EAX ; /pBytesWritten = 0016DCF8
004019A4 |PUSH 1 ; |BytesToWrite = 1
004019A6 |PUSH packed.00403050 ; |Buffer = packed.00403050
004019AB |PUSH ECX ; |Address = 77E757C4
004019AC |PUSH EDX ; |hProcess = 00140608
004019AD |CALL DWORD PTR DS:[<&KERNEL32.WriteProc>; \WriteProcessMemory
004019B3 |JMP SHORT packed.00401A05
004019B5 |MOV EAX,DWORD PTR DS:[40404C]
004019BA |CMP EAX,80000001 <-------------------- Check exception, is it PAGE_GUARD ?
004019BF |JE SHORT packed.004019F3 <------------ If yes, then go to decrypting blocks.
004019C1 |CMP EAX,80000003 <-------------------- Is exception breakpoint ?
004019C6 |JE SHORT packed.004019D7 <------------ Then go to import procedure.
004019C8 |CMP EAX,C0000005 <-------------------- Memory access violation?
004019CD |JNZ SHORT packed.00401A05 <----------- Not debugger problem, return to debuggee.
004019CF |MOV DWORD PTR DS:[4043DC],EBP
004019D5 |JMP SHORT packed.00401A05 <----------- Every else just continues to debuggee.
004019D7 |MOV EAX,DWORD PTR DS:[404064] <------- Here it will jump if exception is breakpoint.
004019DC |MOV ECX,DWORD PTR DS:[404058]
004019E2 |PUSH EAX
004019E3 |MOV EAX,DWORD PTR DS:[404048]
004019E8 |PUSH ECX
004019E9 |CALL packed.00401AE0 <---------------- In this call imports will be processed.
004019EE |ADD ESP,8
004019F1 |JMP SHORT packed.00401A05
004019F3 |MOV EDX,DWORD PTR DS:[404064] <------- If exception is page_guard, here jumps.
004019F9 |MOV EBX,DWORD PTR DS:[404030]
004019FF |PUSH EDX
00401A00 |CALL packed.00402690 <---------------- Call that decrypts and writes 1000 blocks of code.
00401A05 |MOV EAX,DWORD PTR DS:[404048]
00401A0A |MOV ECX,DWORD PTR DS:[404044]
00401A10 |PUSH 10002
00401A15 |PUSH EAX
00401A16 |PUSH ECX
00401A17 |CALL EDI <---------------------------- ContinueDebugEvent gives control back to debuggee.
00401A19 |CMP DWORD PTR DS:[4043DC],EBP
00401A1F \JNZ packed.00401964
WaitForDebugEvent api returns some information of debugging event. One part of that information is what kind of exception coused event and where did that exception occured. On a base of that information beria decides what to do. If exception is breakpoint, it will jump to call where imports are processed (actually there is more things , but it's not all that important). If exception is guard page violation, beria will jump to code where algo for decrypting blocks is. Memory access violations are not debugger job and he will just pass control to debuggee, let him take care of that.
I spend quite some time examning code in order to see how beria works but here I will just generally descrybe main two parts, decrypting blocks and couple words about imports.
Decrypting blocks of code:
If exception is 80000001->PAGE_GUARD , we will jump to algo that decrypts blocks:
004019F3 |> 8B15 64404000 |MOV EDX,DWORD PTR DS:[404064] ; packed.0042ADB4
004019F9 |. 8B1D 30404000 |MOV EBX,DWORD PTR DS:[404030]
004019FF |. 52 |PUSH EDX
00401A00 |. E8 8B0C0000 |CALL packed.00402690
First line shows where exception occured, in this case at 0042ADB4 which is OEP of unpacked file. OEP, because it is a first PAGE_GUARD exception and logic tels you that first opcode that some exe trying to execute is OEP one. Tracing in with F7 we will enter in CALL at 401A00 and continue to this line:
004026CC |. 8B4C83 14 MOV ECX,DWORD PTR DS:[EBX+EAX*4+14] ; packed.0042D000
Address 0042D000 is base of IAT section. I have concluded that later (well I also packed file myself and I know how original was looking ;). Using bp's on WriteProcessMemory you can later see that in that section some API's are written, whole blocks of them so you know that it should be IAT there. Point is that code that missing in main exe is between 401000 and that IAT base 42D000 and it needs to be decrypted. You can trace further to see what is going on after, but I will not descrybe that here simply because tutorial would be too big , plus I don't have strength to write that much. Just hit Ctrl+F9 (run till return) and F7 to return from our decryptor call:
004019F3 |> 8B15 64404000 |MOV EDX,DWORD PTR DS:[404064];packed.0042ADB4
004019F9 |. 8B1D 30404000 |MOV EBX,DWORD PTR DS:[404030]
004019FF |. 52 |PUSH EDX
00401A00 |. E8 8B0C0000 |CALL packed.00402690
00401A05 |> A1 48404000 |MOV EAX,DWORD PTR DS:[404048];Default case of switch 0040197A
You are at the last line above, there beria will call ContinueDebugEvent which will return control to second process after 1000 block of code is decrypted. Here we could make jump that will loop and decrypt all blocks of code from 401000 to 42D000, just address that shows where exception ocured [404064] must be changed every time.
Imports:
Beria is doing trick thing with imports which cannot be fixed on the fly with OllyDbg. Just few words about this. Some imports will be written initialy in the IAT section , but some of them will be loaded when they are needed. Beria will replace lot of imports with pointers to some allocated memory block, in my case 003A0000 one:
0042D0B0 00 00 00 00 00 00 3A 00 01 00 3A 00 02 00 3A 00 ......:...:...:.
0042D0C0 03 00 3A 00 04 00 3A 00 05 00 3A 00 06 00 3A 00 ..:...:...:...:.
0042D0D0 07 00 3A 00 08 00 3A 00 09 00 3A 00 0A 00 3A 00 ..:...:...:...:.
0042D0E0 0B 00 3A 00 0C 00 3A 00 0D 00 3A 00 0E 00 3A 00 ..:...:...:...:.
0042D0F0 0F 00 3A 00 10 00 3A 00 11 00 3A 00 12 00 3A 00 ..:...:...:...:.
0042D100 13 00 3A 00 14 00 3A 00 15 00 3A 00 16 00 3A 00 ..:...:...:...:.
0042D110 17 00 3A 00 18 00 3A 00 19 00 3A 00 1A 00 3A 00 ..:...:...:...:.
You see, pointters to 3A0000, 3A0001, 3A0002,.....,3A0xxx. An there in that section:
003A0000 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0010 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0020 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0030 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0040 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0050 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0060 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0070 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0080 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0090 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A00A0 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A00B0 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A00C0 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A00D0 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A00E0 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A00F0 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0100 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC ................
003A0110 CC CC CC CC CC CC CC 00 00 00 00 00 00 00 00 00 ................
So import will now point to some CC byte which is actually INT3 opcode = breakpoint. With dhis, debuggee will couse breakpoint exception when he needs some import. Debugger process will then take controll. We can see what debugger will do now:
004019D7 |MOV EAX,DWORD PTR DS:[404064]
004019DC |MOV ECX,DWORD PTR DS:[404058] <-- Here you will see where exception occured.
004019E2 |PUSH EAX
004019E3 |MOV EAX,DWORD PTR DS:[404048]
004019E8 |PUSH ECX
004019E9 |CALL packed.00401AE0
004019EE |ADD ESP,8
004019F1 |JMP SHORT packed.00401A05
At above line you will see where exception ocured, in my case it is 003A00C and that is 003A00C-1=003A00B (minus one byte because exception is ocurred when opcode is executed and then eip register already points to next opcode). Placing bp on WriteProcessMemory we can see what is happening next:
It will give to debuggee name of kernel32.dll to some 160000 memory block which is probably buffer:
0012FF30 00402570 /CALL to WriteProcessMemory from packed.0040256A
0012FF34 00000034 |hProcess = 00000034
0012FF38 00160000 |Address = 160000
0012FF3C 00167BA0 |Buffer = 00167BA0 <------------ Follow it in buffer.
0012FF40 0000001A |BytesToWrite = 1A (26.)
0012FF44 0012FF68 \pBytesWritten = 0012FF68
00167BA0 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C kernel32.dll
Then it will write that pointer to address which is probably stack of debuggee (don't know what purpose of this pointer is):
0012FF10 00402162 /CALL to WriteProcessMemory from packed.0040215C
0012FF14 00000034 |hProcess = 00000034
0012FF18 0012FFA0 |Address = 12FFA0
0012FF1C 0012FF44 |Buffer = 0012FF44
0012FF20 00000004 |BytesToWrite = 4
0012FF24 0012FF38 \pBytesWritten = 0012FF38
0012FF44 0C 00 3A 00 ..:.
Then it writes buffer pointer to stack, telling debuggee where is that kernel32.dll name:
0012FF08 00402162 /CALL to WriteProcessMemory from packed.0040215C
0012FF0C 00000034 |hProcess = 00000034
0012FF10 0012FF9C |Address = 12FF9C
0012FF14 0012FF3C |Buffer = 0012FF3C
0012FF18 00000004 |BytesToWrite = 4
0012FF1C 0012FF30 \pBytesWritten = 0012FF30
0012FF3C 00 00 16 00 ....
Then it writes LoadLibraryA API to debuggee stack:
0012FF00 00402162 /CALL to WriteProcessMemory from packed.0040215C
0012FF04 00000034 |hProcess = 00000034
0012FF08 0012FF98 |Address = 12FF98
0012FF0C 0012FF34 |Buffer = 0012FF34
0012FF10 00000004 |BytesToWrite = 4
0012FF14 0012FF28 \pBytesWritten = 0012FF28
0012FF34 B4 D8 E7 77 ...w
Then again that pointer (why? - who cares):
0012FF10 00402162 /CALL to WriteProcessMemory from packed.0040215C
0012FF14 00000034 |hProcess = 00000034
0012FF18 0012FFA0 |Address = 12FFA0
0012FF1C 0012FF44 |Buffer = 0012FF44
0012FF20 00000004 |BytesToWrite = 4
0012FF24 0012FF38 \pBytesWritten = 0012FF38
0012FF44 0C 00 3A 00 ..:.
Then it will give some API name to debuggee:
0012FF30 004024AD /CALL to WriteProcessMemory from packed.004024A7
0012FF34 00000034 |hProcess = 00000034
0012FF38 00160000 |Address = 160000
0012FF3C 00168118 |Buffer = 00168118
0012FF40 00000042 |BytesToWrite = 42 (66.)
0012FF44 0012FF68 \pBytesWritten = 0012FF68
00168118 47 65 74 4D 6F 64 75 6C 65 48 61 6E 64 6C 65 41 GetModuleHandleA
Then again pointer to buffer:
0012FF10 00402162 /CALL to WriteProcessMemory from packed.0040215C
0012FF14 00000034 |hProcess = 00000034
0012FF18 0012FF9C |Address = 12FF9C
0012FF1C 0012FF44 |Buffer = 0012FF44
0012FF20 00000004 |BytesToWrite = 4
0012FF24 0012FF38 \pBytesWritten = 0012FF38
0012FF44 00 00 16 00 ....
Then module handle of some dll (kernel32):
0012FF10 00402162 /CALL to WriteProcessMemory from packed.0040215C
0012FF14 00000034 |hProcess = 00000034
0012FF18 0012FF98 |Address = 12FF98
0012FF1C 0012FF44 |Buffer = 0012FF44
0012FF20 00000004 |BytesToWrite = 4
0012FF24 0012FF38 \pBytesWritten = 0012FF38
0012FF44 00 00 E6 77 ...w
Then GetProcAddress API:
0012FF08 00402162 /CALL to WriteProcessMemory from packed.0040215C
0012FF0C 00000034 |hProcess = 00000034
0012FF10 0012FF94 |Address = 12FF94
0012FF14 0012FF3C |Buffer = 0012FF3C
0012FF18 00000004 |BytesToWrite = 4
0012FF1C 0012FF30 \pBytesWritten= 0012FF30
0012FF3C 85 B2 E7 77 < /FONT > < /FONT >
Ok, to make story short , what is going on?
Debuggee will make INT3 exception when it needs some import. Debugger will then give to debuggee name of dll and API based on address where exception INT3 ocured, and then debuggee will load that dll and API using LoadLibraryA and GetProcAddress API's. This is very good import protection but lucky for us, there is easier way to fix IAT. You will see that later. < /FONT > < /FONT >
And these are most interesting parts in beria. < /FONT >
-
4. Unpacking target
--------------------------------------------------------------------------------
It is time to unpack our target. Next chapters will describe how.
4.1. Finding OEP and decrypting target
--------------------------------------------------------------------------------
We can find OEP very easy and decrypting target isn't hard either. To find OEP, just place bp on next line:
004019F3 |> 8B15 64404000 |MOV EDX,DWORD PTR DS:[404064]
Press F9:
004019F3 |> 8B15 64404000 |MOV EDX,DWORD PTR DS:[404064] ; packed.0042ADB4
OEP = 0042ADB4
Keep tracing with F7 to this line:
004026CC |. 8B4C83 14 MOV ECX,DWORD PTR DS:[EBX+EAX*4+14] ; packed.0042D000
IAT = 42D0000
Code between 401000 and 42D0000 needs to be decrypted. For that, we will inject simple patch code in our target that will decrypt whole that range. Restart and place bp again at next line:
004019F3 |MOV EDX,DWORD PTR DS:[404064] <----- Follow this address in dump and write there 401000=00104000.
004019F9 |MOV EBX,DWORD PTR DS:[404030]
004019FF |PUSH EDX
00401A00 |CALL packed.00402690
00401A05 |MOV EAX,DWORD PTR DS:[404048]
00401A0A |MOV ECX,DWORD PTR DS:[404044]
00401A10 |PUSH 10002
00401A15 |PUSH EAX
00401A16 |PUSH ECX
00401A17 |CALL EDI
00401A19 |CMP DWORD PTR DS:[4043DC],EBP
00401A1F \JNZ packed.00401964
In dump there will be OEP value 0042ADB4, you change it to 401000. Then make next changes:
004019F3 |MOV EDX,DWORD PTR DS:[404064]
004019F9 |MOV EBX,DWORD PTR DS:[404030]
004019FF |PUSH EDX
00401A00 |CALL packed.00402690
00401A05 |ADD DWORD PTR DS:[404064],1000 <------- Add 1000 bytes to go on next block.
00401A0F |CMP DWORD PTR DS:[404064],0042D000 <--- Did we came to IAT base?
00401A19 |JNZ SHORT packed.004019F3 <------------ If not then go decrypt new block.
00401A1B |NOP <---------------------------------- Place bp here and run.
00401A1C |NOP
00401A1D |NOP
00401A1E |NOP
Do all like above and run. When you stop on bp, target is decrypted. Now we need to detach processes. Because page_guard protection, second process will crush. I tried to alter parameters of VirtualProtectEx functions to get non crushing process, but I failed. But I have notice that Windows DrWatson reported crushing right at OEP due 80000001 exception. And then idea fall on my mind. Olly has just-in-time debugging option! That mean we can set olly to be main windows debugger instead DrWatson so when some program crush, olly will popup and not that small ugly message from DrWatson. Go in olly options menu and click on Just-in-time debugging button. There click on "Make olly just-in-time debugger" and "Attach without confirmation", then "done". Good :) Now we need to detach processes. Just inject new inline patch below first one:
004019F3 MOV EDX,DWORD PTR DS:[404064]
004019F9 MOV EBX,DWORD PTR DS:[404030]
004019FF PUSH EDX
00401A00 CALL packed.00402690
00401A05 ADD DWORD PTR DS:[404064],1000
00401A0F CMP DWORD PTR DS:[404064],0042D000
00401A19 JNZ SHORT packed.004019F3
00401A1B PUSH 644 <------------------------ Second process PID. Your is different ofcourse.
00401A20 CALL DebugActiveProcessStop
00401A25 NOP <----------------------------- Place bp here and run.
00401A26 NOP
00401A27 NOP
00401A28 NOP
00401A29 NOP
Your PID will be different. You can get it in the attach window (the grey one). After you press F9, new Olly will popup saying "Bad or unknown format of 32-bit...". Bravo! It worked :) Don't worry for that message, just click OK. Close first olly, we don't need it anymore.
Now, you are probably confused because Olly doesn't shows target code, instead it is in ntdll.dll module. I have noticed that in Delphi programs this can happen, while for C, asm, it will land directly on targets OEP. I don't know why this happens but if does, you press F9 until you land on OEP:
0042ADB4 PUSH EBP
0042ADB5 MOV EBP,ESP
0042ADB7 ADD ESP,-0C
0042ADBA PUSH EBX
0042ADBB CALL packed.004031A0
0042ADC0 CALL packed.0040469C
0042ADC5 CALL packed.00407208
0042ADCA CALL packed.0040DB20
0042ADCF CALL packed.0040DC04
0042ADD4 CALL packed.0040FB60
0042ADD9 CALL packed.004161F8
0042ADDE CALL packed.00422AB0
0042ADE3 CALL packed.00427AE0
0042ADE8 CALL packed.004281E0
0042ADED MOV EBX,packed.0042C628
0042ADF2 MOV EAX,DWORD PTR DS:[EBX]
0042ADF4 CALL packed.00422118
0042ADF9 MOV EDX,packed.0042AE60 ; ASCII "Base Calculator"
0042ADFE MOV EAX,DWORD PTR DS:[EBX]
0042AE00 CALL packed.00421E44
...
...
Now we need to dump target and reapair that dump. For decrypting target and finding OEP you can use this little script that I made:
msgyn "IGNORE ALL EXCEPTIONS and make sure that NO BREAKPOINT IS LEFT! Then (this is the most important) set OllyDbg to be just-in-time debugger (instead DrWatson) and set to 'Attach without confirmation'. You have done all this?"
cmp $RESULT,0
je exit
var tmp1
var tmp2
//--------- Base and Size of image -----------
var Image_Base
gmi eip,MODULEBASE
mov Image_Base,$RESULT
var Image_Size
gmi eip,MODULESIZE
mov Image_Size,$RESULT
//------------- Debugee PID ----------------
var process_PID
gpa "CreateProcessA","kernel32.dll"
bp $RESULT
esto
bc eip
mov process_PID,esp
add process_PID,28
mov process_PID,[process_PID]
add process_PID,8
rtr
sti
sti
sti
mov process_PID,[process_PID]
//------ Number of imports in packed target -------
var imports
mov imports,eip
add imports,1C3
mov tmp1,imports
bp imports
esto
bc eip
sti
sti
sti
sti
mov imports,edx
//------------ OEP of packed target ---------------
var OEP
mov OEP,tmp1
add OEP,0a
bp OEP
esto
bc eip
add OEP,2
mov OEP,[OEP]
mov OEP,[OEP]
//------------ Base of import section --------------
var IAT
mov IAT,eip
add IAT,CD9
bp IAT
esto
bc eip
sti
mov IAT,ecx
//---------- Decrypt all untill IAT section ------------
var rel_oep
mov rel_oep,OEP
and rel_oep,0FFF
var break
mov break,eip
sub break,0cdd
bp break
var jump
mov jump,break
add jump,12
mov tmp1,[jump]
mov [jump],9090eceb
esto
add break,2
mov break,[break]
mov [break],401000
Decrypt: //Decrypt untill the end of file.
esto
add [break],1000
mov tmp2,[break]
cmp tmp2,IAT
jne Decrypt
bc eip
//------------ Detach processes -------------
mov eax,process_PID
asm eip,"PUSH EAX"
sti
asm eip,"CALL DebugActiveProcessStop"
msg "Done! Check log window for details and instructions."
log " "
log "- - - - - - - - - - - - - - - - - - - -"
log "BERIA 0.07 - UNPACKING SCRIPT by haggar"
log "- - - - - - - - - - - - - - - - - - - -"
log " "
log "Target is unpacked and processes (almost) detached."
log "If you have done all like I told you, another Olly"
log "will popup after you press F8 with unpacked target"
log "loaded in it, right on OEP."
log " "
log "Some information about unpacked target:"
log " "
log Image_Base
log Image_Size
log process_PID
log OEP
log IAT
log imports
log " "
log "Press F8 now to detach processes."
exit:
ret
- >
-
4.2. Dumping and repairing
--------------------------------------------------------------------------------
I have noticed that detached file in memory has only Read atribute. After dumping to hard drive and fixing imports with ImpREC, dumped file just didn't work. So I load it in olly and sow that ImpREC didn't fix import jumps. Then I have in olly select full access (read,write, execute) to image, I dumped and used ImpREC and this time dump was properly repaired. So before you use LordPE for dumping, be sure that you have give in Olly full access.
File in memory has only one section, PE header, but it's size is size of all sections. Minimize this Olly, you will need it later. Dump has lot of wrong values that needs to be changed. Open dump in LordPE's PE editor. First important values are:
EntryPoint = 00001660 --> Change it to 0002ADB4
SizeOfImage = 0004B000 --> This is good, remember it.
BaseOfData = 00003000 --> Set it to 00001000, same as BaseOfCode.
NumberOfSections = 0006 --> Importat one, set it to 1.
Now click "save" button, then "sections" and you will see only one section there. Right click on that section and "edit section header...". There change next values:
VirtualSize = 2000 --> Set it to 4A000 (that is SizeOfImage - PEheader size)
RawSize = 2000 --> Change it to 4A000.
Close window and click save button to save changes. Click on "directories" button and there set ImportTable RVA and Size to 0. Again save changes. Now dump has correct format and it can also be loaded in Olly. What we have done is, that we have placed all sections in one. That is because we doesn't know what original format of file was. Ok, I know because I packed this file, but it is even easier to reduce it to one section than try to rebuild original ones.
Now we need to fix imports.
-
4.3. Decrypting imports
--------------------------------------------------------------------------------
Open new instance of Olly and open target in it.
Protected target holds information of all dll's and api's in one tab. That tab is not encryped in any way, also it is very convenient that DLL and API names are sorted exactly like thunks in original IAT. My idea is to make inline patch that will load DLL's in memory, find address of all api's and store those api's in thunks format. Then I will just binary copy them and paste to unpacked file.
You will find tab easily. It starts at 4261ea:
004261EA 08 00 00 00 17 01 00 00 6B 65 72 6E 65 6C 33 32 ........kernel32
004261FA 2E 64 6C 6C 00 00 72 00 79 00 5C 00 4D 00 61 00 .dll..r.y.\.M.a.
0042620A 63 00 68 00 00 00 00 00 00 00 00 00 1B 00 00 00 c.h.............
0042621A 75 73 65 72 33 32 2E 64 6C 6C 00 00 5C 00 43 00 user32.dll..\.C.
0042622A 6C 00 61 00 73 00 73 00 65 00 73 00 00 00 00 00 l.a.s.s.e.s.....
...
...
0042645A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042646A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042647A 00 00 00 00 01 00 00 00 23 00 00 00 00 00 00 00 ........#.......
0042648A C0 D0 02 00 00 00 00 00 56 69 72 74 75 61 6C 46 ........VirtualF
0042649A 72 65 65 00 00 00 00 00 00 00 00 00 00 00 00 00 ree.............
004264AA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004264BA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004264CA 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
004264DA 23 00 00 00 00 00 00 00 C4 D0 02 00 00 00 00 00 #...............
004264EA 56 69 72 74 75 61 6C 41 6C 6C 6F 63 00 00 00 00 VirtualAlloc....
004264FA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
...
...
Table begins with dword value 00000008. That is number of dll's in table. Next dword is 00000171, that is number of all API's. Then we have 28h(40dec) byte arrays. First 24 bytes are reserved for dll name, and last 4 bytes hold number of API's per that dll. From that we can see which dll's program needs and how many API's must be in thunks:
Name NumberOfApi
kernel32.dll 1b
user32.dll 1
oleaut32.dll 5
advapi32.dll 6
kernel32.dll 23
gdi32.dll 38
user32.dll 89
comctl32.dll 0c
Second part of table consist of 58h(88dec) bytes arrays which holds API names. With this information I can inject some code that will load all these dll's, then get all API's, and format some kind of temporary thunk. I have injected code litle above this tab in our packed target:
0042615A PUSHAD <------------------------- This is not important, but habbit :)
0042615B PUSHFD
0042615C PUSH 004261F2 <------------------ This value points first dll name in table, kernel32.dll.
00426161 CALL kernel32.LoadLibraryA <----- I'm calling LoadLibraryA to load that dll in memory.
00426166 CMP EAX,0 <---------------------- If dll is already loaded, EAX=0, if not then EAX=handle of dll.
00426169 JNZ SHORT packed.00426176 <------ If EAX=0, I need second way to obtain handle of dll.
0042616B PUSH DWORD PTR DS:[42615D] <----- Push dll name again. This is second way using GetModuleHandle.
00426171 CALL kernel32.GetModuleHandleA <- If EAX=0, I'm using this api to retrieve handle.
00426176 MOV DWORD PTR DS:[426181],EAX <-- I'm giving EAX to GetProcAddress parameter.
0042617B PUSH 00426332 <------------------ Pushing API name. Points to first one -DeleteCriticalSection
00426180 PUSH 1000000 <------------------- This is 1000000 is just temporary. Here EAX=handle will be written.
00426185 CALL kernel32.GetProcAddress <--- This one gives me API.
0042618A MOV DWORD PTR DS:[426332],EAX <-- And I'm storing API. I'm gona overwrite api names, doesn't mathers.
0042618F DEC DWORD PTR DS:[426216] <------ Below lines are just to set pointers to new dll, api or place for thunk.
00426195 ADD DWORD PTR DS:[42617C],58
0042619C ADD DWORD PTR DS:[42618B],4
004261A3 CMP DWORD PTR DS:[426216],0
004261AA JNZ SHORT packed.0042617B
004261AC ADD DWORD PTR DS:[42615D],28
004261B3 DEC DWORD PTR DS:[4261EA]
004261B9 ADD DWORD PTR DS:[4261A5],28
004261C0 ADD DWORD PTR DS:[426191],28
004261C7 ADD DWORD PTR DS:[42618B],4
004261CE CMP DWORD PTR DS:[4261EA],0
004261D5 JNZ SHORT packed.0042615C
004261D7 NOP <---------------------------- Place bp here.
Probably this code looks confusing because it is self-modifiyng, but I wanted to make the smallest possible patch. This code will just try to load DLL using LoadLibraryA and by that retrieving to EAX handle of that dll. If dll is already loaded before, like kernel32.dll for example, the return value to EAX will be 0 so in that case I have included GetModuleHandleA api to obtaing handle from already loaded dll. After that, GetProcAddress will retrieve API from that dll on a base of handle and name of api, and place it in table.
If you are lazy to code all that, you can just binary paste next bytes:
60 9C 68 F2 61 42 00 E8 4E 77 A5 77 83 F8 00 75 0B FF 35 5D 61 42 00 E8 63 4B A5 77 A3 81 61 42
00 68 32 63 42 00 68 00 00 00 01 E8 FB 50 A5 77 A3 32 63 42 00 FF 0D 16 62 42 00 83 05 7C 61 42
00 58 83 05 8B 61 42 00 04 83 3D 16 62 42 00 00 75 CF 83 05 5D 61 42 00 28 FF 0D EA 61 42 00 83
05 A5 61 42 00 28 83 05 91 61 42 00 28 83 05 8B 61 42 00 04 83 3D EA 61 42 00 00 75 85 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
But remember that this will only work for this file and you need to change some things because your kernel address may differ.
After injecting, you should have next picture in CPU window:
And next one in dump window:
There is couple things to do before creating thunks. Check memory map window and you'll see that image has read only access (R):
Memory map
Address Size Owner Section Contains Type Access Initial Mapped as
00400000 00001000 packed PE header Imag R RWE
00401000 00002000 packed code Imag R RWE
00403000 00001000 packed data,imports Imag R RWE
00404000 00001000 packed Imag R RWE
00405000 00001000 packed Imag R RWE
00406000 0003C000 packed Imag R RWE
00442000 00009000 packed resources Imag R RWE
What we need is full access so right click and set full access:
Memory map
Address Size Owner Section Contains Type Access Initial Mapped as
00400000 00001000 packed PE header Imag RWE Copy RWE
00401000 00002000 packed code Imag RWE Copy RWE
00403000 00001000 packed data,imports Imag RWE Copy RWE
00404000 00001000 packed Imag RWE Copy RWE
00405000 00001000 packed Imag RWE Copy RWE
00406000 0003C000 packed Imag RWE Copy RWE
00442000 00009000 packed resources Imag RWE Copy RWE
Now we need to set start point to begining of our patch. Right click on first opcode, and from menu select "New origin here", just like on picture below:
Now just run, and if you have done it all right, in dump window you should see thunks (red collor):
0042615A 60 9C 68 32 63 42 00 E8 4E 77 A5 77 83 F8 00 75 `.h2cB..Nw.w...u
0042616A 0B FF 35 5D 61 42 00 E8 63 4B A5 77 A3 81 61 42 ..5]aB..cK.w..aB
0042617A 00 68 1A C3 42 00 68 00 00 34 77 E8 FB 50 A5 77 .h..B.h..4w..P.w
0042618A A3 AE 67 42 00 FF 0D 56 63 42 00 83 05 7C 61 42 .®gB...VcB...|aB
0042619A 00 58 83 05 8B 61 42 00 04 83 3D 56 63 42 00 00 .X...aB...=VcB..
004261AA 75 CF 83 05 5D 61 42 00 28 FF 0D EA 61 42 00 83 u...]aB.(...aB..
004261BA 05 A5 61 42 00 28 83 05 91 61 42 00 28 83 05 8B ..aB.(...aB.(...
004261CA 61 42 00 04 83 3D EA 61 42 00 00 75 85 90 90 90 aB...=.aB..u....
004261DA 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
004261EA 08 00 00 00 17 01 00 00 6B 65 72 6E 65 6C 33 32 ........kernel32
004261FA 2E 64 6C 6C 00 00 72 00 79 00 5C 00 4D 00 61 00 .dll..r.y.\.M.a.
0042620A 63 00 68 00 00 00 00 00 00 00 00 00 00 00 00 00 c.h.............
0042621A 75 73 65 72 33 32 2E 64 6C 6C 00 00 5C 00 43 00 user32.dll..\.C.
0042622A 6C 00 61 00 73 00 73 00 65 00 73 00 00 00 00 00 l.a.s.s.e.s.....
0042623A 1B 00 00 00 00 00 00 00 6F 6C 65 61 75 74 33 32 ........oleaut32
0042624A 2E 64 6C 6C 00 00 73 00 6F 00 63 00 69 00 61 00 .dll..s.o.c.i.a.
0042625A 74 00 69 00 00 00 00 00 1C 00 00 00 00 00 00 00 t.i.............
0042626A 61 64 76 61 70 69 33 32 2E 64 6C 6C 00 00 5C 00 advapi32.dll..\.
0042627A 43 00 4C 00 53 00 49 00 44 00 5C 00 00 00 00 00 C.L.S.I.D.\.....
0042628A 21 00 00 00 00 00 00 00 6B 65 72 6E 65 6C 33 32 !.......kernel32
0042629A 2E 64 6C 6C 00 00 37 00 2D 00 31 00 31 00 44 00 .dll..7.-.1.1.D.
004262AA 30 00 2D 00 00 00 00 00 27 00 00 00 00 00 00 00 0.-.....'.......
004262BA 67 64 69 33 32 2E 64 6C 6C 00 44 00 37 00 44 00 gdi32.dll.D.7.D.
004262CA 30 00 36 00 32 00 7D 00 5C 00 49 00 00 00 00 00 0.6.2.}.\.I.....
004262DA 4A 00 00 00 00 00 00 00 75 73 65 72 33 32 2E 64 J.......user32.d
004262EA 6C 6C 00 00 32 00 00 00 36 00 00 00 54 00 00 00 ll..2...6...T...
004262FA 80 80 80 D3 00 00 00 00 82 00 00 00 00 00 00 00 ................
0042630A 63 6F 6D 63 74 6C 33 32 2E 64 6C 6C 00 ED 69 DB comctl32.dll..i.
0042631A DB 78 D6 D6 35 EC EC 67 DC DC 7A D5 00 00 00 00 .x..5..g..z.....
0042632A 0B 01 00 00 00 00 00 00 CA 25 F5 77 90 56 F7 77 .........%.w.V.w
0042633A DE 55 F7 77 98 A6 E7 77 1E 15 E8 77 C5 AB E7 77 .U.w...w...w...w
0042634A CB 60 E7 77 D5 A5 E7 77 9C A8 E7 77 A2 A9 E7 77 .`.w...w...w.©.w
0042635A 44 AB E7 77 4F A7 E7 77 D9 AC E7 77 02 15 F5 77 D..wO..w...w...w
0042636A AB E2 E7 77 8D F0 E7 77 81 EF E7 77 90 C6 E6 77 ...w...w...w...w
0042637A 44 0C F6 77 A1 AA E7 77 62 38 E7 77 E7 E3 E7 77 D..w...wb8.w...w
0042638A B7 15 E8 77 4E E3 E7 77 63 98 E7 77 C9 B3 E7 77 ...wN..wc..w...w
0042639A 43 A6 E7 77 00 00 00 00 64 B0 D6 77 00 00 00 00 C..w....d..w....
004263AA E2 19 12 77 B8 AB 12 77 EC 14 12 77 8F 3D 12 77 ...w...w...w.=.w
004263BA 74 16 12 77 00 00 00 00 B1 63 DE 77 10 24 DD 77 t..w.....c.w.$.w
004263CA 9A 22 DD 77 6D 69 DE 77 D6 27 DD 77 D8 17 DD 77 .".wmi.w.'.w...w
004263DA B8 D0 02 00 8D F0 E7 77 C5 AB E7 77 99 98 E7 77 .......w...w...w
004263EA FA D2 E6 77 81 EF E7 77 9F EE E7 77 A1 AA E7 77 ...w...w...w...w
004263FA CC 39 E7 77 A4 E2 E7 77 B6 63 E7 77 B4 D8 E7 77 .9.w...w.c.w...w
0042640A 47 39 E7 77 92 F7 E6 77 1E 3A E7 77 4F 04 E7 77 G9.w...w.:.wO..w
0042641A 04 58 E7 77 EE FA E6 77 C9 56 E7 77 8E DF E6 77 .X.w...w.V.w...w
0042642A 02 DD E7 77 95 D0 E7 77 41 CA E6 77 85 B2 E7 77 ...w...wA..w...w
0042643A FC AC E7 77 8D 73 E7 77 32 A7 E7 77 14 69 E7 77 ...w.s.w2..w.i.w
0042644A 0C E6 E7 77 CD D5 E6 77 DF E5 E7 77 CB FF E6 77 ...w...w...w...w
0042645A A6 BD E7 77 C9 B3 E7 77 23 5D E7 77 43 A6 E7 77 ...w...w#].wC..w
0042646A 00 00 00 00 FC C0 00 7F 98 52 00 7F 83 65 00 7F .........R...e..
0042647A A9 64 00 7F 73 CD 02 7F 57 34 00 7F D7 1C 00 7F ©d..s...W4......
0042648A EF 3E 00 7F 26 D0 00 7F 42 56 03 7F 53 1E 00 7F .>..&...BV..S...
0042649A 82 1D 00 7F FB 4D 00 7F 00 1B 00 7F D7 53 00 7F .....M.......S..
004264AA 62 54 00 7F C7 C2 00 7F 94 BB 03 7F 84 C1 00 7F bT..............
004264BA B2 C5 00 7F 8E 9B 01 7F 5B 63 00 7F 0A 50 00 7F ........[c...P..
004264CA AD 28 00 7F 2B D1 00 7F 16 19 02 7F EE BF 00 7F .(..+...........
004264DA 5B BF 00 7F 9F 72 00 7F F6 20 00 7F AE 5A 00 7F [....r... ..®Z..
004264EA AA 50 00 7F 95 56 03 7F 66 91 01 7F 38 18 02 7F .P...V..f...8...
004264FA DE 4A 00 7F 31 5D 00 7F 7C CF 00 7F 1D BF 03 7F .J..1]..|.......
0042650A 6D 41 00 7F 04 7E 01 7F CC 39 00 7F 35 77 01 7F mA...~...9..5w..
0042651A 74 35 00 7F E4 20 00 7F D7 3C 00 7F B5 F2 00 7F t5... ...<......
0042652A D9 5A 00 7F C5 C4 00 7F 87 5B 00 7F 48 20 00 7F .Z.......[..H ..
0042653A 3E 36 00 7F 70 CB 00 7F E0 22 00 7F D0 58 03 7F >6..p...."...X..
0042654A DB 3B 00 7F 00 00 00 00 53 46 D4 77 6E B6 D6 77 .;......SF.wn..w
0042655A 47 3C D4 77 21 F4 D6 77 84 5B D4 77 6E B6 D4 77 G<.w!..w.[.wn..w
0042656A F0 9C D4 77 E2 3D D4 77 6F EA D5 77 60 2B D9 77 ...w.=.wo..w`+.w
0042657A 2C DA D6 77 0F 79 D4 77 E5 B8 D6 77 C9 CC D5 77 ,..w.y.w...w...w
0042658A EB AA D4 77 12 50 D5 77 E6 7E D4 77 00 79 D4 77 ...w.P.w.~.w.y.w
0042659A A2 BF D4 77 4E 7A D4 77 7B 41 D4 77 5D 68 D6 77 ...wNz.w{A.w]h.w
004265AA DB 61 D4 77 DD 56 D4 77 5F 6F D6 77 40 D4 D5 77 .a.w.V.w_o.w@..w
004265BA 22 8E D4 77 9B 79 D4 77 F8 6A D4 77 63 CD D6 77 "..w.y.w.j.wc..w
004265CA DD 6D D6 77 8B 9D D4 77 1D 5F D4 77 B8 71 D6 77 .m.w...w._.w.q.w
004265DA F4 45 D4 77 AF 6F D6 77 E7 4B D5 77 38 53 D4 77 .E.w.o.w.K.w8S.w
004265EA E3 7D D4 77 BF 8E D4 77 BF 8E D4 77 51 BF D4 77 .}.w...w...wQ..w
004265FA 11 46 D4 77 E4 B9 D4 77 48 44 D4 77 A3 64 D4 77 .F.w...wHD.w.d.w
0042660A D9 6C D6 77 39 45 D4 77 F8 DD D6 77 64 B0 D6 77 .l.w9E.w...wd..w
0042661A FB E2 D6 77 3D CF D5 77 FB 6D D4 77 1F 97 D4 77 ...w=..w.m.w...w
0042662A 7C 9C D4 77 8A 41 D4 77 73 79 D4 77 BB 5A D4 77 |..w.A.wsy.w.Z.w
0042663A 42 9C D4 77 A7 52 D4 77 AF 6A D4 77 DE 6A D4 77 B..w.R.w.j.w.j.w
0042664A 0A 59 D6 77 5D CF D5 77 00 53 D4 77 53 55 D4 77 .Y.w]..w.S.wSU.w
0042665A D8 86 D7 77 CE 58 D6 77 8A 6C D4 77 DC 59 D4 77 ...w.X.w.l.w.Y.w
0042666A FB 7F D4 77 0C 31 D5 77 94 56 D4 77 7E CC D5 77 ...w.1.w.V.w~..w
0042667A D6 3F D4 77 12 60 D4 77 64 61 D4 77 B6 41 D4 77 .?.w.`.wda.w.A.w
0042668A C9 48 D5 77 DD 68 D6 77 9C 66 D6 77 C8 54 D4 77 .H.w.h.w.f.w.T.w
0042669A B4 67 D4 77 59 70 D6 77 47 49 D5 77 3C 61 D4 77 .g.wYp.wGI.w<a.w
004266AA 50 D1 D5 77 83 D2 D5 77 29 6B D4 77 AB 74 D7 77 P..w...w)k.w.t.w
004266BA 74 96 D4 77 6A 68 D4 77 07 9C D4 77 D2 A0 D4 77 t..wjh.w...w...w
004266CA F8 85 D4 77 29 53 D4 77 A8 41 D4 77 1E CC D5 77 ...w)S.w.A.w...w
004266DA A9 CB D6 77 A7 45 D4 77 61 94 D4 77 3E 46 D4 77 ©..w.E.wa..w>F.w
004266EA AA 79 D4 77 79 CF D5 77 0E 6C D4 77 77 68 D4 77 .y.wy..w.l.wwh.w
004266FA 6E 6A D4 77 AE 69 D6 77 1E 48 D6 77 F1 52 D4 77 nj.w®i.w.H.w.R.w
0042670A D7 79 D4 77 2C 50 D5 77 CF CC D6 77 BF 57 D6 77 .y.w,P.w...w.W.w
0042671A 66 D2 D5 77 AA 5B D6 77 69 43 D4 77 80 A1 D4 77 f..w.[.wiC.w...w
0042672A 5A A2 D4 77 1B A2 D4 77 1B A2 D4 77 55 61 D4 77 Z..w...w...wUa.w
0042673A 50 5C D4 77 D2 E8 D5 77 B3 E7 D5 77 21 8B D4 77 P\.w...w...w!..w
0042674A 69 A2 D4 77 48 6A D6 77 B0 F7 D6 77 CC 6C D6 77 i..wHj.w...w.l.w
0042675A 76 8D D4 77 E7 D1 D5 77 69 5F D4 77 2E 7E D4 77 v..w...wi_.w.~.w
0042676A E2 52 D4 77 46 C2 D4 77 CC 92 D4 77 00 00 00 00 .R.wF..w...w....
0042677A 94 90 38 77 9E 91 38 77 47 90 38 77 5E 91 38 77 ..8w..8wG.8w^.8w
0042678A 80 91 38 77 38 91 38 77 C0 90 38 77 F7 90 38 77 ..8w8.8w..8w..8w
0042679A 54 50 34 77 25 74 35 77 3D 51 34 77 E3 AD 34 77 TP4w%t5w=Q4w..4w
004267AA 47 65 74 4C 61 73 74 45 72 72 6F 72 00 00 00 00 GetLastError....
004267BA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004267CA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004267DA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004267EA 00 00 00 00 01 00 00 00 23 00 00 00 00 00 00 00 ........#.......
004267FA E8 D0 02 00 00 00 00 00 47 65 74 43 6F 6D 6D 61 ........GetComma
0042680A 6E 64 4C 69 6E 65 41 00 00 00 00 00 00 00 00 00 ndLineA.........
0042681A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042682A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042683A 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
Imports are now decrypted but we need to rebuild them on unpacked file.
-
4.4. Rebuilding imports
--------------------------------------------------------------------------------
We have just decrypted imports and created thunks, but we need to do that on unpacked target. Simpy binary copy all thunks and bynary paste them in unpacked file in second Olly (load another olly and use my script for decrypting and deataching, then paste thunks). IAT section is at 0042D000 as we sow before, but first thunk start at 42D0B0 as you can see from below code. You see that at 42D0B0 is first pointer to INT3 00003A00<->003A0000:
0042D000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D0A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0042D0B0 00 00 00 00 00 00 3A 00 01 00 3A 00 02 00 3A 00 ......:...:...:.
0042D0C0 03 00 3A 00 04 00 3A 00 05 00 3A 00 06 00 3A 00 ..:...:...:...:.
0042D0D0 07 00 3A 00 08 00 3A 00 09 00 3A 00 0A 00 3A 00 ..:...:...:...:.
0042D0E0 0B 00 3A 00 0C 00 3A 00 0D 00 3A 00 0E 00 3A 00 ..:...:...:...:.
0042D0F0 0F 00 3A 00 10 00 3A 00 11 00 3A 00 12 00 3A 00 ..:...:...:...:.
0042D100 13 00 3A 00 14 00 3A 00 15 00 3A 00 16 00 3A 00 ..:...:...:...:.
0042D110 17 00 3A 00 18 00 3A 00 19 00 3A 00 1A 00 3A 00 ..:...:...:...:.
0042D120 00 00 00 00 1B 00 3A 00 00 00 00 00 1C 00 3A 00 ......:.......:.
0042D130 1D 00 3A 00 1E 00 3A 00 1F 00 3A 00 20 00 3A 00 ..:...:...:. .:.
0042D140 00 00 00 00 21 00 3A 00 22 00 3A 00 23 00 3A 00 ....!.:.".:.#.:.
0042D150 24 00 3A 00 25 00 3A 00 26 00 3A 00 00 00 00 00 $.:.%.:.&.:.....
0042D160 27 00 3A 00 28 00 3A 00 29 00 3A 00 2A 00 3A 00 '.:.(.:.).:.*.:.
So paste all thunks from there:
0042D0B0 00 00 00 00 CA 25 F5 77 90 56 F7 77 DE 55 F7 77 .....%.w.V.w.U.w
0042D0C0 98 A6 E7 77 1E 15 E8 77 C5 AB E7 77 CB 60 E7 77 ...w...w...w.`.w
0042D0D0 D5 A5 E7 77 9C A8 E7 77 A2 A9 E7 77 44 AB E7 77 ...w...w.©.wD..w
0042D0E0 4F A7 E7 77 D9 AC E7 77 02 15 F5 77 AB E2 E7 77 O..w...w...w...w
0042D0F0 8D F0 E7 77 81 EF E7 77 90 C6 E6 77 44 0C F6 77 ...w...w...wD..w
0042D100 A1 AA E7 77 62 38 E7 77 E7 E3 E7 77 B7 15 E8 77 ...wb8.w...w...w
0042D110 4E E3 E7 77 63 98 E7 77 C9 B3 E7 77 43 A6 E7 77 N..wc..w...wC..w
0042D120 00 00 00 00 64 B0 D6 77 00 00 00 00 E2 19 12 77 ....d..w.......w
0042D130 B8 AB 12 77 EC 14 12 77 8F 3D 12 77 74 16 12 77 ...w...w.=.wt..w
0042D140 00 00 00 00 B1 63 DE 77 10 24 DD 77 9A 22 DD 77 .....c.w.$.w.".w
0042D150 6D 69 DE 77 D6 27 DD 77 D8 17 DD 77 B8 D0 02 00 mi.w.'.w...w....
0042D160 8D F0 E7 77 C5 AB E7 77 99 98 E7 77 FA D2 E6 77 ...w...w...w...w
0042D170 81 EF E7 77 9F EE E7 77 A1 AA E7 77 CC 39 E7 77 ...w...w...w.9.w
0042D180 A4 E2 E7 77 B6 63 E7 77 B4 D8 E7 77 47 39 E7 77 ...w.c.w...wG9.w
0042D190 92 F7 E6 77 1E 3A E7 77 4F 04 E7 77 04 58 E7 77 ...w.:.wO..w.X.w
0042D1A0 EE FA E6 77 C9 56 E7 77 8E DF E6 77 02 DD E7 77 ...w.V.w...w...w
0042D1B0 95 D0 E7 77 41 CA E6 77 85 B2 E7 77 FC AC E7 77 ...wA..w...w...w
0042D1C0 8D 73 E7 77 32 A7 E7 77 14 69 E7 77 0C E6 E7 77 .s.w2..w.i.w...w
0042D1D0 CD D5 E6 77 DF E5 E7 77 CB FF E6 77 A6 BD E7 77 ...w...w...w...w
0042D1E0 C9 B3 E7 77 23 5D E7 77 43 A6 E7 77 00 00 00 00 ...w#].wC..w....
0042D1F0 FC C0 00 7F 98 52 00 7F 83 65 00 7F A9 64 00 7F .....R...e..©d..
0042D200 73 CD 02 7F 57 34 00 7F D7 1C 00 7F EF 3E 00 7F s...W4.......>..
0042D210 26 D0 00 7F 42 56 03 7F 53 1E 00 7F 82 1D 00 7F &...BV..S.......
0042D220 FB 4D 00 7F 00 1B 00 7F D7 53 00 7F 62 54 00 7F .M.......S..bT..
0042D230 C7 C2 00 7F 94 BB 03 7F 84 C1 00 7F B2 C5 00 7F ................
0042D240 8E 9B 01 7F 5B 63 00 7F 0A 50 00 7F AD 28 00 7F ....[c...P...(..
0042D250 2B D1 00 7F 16 19 02 7F EE BF 00 7F 5B BF 00 7F +...........[...
0042D260 9F 72 00 7F F6 20 00 7F AE 5A 00 7F AA 50 00 7F .r... ..®Z...P..
0042D270 95 56 03 7F 66 91 01 7F 38 18 02 7F DE 4A 00 7F .V..f...8....J..
0042D280 31 5D 00 7F 7C CF 00 7F 1D BF 03 7F 6D 41 00 7F 1]..|.......mA..
0042D290 04 7E 01 7F CC 39 00 7F 35 77 01 7F 74 35 00 7F .~...9..5w..t5..
0042D2A0 E4 20 00 7F D7 3C 00 7F B5 F2 00 7F D9 5A 00 7F . ...<.......Z..
0042D2B0 C5 C4 00 7F 87 5B 00 7F 48 20 00 7F 3E 36 00 7F .....[..H ..>6..
0042D2C0 70 CB 00 7F E0 22 00 7F D0 58 03 7F DB 3B 00 7F p...."...X...;..
0042D2D0 00 00 00 00 53 46 D4 77 6E B6 D6 77 47 3C D4 77 ....SF.wn..wG<.w
0042D2E0 21 F4 D6 77 84 5B D4 77 6E B6 D4 77 F0 9C D4 77 !..w.[.wn..w...w
0042D2F0 E2 3D D4 77 6F EA D5 77 60 2B D9 77 2C DA D6 77 .=.wo..w`+.w,..w
0042D300 0F 79 D4 77 E5 B8 D6 77 C9 CC D5 77 EB AA D4 77 .y.w...w...w...w
0042D310 12 50 D5 77 E6 7E D4 77 00 79 D4 77 A2 BF D4 77 .P.w.~.w.y.w...w
0042D320 4E 7A D4 77 7B 41 D4 77 5D 68 D6 77 DB 61 D4 77 Nz.w{A.w]h.w.a.w
0042D330 DD 56 D4 77 5F 6F D6 77 40 D4 D5 77 22 8E D4 77 .V.w_o.w@..w"..w
0042D340 9B 79 D4 77 F8 6A D4 77 63 CD D6 77 DD 6D D6 77 .y.w.j.wc..w.m.w
0042D350 8B 9D D4 77 1D 5F D4 77 B8 71 D6 77 F4 45 D4 77 ...w._.w.q.w.E.w
0042D360 AF 6F D6 77 E7 4B D5 77 38 53 D4 77 E3 7D D4 77 .o.w.K.w8S.w.}.w
0042D370 BF 8E D4 77 BF 8E D4 77 51 BF D4 77 11 46 D4 77 ...w...wQ..w.F.w
0042D380 E4 B9 D4 77 48 44 D4 77 A3 64 D4 77 D9 6C D6 77 ...wHD.w.d.w.l.w
0042D390 39 45 D4 77 F8 DD D6 77 64 B0 D6 77 FB E2 D6 77 9E.w...wd..w...w
0042D3A0 3D CF D5 77 FB 6D D4 77 1F 97 D4 77 7C 9C D4 77 =..w.m.w...w|..w
0042D3B0 8A 41 D4 77 73 79 D4 77 BB 5A D4 77 42 9C D4 77 .A.wsy.w.Z.wB..w
0042D3C0 A7 52 D4 77 AF 6A D4 77 DE 6A D4 77 0A 59 D6 77 .R.w.j.w.j.w.Y.w
0042D3D0 5D CF D5 77 00 53 D4 77 53 55 D4 77 D8 86 D7 77 ]..w.S.wSU.w...w
0042D3E0 CE 58 D6 77 8A 6C D4 77 DC 59 D4 77 FB 7F D4 77 .X.w.l.w.Y.w...w
0042D3F0 0C 31 D5 77 94 56 D4 77 7E CC D5 77 D6 3F D4 77 .1.w.V.w~..w.?.w
0042D400 12 60 D4 77 64 61 D4 77 B6 41 D4 77 C9 48 D5 77 .`.wda.w.A.w.H.w
0042D410 DD 68 D6 77 9C 66 D6 77 C8 54 D4 77 B4 67 D4 77 .h.w.f.w.T.w.g.w
0042D420 59 70 D6 77 47 49 D5 77 3C 61 D4 77 50 D1 D5 77 Yp.wGI.w<a.wP..w
0042D430 83 D2 D5 77 29 6B D4 77 AB 74 D7 77 74 96 D4 77 ...w)k.w.t.wt..w
0042D440 6A 68 D4 77 07 9C D4 77 D2 A0 D4 77 F8 85 D4 77 jh.w...w...w...w
0042D450 29 53 D4 77 A8 41 D4 77 1E CC D5 77 A9 CB D6 77 )S.w.A.w...w©..w
0042D460 A7 45 D4 77 61 94 D4 77 3E 46 D4 77 AA 79 D4 77 .E.wa..w>F.w.y.w
0042D470 79 CF D5 77 0E 6C D4 77 77 68 D4 77 6E 6A D4 77 y..w.l.wwh.wnj.w
0042D480 AE 69 D6 77 1E 48 D6 77 F1 52 D4 77 D7 79 D4 77 ®i.w.H.w.R.w.y.w
0042D490 2C 50 D5 77 CF CC D6 77 BF 57 D6 77 66 D2 D5 77 ,P.w...w.W.wf..w
0042D4A0 AA 5B D6 77 69 43 D4 77 80 A1 D4 77 5A A2 D4 77 .[.wiC.w...wZ..w
0042D4B0 1B A2 D4 77 1B A2 D4 77 55 61 D4 77 50 5C D4 77 ...w...wUa.wP\.w
0042D4C0 D2 E8 D5 77 B3 E7 D5 77 21 8B D4 77 69 A2 D4 77 ...w...w!..wi..w
0042D4D0 48 6A D6 77 B0 F7 D6 77 CC 6C D6 77 76 8D D4 77 Hj.w...w.l.wv..w
0042D4E0 E7 D1 D5 77 69 5F D4 77 2E 7E D4 77 E2 52 D4 77 ...wi_.w.~.w.R.w
0042D4F0 46 C2 D4 77 CC 92 D4 77 00 00 00 00 94 90 38 77 F..w...w......8w
0042D500 9E 91 38 77 47 90 38 77 5E 91 38 77 80 91 38 77 ..8wG.8w^.8w..8w
0042D510 38 91 38 77 C0 90 38 77 F7 90 38 77 54 50 34 77 8.8w..8w..8wTP4w
0042D520 25 74 35 77 3D 51 34 77 E3 AD 34 77 00 00 00 00 %t5w=Q4w..4w....
Now open ImpREC , attach to this process, enter OEP=0002ADB4 and click "IAT Auto search". For IAT size enter 1000 and click "Get imports". There will be two invalid thunks. First one is good but that dll is not loaded in memory so ImpREC doesn't know to what dll those import belongs. Second thunk has one invalid import and that is truly invalid because there should bezero DWORD instead that thunk. It's just some junk that stayed from my inline rebuilder.
Now save that import tree to hard disk and open it in Notepad, we gona fix that invalid module. In tree you can see invalid thunk (red one below):
; Syntax for each function in a thunk (the separator is a TAB)
; ------------------------------------------------------------
; Flag RVA ModuleName Ordinal Name
;
; Details for <Valid> parameter:
; ------------------------------
; Flag: 0 = valid: no -> - Name contains the address of the redirected API (you can set
; it to zero if you edit it).
; - Ordinal is not considered but you should let '0000' as value.
; - ModuleName is not considered but you should let '?' as value.
;
; 1 = valid: yes -> All next parameters on the line will be considered.
; Function imported by ordinal must have no name (the 4th TAB must
; be there though).
;
; 2 = Equivalent to 0 but it is for the loader.
;
; 3 = Equivalent to 1 but it is for the loader.
;
; 4 = Equivalent to 0 with (R) tag.
;
; 5 = Equivalent to 1 with (R) tag.
;
; And finally, edit this file as your own risk! :-)
Target: x:\xxxxxxxxxxxxx\beria\packed.exe
OEP: 0002ADB4 IATRVA: 0002D0B0 IATSize: 00001000
FThunk: 0002D0B4 NbFunc: 0000001B
1 0002D0B4 kernel32.dll 007C DeleteCriticalSection
1 0002D0B8 kernel32.dll 023B LeaveCriticalSection
1 0002D0BC kernel32.dll 0091 EnterCriticalSection
1 0002D0C0 kernel32.dll 0210 InitializeCriticalSection
1 0002D0C4 kernel32.dll 0368 VirtualFree
1 0002D0C8 kernel32.dll 0365 VirtualAlloc
1 0002D0CC kernel32.dll 0246 LocalFree
1 0002D0D0 kernel32.dll 0242 LocalAlloc
1 0002D0D4 kernel32.dll 0379 WideCharToMultiByte
1 0002D0D8 kernel32.dll 0349 TlsSetValue
1 0002D0DC kernel32.dll 0348 TlsGetValue
1 0002D0E0 kernel32.dll 025F MultiByteToWideChar
1 0002D0E4 kernel32.dll 0170 GetModuleHandleA
1 0002D0E8 kernel32.dll 0163 GetLastError
1 0002D0EC kernel32.dll 0104 GetCommandLineA
1 0002D0F0 kernel32.dll 0386 WriteFile
1 0002D0F4 kernel32.dll 0301 SetFilePointer
1 0002D0F8 kernel32.dll 02F8 SetEndOfFile
1 0002D0FC kernel32.dll 02BF RtlUnwind
1 0002D100 kernel32.dll 029E ReadFile
1 0002D104 kernel32.dll 0291 RaiseException
1 0002D108 kernel32.dll 01A9 GetStdHandle
1 0002D10C kernel32.dll 0156 GetFileSize
1 0002D110 kernel32.dll 0159 GetFileType
1 0002D114 kernel32.dll 00B1 ExitProcess
1 0002D118 kernel32.dll 004F CreateFileA
1 0002D11C kernel32.dll 0031 CloseHandle
FThunk: 0002D124 NbFunc: 00000001
1 0002D124 user32.dll 01DD MessageBoxA
FThunk: 0002D12C NbFunc: 00000005
0 0002D12C ? 0000 771219E2
0 0002D130 ? 0000 7712ABB8
0 0002D134 ? 0000 771214EC
0 0002D138 ? 0000 77123D8F
0 0002D13C ? 0000 77121674
FThunk: 0002D144 NbFunc: 00000006
1 0002D144 advapi32.dll 01F9 RegSetValueExA
1 0002D148 advapi32.dll 01EC RegQueryValueExA
1 0002D14C advapi32.dll 01E2 RegOpenKeyExA
1 0002D150 advapi32.dll 01DB RegFlushKey
1 0002D154 advapi32.dll 01CD RegCreateKeyExA
1 0002D158 advapi32.dll 01C9 RegCloseKey
FThunk: 0002D160 NbFunc: 00000023
1 0002D160 kernel32.dll 0386 WriteFile
1 0002D164 kernel32.dll 0365 VirtualAlloc
1 0002D168 kernel32.dll 0342 TerminateThread
1 0002D16C kernel32.dll 0338 SizeofResource
1 0002D170 kernel32.dll 0301 SetFilePointer
1 0002D174 kernel32.dll 02FB SetErrorMode
1 0002D178 kernel32.dll 029E ReadFile
1 0002D17C kernel32.dll 025E MulDiv
1 0002D180 kernel32.dll 024F LockResource
1 0002D184 kernel32.dll 0241 LoadResource
1 0002D188 kernel32.dll 023C LoadLibraryA
1 0002D18C kernel32.dll 01F7 GlobalUnlock
1 0002D190 kernel32.dll 01F3 GlobalReAlloc
1 0002D194 kernel32.dll 01EF GlobalHandle
1 0002D198 kernel32.dll 01F0 GlobalLock
1 0002D19C kernel32.dll 01EC GlobalFree
1 0002D1A0 kernel32.dll 01E7 GlobalDeleteAtom
1 0002D1A4 kernel32.dll 01E5 GlobalAlloc
1 0002D1A8 kernel32.dll 01E3 GlobalAddAtomA
1 0002D1AC kernel32.dll 01D6 GetVersionExA
1 0002D1B0 kernel32.dll 01D5 GetVersion
1 0002D1B4 kernel32.dll 01AE GetSystemDefaultLCID
1 0002D1B8 kernel32.dll 0192 GetProcAddress
1 0002D1BC kernel32.dll 016E GetModuleFileNameA
1 0002D1C0 kernel32.dll 0166 GetLocaleInfoA
1 0002D1C4 kernel32.dll 0139 GetCurrentThreadId
1 0002D1C8 kernel32.dll 0137 GetCurrentProcessId
1 0002D1CC kernel32.dll 0136 GetCurrentProcess
1 0002D1D0 kernel32.dll 00ED FreeResource
1 0002D1D4 kernel32.dll 00EB FreeLibrary
1 0002D1D8 kernel32.dll 00DA FindResourceA
1 0002D1DC kernel32.dll 006B CreateThread
1 0002D1E0 kernel32.dll 004F CreateFileA
1 0002D1E4 kernel32.dll 0037 CompareStringA
1 0002D1E8 kernel32.dll 0031 CloseHandle
FThunk: 0002D1F0 NbFunc: 00000038
1 0002D1F0 gdi32.dll 0253 UnrealizeObject
1 0002D1F4 gdi32.dll 024B StretchDIBits
1 0002D1F8 gdi32.dll 024A StretchBlt
1 0002D1FC gdi32.dll 0244 SetWindowOrgEx
1 0002D200 gdi32.dll 0242 SetWinMetaFileBits
1 0002D204 gdi32.dll 0240 SetViewportOrgEx
1 0002D208 gdi32.dll 023D SetTextColor
1 0002D20C gdi32.dll 0239 SetStretchBltMode
1 0002D210 gdi32.dll 0236 SetROP2
1 0002D214 gdi32.dll 0223 SetEnhMetaFileBits
1 0002D218 gdi32.dll 0217 SetBkMode
1 0002D21C gdi32.dll 0216 SetBkColor
1 0002D220 gdi32.dll 0210 SelectPalette
1 0002D224 gdi32.dll 020F SelectObject
1 0002D228 gdi32.dll 0208 SaveDC
1 0002D22C gdi32.dll 0201 RestoreDC
1 0002D230 gdi32.dll 01F7 Rectangle
1 0002D234 gdi32.dll 01F6 RectVisible
1 0002D238 gdi32.dll 01F4 RealizePalette
1 0002D23C gdi32.dll 01EF Polyline
1 0002D240 gdi32.dll 01E1 PlayEnhMetaFile
1 0002D244 gdi32.dll 01DE PatBlt
1 0002D248 gdi32.dll 01D2 MoveToEx
1 0002D24C gdi32.dll 01C8 IntersectClipRect
1 0002D250 gdi32.dll 01C4 GetWindowOrgEx
1 0002D254 gdi32.dll 01C2 GetWinMetaFileBits
1 0002D258 gdi32.dll 01BD GetTextMetricsA
1 0002D25C gdi32.dll 01B7 GetTextExtentPointA
1 0002D260 gdi32.dll 01AA GetSystemPaletteEntries
1 0002D264 gdi32.dll 01A6 GetStockObject
1 0002D268 gdi32.dll 019B GetPaletteEntries
1 0002D26C gdi32.dll 0196 GetObjectA
1 0002D270 gdi32.dll 0176 GetEnhMetaFilePaletteEntries
1 0002D274 gdi32.dll 0175 GetEnhMetaFileHeader
1 0002D278 gdi32.dll 0172 GetEnhMetaFileBits
1 0002D27C gdi32.dll 016C GetDeviceCaps
1 0002D280 gdi32.dll 016B GetDIBits
1 0002D284 gdi32.dll 0166 GetCurrentPositionEx
1 0002D288 gdi32.dll 014B GetBitmapBits
1 0002D28C gdi32.dll 00D8 ExcludeClipRect
1 0002D290 gdi32.dll 00CE EnumFontsA
1 0002D294 gdi32.dll 0090 DeleteObject
1 0002D298 gdi32.dll 008E DeleteEnhMetaFile
1 0002D29C gdi32.dll 008D DeleteDC
1 0002D2A0 gdi32.dll 0051 CreateSolidBrush
1 0002D2A4 gdi32.dll 004C CreateRectRgn
1 0002D2A8 gdi32.dll 0049 CreatePenIndirect
1 0002D2AC gdi32.dll 0046 CreatePalette
1 0002D2B0 gdi32.dll 003B CreateFontIndirectA
1 0002D2B4 gdi32.dll 0034 CreateDIBitmap
1 0002D2B8 gdi32.dll 002E CreateCompatibleDC
1 0002D2BC gdi32.dll 002D CreateCompatibleBitmap
1 0002D2C0 gdi32.dll 002A CreateBrushIndirect
1 0002D2C4 gdi32.dll 0028 CreateBitmap
1 0002D2C8 gdi32.dll 0024 CopyEnhMetaFileA
1 0002D2CC gdi32.dll 0013 BitBlt
FThunk: 0002D2D4 NbFunc: 00000089
1 0002D2D4 user32.dll 02D6 WindowFromPoint
1 0002D2D8 user32.dll 02D3 WinHelpA
1 0002D2DC user32.dll 02D1 WaitMessage
1 0002D2E0 user32.dll 02D0 WaitForInputIdle
1 0002D2E4 user32.dll 02BC UpdateWindow
1 0002D2E8 user32.dll 02B4 UnregisterClassA
1 0002D2EC user32.dll 02AF UnhookWindowsHookEx
1 0002D2F0 user32.dll 02AB TranslateMessage
1 0002D2F4 user32.dll 02AA TranslateMDISysAccel
1 0002D2F8 user32.dll 02A5 TrackPopupMenu
1 0002D2FC user32.dll 029A SystemParametersInfoA
1 0002D300 user32.dll 0293 ShowWindow
1 0002D304 user32.dll 0290 ShowOwnedPopups
1 0002D308 user32.dll 028F ShowCursor
1 0002D30C user32.dll 0285 SetWindowRgn
1 0002D310 user32.dll 028B SetWindowsHookExA
1 0002D314 user32.dll 0287 SetWindowTextA
1 0002D318 user32.dll 0284 SetWindowPos
1 0002D31C user32.dll 0283 SetWindowPlacement
1 0002D320 user32.dll 0281 SetWindowLongA
1 0002D324 user32.dll 027B SetTimer
1 0002D328 user32.dll 0270 SetScrollPos
1 0002D32C user32.dll 026F SetScrollInfo
1 0002D330 user32.dll 026D SetRect
1 0002D334 user32.dll 026B SetPropA
1 0002D338 user32.dll 025E SetMenu
1 0002D33C user32.dll 0258 SetForegroundWindow
1 0002D340 user32.dll 0257 SetFocus
1 0002D344 user32.dll 024E SetCursor
1 0002D348 user32.dll 024B SetClipboardData
1 0002D34C user32.dll 0245 SetCapture
1 0002D350 user32.dll 0244 SetActiveWindow
1 0002D354 user32.dll 023C SendMessageA
1 0002D358 user32.dll 0235 ScrollWindow
1 0002D35C user32.dll 0232 ScreenToClient
1 0002D360 user32.dll 022D RemovePropA
1 0002D364 user32.dll 022C RemoveMenu
1 0002D368 user32.dll 022B ReleaseDC
1 0002D36C user32.dll 022A ReleaseCapture
1 0002D370 user32.dll 021B RegisterClipboardFormatA
1 0002D374 user32.dll 021B RegisterClipboardFormatA
1 0002D378 user32.dll 0217 RegisterClassA
1 0002D37C user32.dll 020C PtInRect
1 0002D380 user32.dll 0202 PostQuitMessage
1 0002D384 user32.dll 0200 PostMessageA
1 0002D388 user32.dll 01FE PeekMessageA
1 0002D38C user32.dll 01F4 OpenClipboard
1 0002D390 user32.dll 01F3 OffsetRect
1 0002D394 user32.dll 01EF OemToCharA
1 0002D398 user32.dll 01DD MessageBoxA
1 0002D39C user32.dll 01DC MessageBeep
1 0002D3A0 user32.dll 01D4 MapVirtualKeyA
1 0002D3A4 user32.dll 01C9 LoadStringA
1 0002D3A8 user32.dll 01BC LoadIconA
1 0002D3AC user32.dll 01B8 LoadCursorA
1 0002D3B0 user32.dll 01B3 KillTimer
1 0002D3B4 user32.dll 01B1 IsZoomed
1 0002D3B8 user32.dll 01B0 IsWindowVisible
1 0002D3BC user32.dll 01AD IsWindowEnabled
1 0002D3C0 user32.dll 01AC IsWindow
1 0002D3C4 user32.dll 01A9 IsRectEmpty
1 0002D3C8 user32.dll 01A7 IsIconic
1 0002D3CC user32.dll 01A1 IsDialogMessage
1 0002D3D0 user32.dll 01A0 IsClipboardFormatAvailable
1 0002D3D4 user32.dll 0194 InvalidateRect
1 0002D3D8 user32.dll 0193 IntersectRect
1 0002D3DC user32.dll 018F InsertMenuItemA
1 0002D3E0 user32.dll 018E InsertMenuA
1 0002D3E4 user32.dll 018B InflateRect
1 0002D3E8 user32.dll 017C GetWindowThreadProcessId
1 0002D3EC user32.dll 0178 GetWindowTextA
1 0002D3F0 user32.dll 0176 GetWindowRgn
1 0002D3F4 user32.dll 0175 GetWindowRect
1 0002D3F8 user32.dll 0174 GetWindowPlacement
1 0002D3FC user32.dll 016F GetWindowLongA
1 0002D400 user32.dll 015E GetSystemMetrics
1 0002D404 user32.dll 015D GetSystemMenu
1 0002D408 user32.dll 015B GetSysColor
1 0002D40C user32.dll 015A GetSubMenu
1 0002D410 user32.dll 0157 GetScrollPos
1 0002D414 user32.dll 014B GetPropA
1 0002D418 user32.dll 0146 GetParent
1 0002D41C user32.dll 016B GetWindow
1 0002D420 user32.dll 0139 GetMenuStringA
1 0002D424 user32.dll 0138 GetMenuState
1 0002D428 user32.dll 0133 GetMenuItemCount
1 0002D42C user32.dll 012D GetMenu
1 0002D430 user32.dll 0129 GetLastActivePopup
1 0002D434 user32.dll 0122 GetKeyState
1 0002D438 user32.dll 0120 GetKeyNameTextA
1 0002D43C user32.dll 011B GetIconInfo
1 0002D440 user32.dll 0118 GetForegroundWindow
1 0002D444 user32.dll 0117 GetFocus
1 0002D448 user32.dll 010F GetDesktopWindow
1 0002D44C user32.dll 010E GetDCEx
1 0002D450 user32.dll 010D GetDC
1 0002D454 user32.dll 010C GetCursorPos
1 0002D458 user32.dll 0109 GetCursor
1 0002D45C user32.dll 0102 GetClipboardData
1 0002D460 user32.dll 0100 GetClientRect
1 0002D464 user32.dll 00F7 GetClassInfoA
1 0002D468 user32.dll 00F4 GetCapture
1 0002D46C user32.dll 00EC GetActiveWindow
1 0002D470 user32.dll 00EA FrameRect
1 0002D474 user32.dll 00E3 FillRect
1 0002D478 user32.dll 00E0 EqualRect
1 0002D47C user32.dll 00DF EnumWindows
1 0002D480 user32.dll 00DC EnumThreadWindows
1 0002D484 user32.dll 00CD EnumClipboardFormats
1 0002D488 user32.dll 00C9 EndPaint
1 0002D48C user32.dll 00C5 EnableWindow
1 0002D490 user32.dll 00C3 EnableMenuItem
1 0002D494 user32.dll 00C2 EmptyClipboard
1 0002D498 user32.dll 00BD DrawTextA
1 0002D49C user32.dll 00B9 DrawMenuBar
1 0002D4A0 user32.dll 00B7 DrawIcon
1 0002D4A4 user32.dll 00A2 DispatchMessageA
1 0002D4A8 user32.dll 009A DestroyWindow
1 0002D4AC user32.dll 0098 DestroyMenu
1 0002D4B0 user32.dll 0096 DestroyCursor
1 0002D4B4 user32.dll 0096 DestroyCursor
1 0002D4B8 user32.dll 0092 DeleteMenu
1 0002D4BC user32.dll 008F DefWindowProcA
1 0002D4C0 user32.dll 008C DefMDIChildProcA
1 0002D4C4 user32.dll 008A DefFrameProcA
1 0002D4C8 user32.dll 0061 CreateWindowExA
1 0002D4CC user32.dll 005F CreatePopupMenu
1 0002D4D0 user32.dll 005E CreateMenu
1 0002D4D4 user32.dll 0058 CreateIcon
1 0002D4D8 user32.dll 0043 CloseClipboard
1 0002D4DC user32.dll 0041 ClientToScreen
1 0002D4E0 user32.dll 003A CheckMenuItem
1 0002D4E4 user32.dll 001C CallWindowProcA
1 0002D4E8 user32.dll 001B CallNextHookEx
1 0002D4EC user32.dll 000E BeginPaint
1 0002D4F0 user32.dll 0027 CharLowerA
1 0002D4F4 user32.dll 0003 AdjustWindowRectEx
FThunk: 0002D4FC NbFunc: 0000000C
1 0002D4FC comctl32.dll 0038 ImageList_GetDragImage
1 0002D500 comctl32.dll 0031 ImageList_DragShowNolock
1 0002D504 comctl32.dll 004C ImageList_SetDragCursorImage
1 0002D508 comctl32.dll 0030 ImageList_DragMove
1 0002D50C comctl32.dll 002F ImageList_DragLeave
1 0002D510 comctl32.dll 002E ImageList_DragEnter
1 0002D514 comctl32.dll 0036 ImageList_EndDrag
1 0002D518 comctl32.dll 002A ImageList_BeginDrag
1 0002D51C comctl32.dll 004B ImageList_SetBkColor
1 0002D520 comctl32.dll 0046 ImageList_ReplaceIcon
1 0002D524 comctl32.dll 002D ImageList_Destroy
1 0002D528 comctl32.dll 002C ImageList_Create
There is one invalid thunk with 5 unknown imports. Instead API names, there are API addresses there. But which dll is that and what API's should be there? Answer is simple, just look in IAT table of packed.exe:
0042623A 1B 00 00 00 01 00 00 00 6F 6C 65 61 75 74 33 32 ........oleaut32
0042624A 2E 64 6C 6C 00 00 73 00 6F 00 63 00 69 00 61 00 .dll..s.o.c.i.a.
0042625A 74 00 69 00 00 00 00 00 1C 00 00 00 05 00 00 00 t.i.............
0042626A 61 64 76 61 70 69 33 32 2E 64 6C 6C 00 00 5C 00 advapi32.dll..\.
...
...
00426C8A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426C9A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426CAA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426CBA 00 00 00 00 01 00 00 00 23 00 00 00 00 00 00 00 ........#.......
00426CCA 24 D1 02 00 00 00 00 00 56 61 72 69 61 6E 74 43 $.......VariantC
00426CDA 68 61 6E 67 65 54 79 70 65 45 78 00 00 00 00 00 hangeTypeEx.....
00426CEA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426CFA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426D0A 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
00426D1A 23 00 00 00 00 00 00 00 2C D1 02 00 00 00 00 00 #.......,.......
00426D2A 56 61 72 69 61 6E 74 43 6F 70 79 49 6E 64 00 00 VariantCopyInd..
00426D3A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426D4A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426D5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426D6A 00 00 00 00 01 00 00 00 23 00 00 00 00 00 00 00 ........#.......
00426D7A 30 D1 02 00 00 00 00 00 56 61 72 69 61 6E 74 43 0.......VariantC
00426D8A 6C 65 61 72 00 00 00 00 00 00 00 00 00 00 00 00 lear............
00426D9A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426DAA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426DBA 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
00426DCA 23 00 00 00 00 00 00 00 34 D1 02 00 00 00 00 00 #.......4.......
00426DDA 53 79 73 53 74 72 69 6E 67 4C 65 6E 00 00 00 00 SysStringLen....
00426DEA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426DFA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426E0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426E1A 00 00 00 00 01 00 00 00 23 00 00 00 00 00 00 00 ........#.......
00426E2A 38 D1 02 00 00 00 00 00 53 79 73 41 6C 6C 6F 63 8.......SysAlloc
00426E3A 53 74 72 69 6E 67 4C 65 6E 00 00 00 00 00 00 00 StringLen.......
00426E4A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426E5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00426E6A 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
00426E7A 23 00 00 00 00 00 00 00 3C D1 02 00 00 00 00 00 #.......<.......
As you can see, dll is oleaut32.dll and API's are
VariantChangeTypeEx
VariantCopyInd
VariantClear
SysStringLen
SysAllocStringLen
We now can edit missing part in import tree:
FThunk: 0002D12C NbFunc: 00000005
1 0002D12C oleaut32.dll 0000 VariantChangeTypeEx
1 0002D130 oleaut32.dll 0000 VariantCopyInd
1 0002D134 oleaut32.dll 0000 VariantClear
1 0002D138 oleaut32.dll 0000 SysStringLen
1 0002D13C oleaut32.dll 0000 SysAllocStringLen
Save changes to import tree, load it in ImpREC and fix dump. Run dump and it should work ;-) !
That's it , beria is unpacked.
-->
-
=-=-=-=-=-=-=-=
در آخر اسکریپت مربوط به آموزش فوق :
msgyn "IGNORE ALL EXCEPTIONS and make sure that NO BREAKPOINT IS LEFT! Then (this is the most important) set OllyDbg to be just-in-time debugger (instead DrWatson) and set to 'Attach without confirmation'. You have done all this?"
cmp $RESULT,0
je exit
var tmp1
var tmp2
//--------- Base and Size of image -----------
var Image_Base
gmi eip,MODULEBASE
mov Image_Base,$RESULT
var Image_Size
gmi eip,MODULESIZE
mov Image_Size,$RESULT
//------------- Debugee PID ----------------
var process_PID
gpa "CreateProcessA","kernel32.dll"
bp $RESULT
esto
bc eip
mov process_PID,esp
add process_PID,28
mov process_PID,[process_PID]
add process_PID,8
rtr
sti
sti
sti
mov process_PID,[process_PID]
//------ Number of imports in packed target -------
var imports
mov imports,eip
add imports,1C3
mov tmp1,imports
bp imports
esto
bc eip
sti
sti
sti
sti
mov imports,edx
//------------ OEP of packed target ---------------
var OEP
mov OEP,tmp1
add OEP,0a
bp OEP
esto
bc eip
add OEP,2
mov OEP,[OEP]
mov OEP,[OEP]
//------------ Base of import section --------------
var IAT
mov IAT,eip
add IAT,CD9
bp IAT
esto
bc eip
sti
mov IAT,ecx
//---------- Decrypt all untill IAT section ------------
var rel_oep
mov rel_oep,OEP
and rel_oep,0FFF
var break
mov break,eip
sub break,0cdd
bp break
var jump
mov jump,break
add jump,12
mov tmp1,[jump]
mov [jump],9090eceb
esto
add break,2
mov break,[break]
mov [break],401000
Decrypt: //Decrypt untill the end of file.
esto
add [break],1000
mov tmp2,[break]
cmp tmp2,IAT
jne Decrypt
bc eip
//------------ Detach processes -------------
mov eax,process_PID
asm eip,"PUSH EAX"
sti
asm eip,"CALL DebugActiveProcessStop"
msg "Done! Check log window for details and instructions."
log " "
log "- - - - - - - - - - - - - - - - - - - -"
log "BERIA 0.07 - UNPACKING SCRIPT by haggar"
log "- - - - - - - - - - - - - - - - - - - -"
log " "
log "Target is unpacked and processes (almost) detached."
log "If you have done all like I told you, another Olly"
log "will popup after you press F8 with unpacked target"
log "loaded in it, right on OEP."
log " "
log "Some information about unpacked target:"
log " "
log Image_Base
log Image_Size
log process_PID
log OEP
log IAT
log imports
log " "
log "Press F8 now to detach processes."
exit:
ret
-
==============================
Alex Protector v1.0 beta2 - manually unpacking
==============================
Alex Protector is simple freeware protector for PE files. It has IAT redirection very simular to Armadillo's Import Eliminator feature and a very interesting way of stealing bytes from OEP.
You will need usual tools for this:
- Windows XP
- OllyDbg 1.10
- LordPE
- ImpREC
- Hex editor
1. Quick analising
It is always intersting to trace trough code and see what exactly protector does, but that kind of approach is very hard and time consuming. I was going to write that kind of tutorial, but I put aside this project to examne some others protectors and lost will for continuing. So I decide that I will wrote tutorial that will "just" explain how to quick unpack some target.
Protector doesn't have some fancy anti-debug tricks and we can just run protected file trough debugger without problem. Debug tricks in this protector are based on exceptions and RDTSC opcodes. I already told you that the most interesting part is OEP stealing and IAT redirection.
Protector will redirect imports to some allocated block of memory and obfuscate it there. Also it gona steal couple first bytes from OEP and mix them with junk in another separate allocaded memory block.
Let see what we have there. Open target in Olly. You will get "Bad or unknown format..." message. This is minor anoyance because we cannot see sections properly in memory map window. Now, place breakpoint on the last opcode in the VirtualAlloc API , click "go to" -> "expression" and enter VirtualAlloc, then place bp on first RETN opcode (in my case it is RETN 10):
77E7ABC5 PUSH EBP
77E7ABC6 MOV EBP,ESP
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 <--------------------- Here is mine!!!
Now run olly with Shift+F9 and you will break on that bp. EAX register will hold now some value, that value is base address of some allocated memory block. Write down that value, keep pressing and do that every time when you stop untill crackme opens. Then in memory map check every block that has been allocated. There are couple interesting (select disassemble view on each dump window):
Block with stolen OEP's code:
This block holds stolen OEP code mixed with junk. You will easy recognized it because it has only on jump in whole block and that jump is at the end and leads in code section to place after stolen bytes:
003B0000 MOV EDI,DFB4AF72
003B0006 LEA EDI,DWORD PTR DS:[1A58BA5F]
003B000C DEC EDI
003B000E SHRD EDI,ESI,0F2
003B0012 BSR EDI,ESI
...
...
lot of similar code here
...
...
003B0C5C TEST EDI,ESI
003B0C5E TEST EDI,874CDC1C
003B0C64 AND EDI,9378C643
003B0C6A JMP packed.004079DE <---------------- Jump to code section!!!
003B0C6F ADD BYTE PTR DS:[EAX],AL
003B0C71 ADD BYTE PTR DS:[EAX],AL
003B0C73 ADD BYTE PTR DS:[EAX],AL
003B0C75 ADD BYTE PTR DS:[EAX],AL
003B0C77 ADD BYTE PTR DS:[EAX],AL
We will not try to find original bytes here, we going just to append this code to our main dump. Most of files will work properly even if you don't add this code. Stolen code are usually 2-3 bytes, never more.
Block with obfuscated imports:
This block looks like this: It has lot of jumps and you will notice RDTSC opcodes which checks are program is debugged. You will also notice some API values. This block you can easy find just by following some import in unpacked crackme:
003A0000 ADD AL,0
003A0002 CMP AL,BYTE PTR DS:[EAX]
003A0004 JMP SHORT 003A0007
003A0006 LEAVE
003A0007 PUSHAD
003A0008 RDTSC
003A000A JMP SHORT 003A000D
003A000C LEAVE
003A000D MOV EBX,EAX
003A000F JMP SHORT 003A0012
003A0011 LEAVE
003A0012 MOV ECX,EDX
003A0014 RDTSC
003A0016 SUB EAX,EBX
003A0018 SBB EDX,ECX
003A001A JMP SHORT 003A001D
003A001C ???
003A001D RDTSC
003A001F ADD EAX,EBX
003A0021 ADC EDX,ECX
003A0023 RDTSC
003A0025 JMP SHORT 003A0028
003A0027 ???
003A0028 SUB EAX,EBX
003A002A JMP SHORT 003A002D
003A002C ???
003A002D SBB EDX,ECX
003A002F TEST EDX,EDX
003A0031 JNZ SHORT 003A0040
003A0033 POPAD
003A0034 JMP SHORT 003A0037
003A0036 ???
003A0037 PUSH ntdll.RtlDeleteCriticalSection <--- Here is API value pushed!
003A003C JMP SHORT 003A003F
003A003E JMP E93BEC06
There is one more block that holds API names, but it is not important for us. Ofcourse, above addresses will probably be different on your computer.
To fix all that we need some section where we will place our import thunks because ImpREC cannot read those which are not in main image. Problem is that we don't know which one section to use because we cannot see them in Olly. But good thing is that protector doesn't check PE header and file size, so we can add couple sections with LordPE. Close olly and open our target in hex editor (make backup before) and add to the end of file 4000h bytes. Open then file in LordPE's pe editor and add one section that you will name NewOEP. Set it's VirtualSize=2000 ans RawSize=1500. Then create one more section, call it NewIAT and enter sizes same as NewOEP. Run target and it will work normall :) Good! Now we gona unpack that modified target.
2. OEP and stolen code
Ok, do you remember that block that has stolen code? Common sense tell us that block will be executed last, before jumping to code section. So we need to break there somehow. That is really easy, you need to use memory and hardware breakpoints and you will catch it easy. You can do it in this way: Place bp on the end of VirtualAlloc and keep pressing Shift+F9 untill you see in EAX value that is address of your stolen OEP block. At my computer that is b30000, for you it can be different so you just follow way how did I do it and not exactly values. But keep pressing untill that value changes to another and then stop. Now go to that 3b0000 (or whatever) in the CPU window:
003B0000 0000 ADD BYTE PTR DS:[EAX],AL <---- Place mem and hw bp here!
003B0002 0000 ADD BYTE PTR DS:[EAX],AL
003B0004 0000 ADD BYTE PTR DS:[EAX],AL
003B0006 0000 ADD BYTE PTR DS:[EAX],AL
003B0008 0000 ADD BYTE PTR DS:[EAX],AL
003B000A 0000 ADD BYTE PTR DS:[EAX],AL
003B000C 0000 ADD BYTE PTR DS:[EAX],AL
003B000E 0000 ADD BYTE PTR DS:[EAX],AL
003B0010 0000 ADD BYTE PTR DS:[EAX],AL
003B0012 0000 ADD BYTE PTR DS:[EAX],AL
003B0014 0000 ADD BYTE PTR DS:[EAX],AL
003B0016 0000 ADD BYTE PTR DS:[EAX],AL
...
...
As you can see there is nothing there. Normally because nothing is written there yet. Place on first line breakpoint memory on write and and the same spot, hardware bp on execution. That is so we will stop when protector writes here and when this code is being executed. Remove bp from VirtualAlloc and presShift+F9. First you will break two times here in this unpacking procedure:
00411316 MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] <--- First time here.
00411317 MOV BL,2
00411319 CALL packed.0041138B
...
... <-------------------------------------------------- One more time somewhere here.
...
0041139D ADC ECX,ECX
0041139F CALL packed.0041138B
004113A4 JB SHORT packed.00411398
004113A6 RETN
004113A7 SUB EDI,DWORD PTR SS:[ESP+28]
004113AB MOV DWORD PTR SS:[ESP+1C],EDI
004113AF POPAD
004113B0 RETN
Press Shift+F9 once more and you will break on hardware bp on execution in our block:
003B0000 MOV EDI,DFB4AF72 <---------------------- Hre you are! Binary copy from here...
003B0006 LEA EDI,DWORD PTR DS:[1A58BA5F]
003B000C DEC EDI
...
...
003B0C5E TEST EDI,874CDC1C
003B0C64 AND EDI,9378C643 <---------------------- ...to here!
003B0C6A JMP packed.004079DE <------------------- This jump you will assemble.
003B0C6F ADD BYTE PTR DS:[EAX],AL
You can remove all breakpoints now. You are at the place before jumping to "false" OEP and execution of packed target. We will first deal with this stolen code. What we gona do is just binary copy-paste all that code to our NewOEP section, so select all from 3b0000 to 3b0c64 and binary copy it. Last jump we will change manually. Dou you remember where starts our NewOEP section? If not check file with LordPE, it satarts from 14000+400000=414000 so go there in olly:
00414000 ADD BYTE PTR DS:[EAX],AL
00414002 ADD BYTE PTR DS:[EAX],AL
00414004 ADD BYTE PTR DS:[EAX],AL
00414006 ADD BYTE PTR DS:[EAX],AL
00414008 ADD BYTE PTR DS:[EAX],AL
...
...
and binary paste previous copied code:
00414000 MOV EDI,DFB4AF72
00414006 LEA EDI,DWORD PTR DS:[1A58BA5F]
0041400C DEC EDI
...
...
00414C64 AND EDI,9378C643
00414C6A ADD BYTE PTR DS:[EAX],AL <------ And just here you need to place jump to false OEP.
00414C6C ADD BYTE PTR DS:[EAX],AL
00414C6E ADD BYTE PTR DS:[EAX],AL
And just assemble at the end that jump to false OEP. We couldn't binary copy it because value would be different, you have now:
00414000 MOV EDI,DFB4AF72
00414006 LEA EDI,DWORD PTR DS:[1A58BA5F]
0041400C DEC EDI
...
...
00414C64 AND EDI,9378C643
00414C6A JMP packed.004079DE
00414C6F NOP
00414C70 ADD BYTE PTR DS:[EAX],AL
00414C72 ADD BYTE PTR DS:[EAX],AL
Stolen code is resolved and now we must fix IAT.
3. Building new IAT
This is little harder part but it's not problem. First you must see how obfuscated import looks. Follow this address to see one example:
00401084 JMP DWORD PTR DS:[3A0784]
That is one import jump. So jump reads value that is stored at 3a0784 and jumps to that value. But interesting is, that value is just 4 bytes below first one DS:[003A0784]=003A0788. if we follow that in dump we will see this:
003A0784 88 07 3A 00 EB 01 C9 60 0F 31 EB 01 C9 8B D8 EB ..:....`.1......
003A0794 01 C9 8B CA 0F 31 2B C3 1B D1 EB 01 C7 0F 31 03 .....1+.......1.
003A07A4 C3 13 D1 0F 31 EB 01 C7 2B C3 EB 01 C7 1B D1 85 ....1...+.......
003A07B4 D2 75 0D 61 EB 01 C7 68 A1 AA E7 77 EB 01 E9 C3 .u.a...h...w....
003A07C4 EB 01 E9 F0 0F C7 C8 EB 01 E9 D2 07 3A 00 EB 01 ............:...
Select disassemble view to get clear picture
003A0784 8807 MOV BYTE PTR DS:[EDI],AL <--- Jump reads these 4 bytes and jumps there!
003A0786 3A00 CMP AL,BYTE PTR DS:[EAX] <--/
003A0788 EB 01 JMP SHORT 003A078B <--------- But that leads here!
003A078A C9 LEAVE
003A078B 60 PUSHAD
003A078C 0F31 RDTSC
003A078E EB 01 JMP SHORT 003A0791
003A0790 C9 LEAVE
003A0791 8BD8 MOV EBX,EAX
003A0793 EB 01 JMP SHORT 003A0796
003A0795 C9 LEAVE
003A0796 8BCA MOV ECX,EDX
003A0798 0F31 RDTSC
003A079A 2BC3 SUB EAX,EBX
003A079C 1BD1 SBB EDX,ECX
003A079E EB 01 JMP SHORT 003A07A1
003A07A0 C7 ???
003A07A1 0F31 RDTSC
003A07A3 03C3 ADD EAX,EBX
003A07A5 13D1 ADC EDX,ECX
003A07A7 0F31 RDTSC
003A07A9 EB 01 JMP SHORT 003A07AC
003A07AB C7 ???
003A07AC 2BC3 SUB EAX,EBX
003A07AE EB 01 JMP SHORT 003A07B1
003A07B0 C7 ???
003A07B1 1BD1 SBB EDX,ECX
003A07B3 85D2 TEST EDX,EDX
003A07B5 75 0D JNZ SHORT 003A07C4
003A07B7 61 POPAD
003A07B8 EB 01 JMP SHORT 003A07BB
003A07BA C7 ???
003A07BB 68 A1AAE777 PUSH kernel32.ReadFile
003A07C0 EB 01 JMP SHORT 003A07C3
003A07C2 -E9 C3EB01E9 JMP E93BF38A
Ha, you see trick? It just continues from same place. This code has some junk and when I removed it, we can see this:
003A0784 8807 MOV BYTE PTR DS:[EDI],AL <--- Our first 4 bytes.
003A0786 3A00 CMP AL,BYTE PTR DS:[EAX] <--/
003A0788 90 NOP
003A0789 90 NOP
003A078A 90 NOP
003A078B 60 PUSHAD
003A078C 0F31 RDTSC <-------------------- Hmm, check.
003A078E 90 NOP
003A078F 90 NOP
003A0790 90 NOP
003A0791 8BD8 MOV EBX,EAX
003A0793 90 NOP
003A0794 90 NOP
003A0795 90 NOP
003A0796 8BCA MOV ECX,EDX
003A0798 0F31 RDTSC
003A079A 2BC3 SUB EAX,EBX
003A079C 1BD1 SBB EDX,ECX
003A079E 90 NOP
003A079F 90 NOP
003A07A0 90 NOP
003A07A1 0F31 RDTSC
003A07A3 03C3 ADD EAX,EBX
003A07A5 13D1 ADC EDX,ECX
003A07A7 0F31 RDTSC
003A07A9 90 NOP
003A07AA 90 NOP
003A07AB 90 NOP
003A07AC 2BC3 SUB EAX,EBX
003A07AE 90 NOP
003A07AF 90 NOP
003A07B0 90 NOP
003A07B1 1BD1 SBB EDX,ECX
003A07B3 85D2 TEST EDX,EDX
003A07B5 75 0D JNZ SHORT 003A07C4 <------- You see? If this code is traced, then we jump below!
003A07B7 61 POPAD
003A07B8 90 NOP
003A07B9 90 NOP
003A07BA 90 NOP
003A07BB 68 A1AAE777 PUSH kernel32.ReadFile <---- Push API value!
003A07C0 90 NOP
003A07C1 90 NOP
003A07C2 90 NOP
003A07C3 C3 RETN <---------------------- Then jump to API!
003A07C4 90 NOP
003A07C5 90 NOP
003A07C6 90 NOP
003A07C7 F0:0FC7C8 LOCK CMPXCHG8B EAX <------- If traced we jump here where ilegal operation is!
We will fix this: we just take API value from push opcode 003A07BB 68 A1AAE777 PUSH kernel32.ReadFile and that value A1AAE777 we place instead our first 4 bytes of this obfuscated API call. But we will not do that manually, we will just use ollyscript for that. Lusky for us, all impoct jumps use this same pattern. After using script, we have:
00401084 JMP DWORD PTR DS:[3A0784] ; kernel32.ReadFile
But that is not done since ImpREc still cannot recognize such API jump. We will now use other script for making new IAT in our main image. First remember that we made NewIAT section at 16000+400000=416000 and we will redirect jumps there. Script will ask you where OEP is, that is 401000 section and where is newIAt section, that is 416000, and then it will rebuild new thunks. Your API jump will have new value:
00401084 JMP DWORD PTR DS:[416000] ; kernel32.ReadFile
Check that section in dump and you will see thunks:
00416000 A1 AA E7 77 90 C6 E6 77 81 EF E7 77 8D F0 E7 77 ...w...w...w...w
00416010 63 98 E7 77 DF E5 E7 77 AB E2 E7 77 8D 73 E7 77 c..w...w...w.s.w
00416020 FC AC E7 77 7E 17 E6 77 94 D9 E7 77 D3 6F ED 77 ...w~..w...w.o.w
00416030 35 4E E7 77 0C 61 E7 77 4F A7 E7 77 9C A8 E7 77 5N.w.a.wO..w...w
00416040 AA 7E E7 77 D5 A5 E7 77 CB 60 E7 77 C5 AB E7 77 .~.w...w.`.w...w
00416050 1E 15 E8 77 98 A6 E7 77 FC AC E7 77 D9 AC E7 77 ...w...w...w...w
00416060 D5 A5 E7 77 44 AB E7 77 A2 A9 E7 77 B1 E2 E7 77 ...wD..w.©.w...w
00416070 8D 73 E7 77 FC AC E7 77 94 D9 E7 77 02 DD E7 77 .s.w...w...w...w
00416080 AA 7E E7 77 0C 61 E7 77 00 00 00 00 44 0C F6 77 .~.w.a.w....D..w
00416090 02 15 F5 77 DE 55 F7 77 90 56 F7 77 CA 25 F5 77 ...w.U.w.V.w.%.w
004160A0 00 00 00 00 64 B0 D6 77 FB 6D D4 77 39 D6 D6 77 ....d..w.m.w9..w
004160B0 21 8B D4 77 50 5C D4 77 69 43 D4 77 77 43 D4 77 !..wP.wiC.wwC.w
004160C0 12 60 D4 77 FB 7F D4 77 7C 9C D4 77 FB 6D D4 77 .`.w...w|..w.m.w
004160D0 64 B0 D6 77 51 BF D4 77 1D 5F D4 77 9B 79 D4 77 d..wQ..w._.w.y.w
004160E0 E2 3D D4 77 84 5B D4 77 00 00 00 00 D8 17 DD 77 .=.w.[.w.......w
004160F0 9A 22 DD 77 10 24 DD 77 00 00 00 00 74 16 12 77 .".w.$.w....t..w
00416100 4B 17 12 77 8F 3D 12 77 EC 14 12 77 B8 AB 12 77 K..w.=.w...w...w
00416110 E2 19 12 77 00 00 00 00 CE 7C 01 7F 00 00 00 00 ...w.....|......
00416120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Good, IAT is fixed! Now you need dump and repair file.
3. Dumping and correcting dump
Just return now to that original block with stolen OEP code and place bp on last jump:
003B0C6A JMP packed.004079DE <------- Place bp here!
003B0C6F ADD BYTE PTR DS:[EAX],AL
003B0C71 ADD BYTE PTR DS:[EAX],AL
003B0C73 ADD BYTE PTR DS:[EAX],AL
Run, remove bp and pres F7 to get in target code section:
004079DE CALL packed.004045B4
004079E3 MOV ESI,packed.004095CC
004079E8 MOV EDI,packed.004095E4
004079ED MOV EBX,packed.004095A0
004079F2 XOR EAX,EAX
004079F4 PUSH EBP
004079F5 PUSH packed.00407C84
004079FA PUSH DWORD PTR FS:[EAX]
004079FD MOV DWORD PTR FS:[EAX],ESP
00407A00 MOV EAX,DWORD PTR DS:[408280]
00407A05 MOV EAX,DWORD PTR DS:[EAX]
...
...
Dump it from there. Use ImpREC, as OEP enter 79de and find imports. You will have all imports in 6 modules without any invalid. Fix dump and close ImpREC. If you start dumped file, it will probably run normaly because protector has stole just couple bytes, but I was testing some files that have important first few bytes and file would crush. So what we need is to change Original Entry Point to that NewOEP block where we have pasted stolen code. Open dump in LordPE and for EntryPoint enter 14000, save changes and run. File works ok :) That is all, I have included my dump and you can examne it so you can see what and how did I do it.
4. Couple more words
I have included in archive next files: packed.exe - original packed target; packed+2.exe - packed file which I added two new sections before unpacking; dumped.exe - unpacked and dumped target; "AlexProtector1.0 - tutorial.txt" - this tutorial.
I have included two scripts for fixing IAT. First Script "AlexProt I.txt" will fix obfuscation from IAT block. You need just enter base address of that block. Second script "AlexProt II.txt" is one that will make new IAT in some section that you chose. You need enter base address of code section so script can find jumps/calls and base of new section where you want to make new IAT. Scrip also needs to be manually changed in some parts for every new file. Actually, this script is one from my Armadillo tutorial.
Btw, for unpacking AlexProtextor 1.0 you have loveboom's script that can unpack and fix IAT on the fly. You can find that script on the forum in topic where I posted OllyScript plugin. But that script will fail in this example.
In this example we have unpacked delphi program where import jumps where JMP DWORD[xxxxxxxx]. In some other apps like ASM compiled, or C++, you can find CALL DWORD[xxxxxxxx].
-
Level : intermediate
====================================
Armadillo 4.30a with Debug Blocker - manually unpacking
====================================
Welcome to new, 5. tutorial in Armadillo serial! Today we will dealing Armadillo 4.30a with standard protection plus Debug Blocker feature, for those who are not familiar with terms - the infamous double process protection.
1. Tools and requirements
- Windows XP , MUST HAVE FOR THIS TUTORIAL;
- OllyDbg 1.10 (hide olly plugin or similar);
- LordPE;
- ImpREC;
- Hex editor;
For following this tutorial you must know how to use above tools, you must have some knowledge about PE file structure and you must know how to unpack single process Armadillo.
2. Killing Debug Blocker - taking control of unpacking process
Acctually, defeating Debug Blocker is very easy, you just must be careful that you do not make some mistake because then you will have to it all again. First, ignore all exceptions in olly and add custom ones if stop on some during unpacking. Load target in Olly, then use plugin to hide Olly and place breakpoint on OutputDebugStringA API. Here is Entry Point of armadillo:
00456000 PUSHAD
00456001 CALL Armadill.00456006
00456006 POP EBP
00456007 PUSH EAX
00456008 PUSH ECX
00456009 BSWAP EDX
0045600B NOT EDX
0045600D PUSHFD
0045600E NOT EDX
00456010 BSWAP EDX
00456012 JMP SHORT Armadill.00456023
00456014 MOV ECX,EBB80FEB
00456019 POP ES
...
...
...
Now you need to place bp on WriteProcessMemory in kernel but armadillo is checking couple first opcodes for brakpoints so I will place bp on end of that API. Hardware breakpoints didn't worked for me neither. So click "go to" and enter WriteProcessMemory to land in kernel:
77E61A94 PUSH EBP <---------------------- Begining of API, you are here!
77E61A95 MOV EBP,ESP
77E61A97 PUSH ECX
77E61A98 PUSH ECX
77E61A99 MOV EAX,DWORD PTR SS:[EBP+C]
77E61A9C PUSH EBX
77E61A9D MOV EBX,DWORD PTR SS:[EBP+14]
77E61AA0 PUSH ESI
...
...
...
77E61B23 LEAVE
77E61B24 RETN 14 <---------------------- Place bp here!!!
Place bp where I told you and run target. You will break on your bp. Remove it and with F7 return to main module and then press Ctrl+A to analyse code. Scroll up and you will see:
00437CD3 PUSH EAX ; /pBytesWritten = 00000001
00437CD4 PUSH 2 ; |BytesToWrite = 2
00437CD6 LEA ECX,DWORD PTR SS:[EBP-8] ; |
00437CD9 PUSH ECX ; |Buffer = 0012D83C
00437CDA MOV EDX,DWORD PTR SS:[EBP+10] ; |Armadill.<ModuleEntryPoint>
00437CDD PUSH EDX ; |Address = 7FFE0304
00437CDE MOV EAX,DWORD PTR SS:[EBP+8] ; |
00437CE1 MOV ECX,DWORD PTR DS:[EAX] ; |
00437CE3 PUSH ECX ; |hProcess = 0012D83C
00437CE4 CALL DWORD PTR DS:[<&KERNEL32.WriteProce>; WriteProcessMemory
00437CEA JO SHORT Armadill.00437CF3 <------------------------------------ You are here!!!!
00437CEC JL SHORT Armadill.00437CF1
00437CEE JMP SHORT Armadill.00437CF5
00437CF0 DB E8
00437CF1 JE SHORT Armadill.00437CEE
00437CF3 JMP SHORT Armadill.00437CEE
00437CF5 JMP SHORT Armadill.00437D56
00437CF7 LEA EDX,DWORD PTR SS:[EBP-4]
00437CFA PUSH EDX ; /pBytesWritten = 7FFE0304
00437CFB PUSH 2 ; |BytesToWrite = 2
00437CFD PUSH Armadill.0046E4A4 ; |Buffer = Armadill.0046E4A4
00437D02 MOV EAX,DWORD PTR SS:[EBP+10] ; |Armadill.<ModuleEntryPoint>
00437D05 PUSH EAX ; |Address = 1
00437D06 MOV ECX,DWORD PTR SS:[EBP+8] ; |
00437D09 MOV EDX,DWORD PTR DS:[ECX] ; |ntdll.77F75BD1
00437D0B PUSH EDX ; |hProcess = 7FFE0304
00437D0C CALL DWORD PTR DS:[<&KERNEL32.WriteProce>; WriteProcessMemory <------ Place bp here and run!!!
You see two calls to WriteProcessMemory and you have just returned from first one. What did this first one done, I don't know, but for us is important second call. Place bp it and run. A new process will will start, now you have two processes in task manager. When you break, remove bp and take a look closer what we have here:
/pBytesWritten = 0000004C
|BytesToWrite = 2 <-------------- Two bytes are written.
|Buffer = Armadill.0046E4A4 <---- This is buffer where bytes are. Follow this in dump!!!
|Armadill.<ModuleEntryPoint>
|Address = 456000 <-------------- This is where bytes will be written!
|
|
|hProcess = 0000004C
WriteProcessMemory
You see that arm will write two bytes from buffer to 456000 address. That address is entry point of armadillo itself. Folow that buffer in dump window (immediate constant):
0046E4A4 60 E8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 `...............
0046E4B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0046E4C4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Now change those bytes to EB FE which is hex value for infinite jump. This will make armadillo second process to loop forever. Remember original bytes or write them down because later you will need to restore them. Changes are:
0046E4A4 EB FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0046E4B4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0046E4C4 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Now press F9 to start target. Your CPU usage will grow because infinite jump. Dont mind for that. In a command bar type "bp WaitForDebugEvent" and place bp. You will break in kernel on that API:
77EB5B98 >PUSH EBP
77EB5B99 MOV EBP,ESP
77EB5B9B SUB ESP,68
77EB5B9E PUSH ESI
77EB5B9F PUSH DWORD PTR SS:[EBP+C]
77EB5BA2 LEA EAX,DWORD PTR SS:[EBP-8]
...
...
Remove that bp and return to armadillo code. You shoud be here (EAX should be=0):
0043328D TEST EAX,EAX
0043328F JE Armadill.00435CA5
00433295 MOV EAX,DWORD PTR SS:[EBP-204]
0043329B AND EAX,0FF
004332A0 TEST EAX,EAX
004332A2 JE SHORT Armadill.004332B7
004332A4 MOV ECX,DWORD PTR DS:[46E5B4]
004332AA CMP DWORD PTR DS:[ECX+20],0
004332AE JE SHORT Armadill.004332B7
004332B0 MOV BYTE PTR SS:[EBP-204],0
004332B7 PUSH Armadill.0046E4A8 ; /pCriticalSection = Armadill.0046E4A8
004332BC CALL DWORD PTR DS:[<&KERNEL32.EnterCriti>; EnterCriticalSection
004332C2 PUSHAD
004332C3 XOR EAX,EAX
...
...
Now we need to deatach processes. First you must know what proces is armadillo debugger process and what process is being debugged. That is very easy; process that is created later is debugger. But we must know it's PID. It is also not hard; open window for attaching to active process in olly and you will see two indentical processes. One that is RED, is our first proces. Second process is one that is created later and we need his PID. For my example that PID is 510. It will be different for you (it is different every time you start program). Remember that PID. Now we will make patch that will kill debug blocker. On our line of code assemble next opcodes:
0043328D PUSH 510 <----------------------------- 510=my PID, your will be different!
00433292 CALL kernel32.DebugActiveProcessStop <- To stop debugging. Only WinXP has this API!!!
00433297 NOP <---------------------------------- Then place bp here and run!!!
00433298 NOP
00433299 NOP
0043329A NOP
0043329B AND EAX,0FF
004332A0 TEST EAX,EAX
004332A2 JE SHORT Armadill.004332B7
004332A4 MOV ECX,DWORD PTR DS:[46E5B4]
004332AA CMP DWORD PTR DS:[ECX+20],0
004332AE JE SHORT Armadill.004332B7
004332B0 MOV BYTE PTR SS:[EBP-204],0
004332B7 PUSH Armadill.0046E4A8 ; /pCriticalSection = Armadill.0046E4A8
004332BC CALL DWORD PTR DS:[<&KERNEL32.EnterCriti>; EnterCriticalSection
004332C2 PUSHAD
004332C3 XOR EAX,EAX
...
...
PUSH opcode will have value of your PID. Then add CALL DebugActiveProcessStop, place bp on NOP below it and run. When olly break on your bp, Debug Blocker is defeated, two armadillo processes are separated now! Congratulations ;) DebugActiveProcessStop is reason why you must have Windows XP to kill Debug Blocker, only XP has this API that we need to deatach processes. Do you know what you can do now? You can close your Olly, that will kill just first proces. Armadillo second proces is looping forever in memory, you can see that in task manager. Read next chapter to see what's next.
3. Attaching and unpacking Armadillo
You have killed Debug Blocker, but now you need to unpack target. Without debug blocker, we have buisness with common armadillo and I have already explained how to unpack armadillo with single process tricks. But we must do one thing more. Our two processes are still there and we need only one. While your first olly is still working, open another one and attach to one of our two processes. You need attach to one that has PID that we have pushed, ie. second process. In my example it was 510 one.
When you attach, you will break in ntdll.dll on system breakpoint. Press F9 to run it and then press F12 to pause program. You will break here:
00456000 >JMP SHORT Armadill.<ModuleEntryPoint> <------- Here!!!
00456002 ADD BYTE PTR DS:[EAX],AL
00456004 ADD BYTE PTR DS:[EAX],AL
00456006 POP EBP
00456007 PUSH EAX
00456008 PUSH ECX
00456009 BSWAP EDX
0045600B NOT EDX
0045600D PUSHFD
0045600E NOT EDX
00456010 BSWAP EDX
00456012 JMP SHORT Armadill.00456023
00456014 MOV ECX,EBB80FEB
00456019 POP ES
0045601A MOV ECX,EB900FEB
0045601F OR CH,BH
00456021 JMP SHORT Armadill.0045602E
00456023 PREFIX REPNE:
00456024 JMP SHORT Armadill.0045601B
00456026 JMP SHORT Armadill.0045601E
...
...
...
Very good. Naw you can close first olly. Do you see infinite jump at Entry Point? You need restore original bytes there. Those where 60e8, restore them and you'll see usuall armadiillo entry point:
00456000 >PUSHAD
00456001 CALL Armadill.00456006
00456006 POP EBP
00456007 PUSH EAX
00456008 PUSH ECX
00456009 BSWAP EDX
0045600B NOT EDX
0045600D PUSHFD
0045600E NOT EDX
00456010 BSWAP EDX
00456012 JMP SHORT Armadill.00456023
00456014 MOV ECX,EBB80FEB
00456019 POP ES
0045601A MOV ECX,EB900FEB
0045601F OR CH,BH
00456021 JMP SHORT Armadill.0045602E
00456023 PREFIX REPNE:
00456024 JMP SHORT Armadill.0045601B
00456026 JMP SHORT Armadill.0045601E
...
...
...
And now you have armadillo with standard protection, just usual IAT problem. You can unpack it normal as any other single process armadillo. Use script that I give you in archive to unpack it if you are lazy, but it would be better that you practice a little. Dump and fix IAT with ImpREC. You can also reduce file size with LordPE.
-
Level : intermediate
=====================================
Armadillo 3.70 with Import Elimination - manually unpacking
=====================================
This is 4. tutorial in Armadillo serial and today we will talk about another specific Armadillo feature - Import Elimination.
1. Intro
Tools
- OllyDbg 1.10
- LordPE
- ImpREC
- Hex editor
Hi and welcome to new Armadillo tutorial!
As I sad, subject of this tutorial is Import Elimination feature. Target for this tutorial is unpackme taken from one KaGra's tutorial and that target has spliced code too, but we will not talk too much about it. It's been explained in previous tutorials along with usual IAT redirection. This version of Armadillo also uses CreateTollhelp32Snapshot API to get all processes and protected file crushes/exits if it finds ollydbg.exe. This is minor anoyance, just rename ollydbg.exe to something else. Also, version of armadillo isn't important, main thing is that our target has IAT elimination feature. Lets see what IAT elimination is.
After you have renamed ollydbg.exe to something else, load target in it. Using CreateThread API find OEP (like in previous tutorials), but do not place bp on first opcode because armadillo will find it and make infinite loop. Place bp on last opcode, run, remove bp and find jump to OEP:
003D7732 FFD7 CALL EDI <----- Jump to OEP!
003D7734 8BD8 MOV EBX,EAX
003D7736 5F POP EDI
003D7737 8BC3 MOV EAX,EBX
003D7739 5E POP ESI
003D773A 5B POP EBX
003D773B C3 RETN
Our target has a weird OEP:
00404000 WAIT <-------------------------- OEP!
00404001 FINIT
00404003 WAIT
00404004 FCLEX
00404006 FLDCW WORD PTR DS:[406000]
0040400C PUSH EBP
0040400D MOV EBP,ESP
0040400F CALL IAT_elim.00404415
00404014 PUSH 0
00404019 CALL DWORD PTR DS:[A99018] <------- IAT elimination!
0040401F MOV DWORD PTR DS:[40F007],EAX
00404024 PUSHAD
00404025 MOV DWORD PTR DS:[40F00B],ESP
0040402B JMP IAT_elim.00404060
00404030 MOV ESP,DWORD PTR DS:[40F00B]
00404036 POPAD
00404037 CALL IAT_elim.00404955
0040403C CALL IAT_elim.004044AE
00404041 MOV ESP,EBP
00404043 POP EBP
00404044 PUSH DWORD PTR DS:[40F1D4]
0040404A CALL DWORD PTR DS:[A99168] <------- IAT elimination!
00404050 WAIT
00404051 FCLEX
00404053 FLDCW WORD PTR DS:[406000]
00404059 RETN
Check where call at 00404019 leads you:
003BD687 PUSH EBP
003BD688 MOV EBP,ESP
003BD68A PUSH ECX
003BD68B PUSH EBX
003BD68C PUSH ESI
003BD68D PUSH EDI
003BD68E PUSHAD
003BD68F MOV EDX,DWORD PTR DS:[3E9218]
003BD695 ADD EDX,64
003BD698 CALL EDX <------------------------- Call to GetTickCount, to fool tools.
003BD69A MOV EDX,DWORD PTR DS:[3E91A4]
003BD6A0 ADD EDX,64
003BD6A3 MOV ECX,5
003BD6A8 CMP BYTE PTR DS:[EDX],0CC <------- Breakpoint check!
003BD6AB JE SHORT 003BD6B4
003BD6AD LOOPD SHORT 003BD6A8
003BD6AF PUSH DWORD PTR SS:[EBP+8]
003BD6B2 CALL EDX <------------------------- Call to GetModuleHandle!!!
003BD6B4 MOV DWORD PTR SS:[EBP-4],EAX
003BD6B7 POPAD
003BD6B8 MOV EAX,DWORD PTR SS:[EBP-4]
003BD6BB POP EDI
003BD6BC POP ESI
003BD6BD POP EBX
003BD6BE LEAVE
003BD6BF RETN 4
Our call leads to emulated import GetModuleHandle. This is usual armadillo emulation which you can see in the first tutorial only here that API call is also allocated in one seperate memory block. So what is IAT elimination? From what I noticed, Armadillo moves whole import table to some allocated memory block far away from our target image. This is making it hard to dump and also prevents ImpREC from getting imports. There is many ways how to repair this and make working dump and we gona see some of them.
2. Repairing IAT elimination
First we need to prevent usual emulation of imports and fix code splicing. Also do not forget to copy-paste PE header when you reach OEP. Idea for fixing IAT elimination is similar to code solicing one. We need to dump redirected IAT section to disk and attach it to our main dump, or we need to inject it somewhere in our main dump just like we did with code splicing. Take a look at the memory after you have reached OEP:
00400000 00001000 IAT_elim Imag R RWE
00401000 00003000 IAT_elim .idata Imag R RWE
00404000 00002000 IAT_elim .text Imag R RWE
00406000 00001000 IAT_elim .data Imag R RWE
00407000 00009000 IAT_elim .bss Imag R RWE
00410000 00001000 IAT_elim IMPORTS Imag R RWE
00411000 00030000 IAT_elim .text1 code Imag R RWE
00441000 00010000 IAT_elim .adata code Imag R RWE
00451000 00020000 IAT_elim .data1 data,imports Imag R RWE
00471000 00030000 IAT_elim .pdata Imag R RWE
004A1000 00002000 IAT_elim .rsrc resources Imag R RWE
.idata , .text , .data , .bss , IMPORTS and .rsc are packed file sections. We will not touch those ones. .text1 , .adata , data1 and .pdata are Armadillo's sections and we can use those for our injecting.
One more thing; in previous tutorials, after we changed that "Magic Jump" for fixing import emulation, our file would crush after finishing with imports. That wasn't problem there, but we can't aford that here and we gona see how to prevent that too. Let's go ;)
2.1 Code Splicing, usual IAT emulation and avoiding crushing
Load target in our olly (change ollydbg.exe to something else xxxx.exe). This version of armadillo doesn't have IsDebuggerPresent or OutputDebugStringA checks. We need to fix Code Splicing and normal import emulation first. Code splicing feature comes first and uses time API from msvctr.dll. We need to place bp on that API but we cannot do it untill that dll isn't loaded in memory. Olly has one nice option in debugging options under events, you can set olly to break on any new dll loading. So go there and select "Break on new module (DLL)" and hit F9 so many times until you see msvctr.dll in module window (I think 5 times). Then uncheck option for break on loading dll's (we don't need it anymore) and place "bp time" in command bar. Hit ENTER and you will break on time API. Remove bp and return to armadillo code (scroll down):
003D2620 CALL DWORD PTR DS:[3DB1A0] ; kernel32.VirtualAlloc
003D2626 MOV DWORD PTR SS:[EBP-1988],EAX <----------- Break here and set EAX=471000!!!
003D262C CMP DWORD PTR SS:[EBP-1988],0
003D2633 JE SHORT 003D2668
003D2635 PUSH 40
003D2637 PUSH 1000
003D263C PUSH DWORD PTR SS:[EBP-1990]
003D2642 PUSH DWORD PTR DS:[3E9230]
003D2648 CALL DWORD PTR DS:[3DB1A0] ; kernel32.VirtualAlloc
003D264E MOV DWORD PTR SS:[EBP-1988],EAX <----------- Break here and set EAX=471000!!!
003D2654 CMP DWORD PTR SS:[EBP-1988],0
003D265B JE SHORT 003D2668
In order to redirect spliced block to our .pdata section, change EAX values to 471000. If you don't remember how and why, then read previous tutorial. Remove all breakpoints that you have and place new "bp _stricmp" in command bar. Hit F9 and you will break there. Armadillo uses this API lot of times but we need to find place where it uses it for API processing. You can speed it up a little; remove bp from begining of the _stricmp API and place it on last opcode, what is RETN. Run and you'll break there. Check registers window:
EAX 00000001
ECX 003E0AA4 ASCII "ArmAccess"
EDX F03B0000
EBX 376BA5C1
ESP 0012BAD4
EBP 0012BAE8
ESI 77C422A2 msvcrt._stricmp
EDI 00000004
EIP 003BA80E
Keep pressing untill you see there next:
EAX FFFFFFFF
ECX 0012CEB0 ASCII "MessageBoxA"
EDX 0012CEB0 ASCII "MessageBoxA"
EBX 1BDA0012
ESP 0012BAE8
EBP 0012ED3C
ESI 00002710
EDI 0012D1E0
EIP 77C4230A msvcrt.77C4230A
Do you see that API names? We are in the API checking procedure. You may ask how the hell I know that? Simply, I unpacked bunch of targets before writting this tutorial and got habbit, but this is not general approach. You will need to dig alot when you try some other target. Return to armadillo code now (remove that and all bp's from before). You are at the place where is our familiar API check:
003D474C CMP DWORD PTR DS:[EAX+8],0
003D4750 JE SHORT 003D479A
003D4752 PUSH 100
003D4757 LEA EAX,DWORD PTR SS:[EBP-1E8C]
003D475D PUSH EAX
003D475E MOV EAX,DWORD PTR SS:[EBP-1D8C]
003D4764 PUSH DWORD PTR DS:[EAX]
003D4766 CALL 003B7C43
003D476B ADD ESP,0C
003D476E LEA EAX,DWORD PTR SS:[EBP-1E8C]
003D4774 PUSH EAX
003D4775 PUSH DWORD PTR SS:[EBP-1D84]
003D477B CALL DWORD PTR DS:[3DB330] ; msvcrt._stricmp
003D4781 POP ECX
003D4782 POP ECX
003D4783 TEST EAX,EAX
003D4785 JNZ SHORT 003D4798 <---------------- Change this jump to JMP!!!!
003D4787 MOV EAX,DWORD PTR SS:[EBP-1D8C]
003D478D MOV EAX,DWORD PTR DS:[EAX+8]
003D4790 MOV DWORD PTR SS:[EBP-1D80],EAX
003D4796 JMP SHORT 003D479A
003D4798 JMP SHORT 003D4737
Change that JNZ to JMP for prevention of redirecting imports. Then we need to place bp on some place that is being executed after all imports are passed. I found one such spot, but maybe there is more of them, don't know. Scroll up:
003D4241 TEST EAX,EAX
003D4243 JNZ SHORT 003D424A
003D4245 JMP 003D4986 <------------ Place bp here!!!
That jump will be executed after imports are checked. Place bp there (that must be only one bp in olly) and press F9. You will break there. We have fixed usuall armadillo IAT emulation/redirection, but armadillo dll is being eencrypted/decrypted on the fly so target will crush because we have change some bytes in it. Scroll down where you have changed that jump from JNZ to JMP and reastore back original JNZ. And that's it! Our target will run nice. We fixed spliced code and normall IAT problem and now we can find OEP. You know how, place bp on CreateThread and find that ECX that thorws you to OEP:
00404000 WAIT
00404001 FINIT
00404003 WAIT
00404004 FCLEX
00404006 FLDCW WORD PTR DS:[406000]
0040400C PUSH EBP
0040400D MOV EBP,ESP
0040400F CALL IAT_elim.00404415
00404014 PUSH 0
00404019 CALL DWORD PTR DS:[A99020] ; kernel32.GetModuleHandleA
0040401F MOV DWORD PTR DS:[40F007],EAX
00404024 PUSHAD
00404025 MOV DWORD PTR DS:[40F00B],ESP
0040402B JMP IAT_elim.00404060
00404030 MOV ESP,DWORD PTR DS:[40F00B]
00404036 POPAD
00404037 CALL IAT_elim.00404955
0040403C CALL IAT_elim.004044AE
00404041 MOV ESP,EBP
00404043 POP EBP
00404044 PUSH DWORD PTR DS:[40F1D4]
0040404A CALL DWORD PTR DS:[A99170] ; kernel32.ExitProcess
00404050 WAIT
00404051 FCLEX
00404053 FLDCW WORD PTR DS:[406000]
00404059 RETN
0040405A ADD BYTE PTR DS:[EAX],AL
...
...
Dou you see changes? Our imports are visible but they are in the A90000 block! ImpREC can't fix that, it will give error reading process memory. I will now describe how did I solve this, but first you must copy-paste PE header to get properly dump. Do it and dump target to hard disk. There are lot of approaches to solve problem of IAT Elimination, but basicly they are all similar.
2.2.1 First way, dumping pointers and attaching them to main dump
Ok, this is wery clumsy way how to do it, but this was first thing that fall on my mind. Problem is that you must think what will help ImpREC to recognize imports, unless you want to manually build whole IAT. ImpREC needs to find somewhere in the main exe image values that coresponds to some API's. Those values like 77xxxxxx and simmilar are placed inside A90000 block and ImpREC cannot handle that so you need to copy that values inside our main image. Lucky for us, Armadillo has added lot of its own sections that we don't need anymore so we can copy there our IAT table. IAT for this unpackme is small and you can find it if you follow taht [A99020]; kernel32.GetModuleHandleA in dump window:
00A98FB0 62 A6 3B 00 BC A7 3B 00 7F A6 3B 00 94 56 D4 77 b.;...;...;..V.w
00A98FC0 93 A7 3B 00 19 A6 3B 00 C8 A6 3B 00 25 E2 E7 77 ..;...;...;.%..w
00A98FD0 31 A6 3B 00 84 A6 3B 00 62 A6 3B 00 91 4B E7 77 1.;...;.b.;..K.w
00A98FE0 76 A6 3B 00 78 A6 3B 00 36 A7 3B 00 4D A7 3B 00 v.;.x.;.6.;.M.;.
...
...
...
00A991C0 CC 95 E6 77 33 A7 3B 00 13 A7 3B 00 26 A7 3B 00 ...w3.;...;.&.;.
00A991D0 5F A7 3B 00 78 A6 3B 00 2E 7E D4 77 AB AB AB AB _.;.x.;..~.w....
00A991E0 AB AB AB AB EE FE EE FE 00 00 00 00 00 00 00 00 ................
00A991F0 04 00 53 00 00 07 1F 00 58 AB AB AB AB AB AB AB ..S.....X.......
00A99200 AB FE EE FE EE FE EE FE 00 00 00 00 00 00 00 00 ................
00A99210 06 00 04 00 00 07 1C 00 2C 01 00 00 10 01 00 00 ........,.......
There you will see some 77xxxxxx DWORDS. Armadillo has scattered them inside this block. Binary copy whole block (from A98FB0 to A99210). Now we gonna chose where to paste that. I chose to paste it in .adata section (remember that .pdata is one that hold spliced code block so don't paste there). So I pasted that content in .adata section which start at 441000. Now open ImpREC and attach to our target. Set for OEP = 4000.If you click now "IAT AutoSearch" you will get nothing. That is problem with IAT Elimination. But we can still force ImpREc to find our pasted values. Set in "IAT infos needed" RVA=41000 (.adata start = 441000-400000=41000). For size you must enter some value that you estimate as enough big. 1000 is more than big, enter that and click "Get Imports". Ha, I got 5 invalid modules, and 142 invalid thunks. Click "Show Invalid" and cut all that invalid thunks. You will get 18 modules and 18 imports, one import per module, altough there are lot kernel32 and user32 ones. That is because scattering, but that still makes valid IAT. Fix dump and try to run it... it will crush. That is because pointers point to A90000 block. I will fix that by expanding fixed dump for B00000 bytes and pasting there values from A90000 block.
Open fixed dump in hex editor and add B00000 bytes to the end of file. Then change VirtualSize and RawSize of last section for B00000 bytes (add them) and save changes. My dumped file is now ~11.5 MB in size. Now open that dump in olly, go to 00A98FB0 in dump window and paste there whole that block 00A98FB0-00A99210 and save changes. In CPU window you should see now that pointers shows API values:
00404019 FF15 2090A900 CALL DWORD PTR DS:[A99020] ; kernel32.GetModuleHandleA
Run file and it will work. Problem is that such file will work only indentical systems like one on which target was unpacked, because fixed pointers. Other bad thing is it's size.
2.2.2 Second way - redirecting pointers using OllyScript
This is much better way because we will get dump that will work on all machines and file will be smaller. I hope that you didn't close olly because we will just continue from our OEP. First, fill select whole .idata section and fill it with zeroes. This is not important but I like clear section so I can see what I'm doing there. We sow that our imports look like this:
00404019 FF15 2090A900 CALL DWORD PTR DS:[A99020] ; kernel32.GetModuleHandleA
I wrote small script that will change all CALL DWORD[A9xxxx] API to CALL DWORD[401yyy] API where 401yyy is in .idata section. But consider that if you want to use this script on your computer, you will have to modify it. My script is searching pattern of bytes that corespond to CALL DWORD[A9xxxx] and those bytes are FF15????A900. So my script is searching for opcodes like this:
findop x,#FF15????A900#
where x is variable for address. I sugest you to read readme.txt from OllyScrypt plugin because this plugin is very helpfull for unpacking.
After using script, my pointers look like this:
00404019 FF15 00104000 CALL DWORD PTR DS:[401000] ; kernel32.GetModuleHandleA
Check .idata section now:
00401000 D9 AC E7 77 00 00 00 00 63 98 E7 77 00 00 00 00 ...w....c..w....
00401010 91 4B E7 77 00 00 00 00 25 E2 E7 77 00 00 00 00 .K.w....%..w....
00401020 7C 88 E7 77 00 00 00 00 A1 16 F5 77 00 00 00 00 |..w.......w....
00401030 CC 95 E6 77 00 00 00 00 A1 16 F5 77 00 00 00 00 ...w.......w....
00401040 6B 15 F5 77 00 00 00 00 2C DA D6 77 00 00 00 00 k..w....,..w....
00401050 D2 A0 D4 77 00 00 00 00 94 56 D4 77 00 00 00 00 ...w.....V.w....
00401060 2E 7E D4 77 00 00 00 00 32 A7 E7 77 00 00 00 00 .~.w....2..w....
00401070 12 50 D5 77 00 00 00 00 64 B0 D6 77 00 00 00 00 .P.w....d..w....
00401080 F0 9C D4 77 00 00 00 00 04 58 E7 77 00 00 00 00 ...w.....X.w....
00401090 C9 56 E7 77 00 00 00 00 00 00 00 00 00 00 00 00 .V.w............
004010A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
There you can clearly see thunks. There is one thunk per API again, it would be too complicated to write script that could put all API values for one dll in one thunk and this is also correct IAT and our dump will work. Now dump again from memory to hard disk.
And now it comes finall step. Open ImpREC, attach to target, enter OEP=4000 and click "IAT AutoSearch", then "Get Imports"and you will get 19 valid functions. Important thing now; UNCHECK "Add new section" and enter there RVA=1000 and then fix dump. Run dumped file and it will work great :) And this file is very small ~200 kb after using LordPE. So we got smal file that works on every machine.
3. Conclusions and final words
The third way to fix IAT problem would be to find where Armadillo is doing redirection. I tried to find that but I had no will to finish it.
I spoted one mistake about spliced code feature in my tutorial. We shouldn't use .pdata section for redirecting spliced block because that section holds some things that armadillo dll/loader uses while unpacking, which continues after we fix splicing. I noticed that when I filled whole that section with zeroes on the fly, my unpackme just crashed. Use .adata section instead because that one holds code that it not longer in use after unpacking loader.
-
Level : intermediate
=======================================
Armadillo with Code Splicing (anti dump) - manually unpacking
=======================================
This is the third tutorial in Armadillo serial and it will discuss about one specific feature - code splicing. I will show two ways how to deal with this protection.
1. Requirements and tools
Basic info needed:
- Basic knowledge about PE structure;
- some experience with unpacking protectors;
- read my two previous tutorials about Armadillo.
Tools:
- OllyDbg 1.10
- LordPE
- ImpREC
- Hex editor
Few words:
Code splicing is feature that "steals" blocks of original program code and redirects it to some allocated memory block. If we
dump such file, dump will be crippled and this is known also as anti-dump method. Interesting is that allocated blocks vary
from execution to execution of packed program, always on little different address. There is two ways to fix this; first way
is to prevent and redirect splicing, second is to dump that spliced block(s) from memory and attach it to our dump.
Also note that this target finds our ollydbg.exe and refuse to start if process with that name is pressent so you must rename
olly to something else. Also, this Armadillo version detects breakpoints on dll's so we will place our bp's always on second
or even better, on last API opcodes. That is not problem at all.
2. Searching OEP, fixing IAT
This version of Armadillo doesn't use Olly exploit so just place bp on last opcode in CreateThread API (you know, go to ->
expression -> type CreateThread and place bp on last RETN) and run. Return to code and find that CALL EDI that throws you on
OEP:
003D6E73 CALL EDI <------ Jump to OEP!!
003D6E75 MOV EBX,EAX
003D6E77 POP EDI
003D6E78 MOV EAX,EBX
003D6E7A POP ESI
003D6E7B POP EBX
003D6E7C RETN
This is probably some 3.xx armadillo version because there are no CALL ECX but EDI. Execute it and you will land on OEP:
004061D0 PUSH EBP <------------------------- OEP!!!
004061D1 MOV EBP,ESP
004061D3 PUSH -1
004061D5 PUSH Armadill.0040A0D0
004061DA PUSH Armadill.00406F5C
004061DF MOV EAX,DWORD PTR FS:[0]
004061E5 PUSH EAX
004061E6 MOV DWORD PTR FS:[0],ESP
004061ED SUB ESP,58
004061F0 PUSH EBX
004061F1 PUSH ESI
004061F2 PUSH EDI
004061F3 MOV DWORD PTR SS:[EBP-18],ESP
004061F6 CALL DWORD PTR DS:[40A02C] <------- Missing import!!!
004061FC XOR EDX,EDX
004061FE MOV DL,AH
00406200 MOV DWORD PTR DS:[40C57C],EDX
You will not dump file now. Dumping will be the last thing to do in this tutorial. First we will prevent IAT redirecting and
save ImpREC tree for further use. Follow that DS:[40A02C] in dump:
0040A02C F7 DC 3B 00 1A AF 3B 00 B7 AF 3B 00 0C E6 E7 77 ..;...;...;....w
0040A03C A1 16 F5 77 6B 15 F5 77 95 9B E9 77 FC AC E7 77 ...wk..w...w...w
0040A04C 2F E0 E9 77 E8 E4 E7 77 9C A8 E7 77 54 AB 3B 00 /..w...w...wT.;.
Do you remember what you need to do from previous tutorials? Remember it's address in dump 0040A02C and restart target in
olly. Place bp on end of CreateThread API and follow in dump 0040A02C address which is place where API redirection value will
be placed:
0040A02C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Place hardware bp on write there, on first 4 bytes and run olly.
Breaking first time is not important:
77C42F43 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
77C42F45 JMP DWORD PTR DS:[EDX*4+77C43058]
77C42F4C MOV EAX,EDI
77C42F4E MOV EDX,3
77C42F53 SUB ECX,4
77C42F56 JB SHORT msvcrt.77C42F64
77C42F58 AND EAX,3
77C42F5B ADD ECX,EAX
77C42F5D JMP DWORD PTR DS:[EAX*4+77C42F70]
77C42F64 JMP DWORD PTR DS:[ECX*4+77C43068]
...
...
Second time is what we want (then delete hw bp):
003D4045 CMP DWORD PTR DS:[EAX+8],0
003D4049 JE SHORT 003D4093
003D404B PUSH 100
003D4050 LEA EAX,DWORD PTR SS:[EBP-1E84]
003D4056 PUSH EAX
003D4057 MOV EAX,DWORD PTR SS:[EBP-1D84]
003D405D PUSH DWORD PTR DS:[EAX]
003D405F CALL 003B7816
003D4064 ADD ESP,0C
003D4067 LEA EAX,DWORD PTR SS:[EBP-1E84]
003D406D PUSH EAX
003D406E PUSH DWORD PTR SS:[EBP-1D7C]
003D4074 CALL DWORD PTR DS:[3DB32C] ; msvcrt._stricmp
003D407A POP ECX
003D407B POP ECX
003D407C TEST EAX,EAX
003D407E JNZ SHORT 003D4091 <---------------------- Most important place, magic jump!!!
...
...
...
003D419A MOV DWORD PTR DS:[EAX],ECX
003D419C MOV EAX,DWORD PTR SS:[EBP-17DC] <--------- You landed here!!!
Remember address of "magic jump" 003D407E. Of course, on your system it can have different value. Now, remember what we have
done in second tutorial. This file has those CRC-decryption-encryption loops and file will crush when we change something.
Restart target, place bp on VirtualAlloc+1 in command bar and press Shift+F9 two times. Then in CPU window go to our jump
location 003D407E:
003D407E ADD BYTE PTR DS:[EAX],AL <---- You're here!
003D4080 ADD BYTE PTR DS:[EAX],AL
003D4082 ADD BYTE PTR DS:[EAX],AL
003D4084 ADD BYTE PTR DS:[EAX],AL
003D4086 ADD BYTE PTR DS:[EAX],AL
...
...
You will see nothing there because armadillo dll still isn't unpacked there. Never mind, you place hardware bp on execution
on 003D407E address, remove bp from VirtualAlloc and run olly. You will break on unpacked-decrypted code:
003D407E JNZ SHORT 003D4091 <---------------- Our magic jump!!!
003D4080 MOV EAX,DWORD PTR SS:[EBP-1D84]
003D4086 MOV EAX,DWORD PTR DS:[EAX+8]
003D4089 MOV DWORD PTR SS:[EBP-1D78],EAX
003D408F JMP SHORT 003D4093
003D4091 JMP SHORT 003D4030
003D4093 CMP DWORD PTR SS:[EBP-1D78],0
003D409A JNZ SHORT 003D40DB
003D409C MOVZX EAX,WORD PTR SS:[EBP-1D74]
...
...
Remove hardware bp, change JNZ to JMP and run olly. It will crush, but we have expected that. Still our import section will
have what we want. Like in the second tutorial, open another olly instance (hidden one too) and find OEP in new instance of
target. Import section is .rdata , binary copy whole that section from crushed target and paste it to new one where you found
OEP. Close olly that holds crushed target, open ImpREC and find imports. You will have 3 invalid thunks, cut it, and save
import tree to hard drive (remember, dumping is last thing).
First part of job is done and now it's time to meet Code Splacing.
3. Code Splicing - anti dumps
Trace in olly with F8 to first call:
0040622A CALL Armadill.00406E27
and enter in it:
00406E27 XOR EAX,EAX
00406E29 PUSH 0
00406E2B CMP DWORD PTR SS:[ESP+8],EAX
00406E2F JMP 03B542CA <------------------------------- Look this jump!!!
00406E34 SETE AL
00406E37 PUSH EAX
00406E38 CALL DWORD PTR DS:[40A06C] ; kernel32.HeapCreate
00406E3E TEST EAX,EAX
00406E40 JMP 03B542DC <------------------------------- Look this jump!!!
00406E45 JE SHORT Armadill.00406E5C
00406E47 CALL Armadill.004074B3
00406E4C TEST EAX,EAX
00406E4E JNZ SHORT Armadill.00406E5F
00406E50 JMP 03B542EF <------------------------------- Look this jump!!!
00406E55 NOP
00406E56 CALL DWORD PTR DS:[40A068] ; kernel32.HeapDestroy
00406E5C XOR EAX,EAX
00406E5E RETN
00406E5F PUSH 1
00406E61 POP EAX
00406E62 RETN
00406E63 INT3
00406E64 JMP 03B54300 <------------------------------- Look this jump!!!
There you will see lot of such jumps. Values of that jumps will be different at you. That is code splicing. Program flow
jumps to some allocated place of memory which cannot be dumped with main exe. There are two ways to fix this.
3.1 First way (lame one :) - dump exe, dump spliced block and attach it to main dump
- Ok, first repair PE header of our target, you know how, open another olly and copy-paste PE header from packed instance of
target to our first - unpacked. Then dump file with LordPE or olly dump (OEP is 61D0). Now use ImpREC and saved tree and fix
IAT. Now we need to add spliced block to dumped file in order to get working dump.
At my computer spliced block is here (and it's size is that big):
03B50000 00016000 Priv RWE RWE
- I will add to my dumped file 4000000 bytes (zeros) with hex editor to the end of the file. That is more than 60 MB, quite a
big. Don't wory for that, we gona fix size of exe later.
- Then I open LordPE's PE editor and there I will change VirtualSize and RawSize of last section (.mackt) for that amount of
bytes, so now my VS is 04001000 and RW is 04001000 too.
- Now I will open that dump in another olly and I will go at the 3B50000 address in it:
03B50000 0000 ADD BYTE PTR DS:[EAX],AL
03B50002 0000 ADD BYTE PTR DS:[EAX],AL
03B50004 0000 ADD BYTE PTR DS:[EAX],AL
03B50006 0000 ADD BYTE PTR DS:[EAX],AL
03B50008 0000 ADD BYTE PTR DS:[EAX],AL
03B5000A 0000 ADD BYTE PTR DS:[EAX],AL
03B5000C 0000 ADD BYTE PTR DS:[EAX],AL
03B5000E 0000 ADD BYTE PTR DS:[EAX],AL
...
...
That is where spliced code block should be. Now I will just binary copy all bytes from spliced block and paste it to my
dumped file starting from 3b5000 address and save changes. Since dump is big it can take some time.
- Now, after I have save all changes, I runned dumped file and guess what? Dump works great! Yeah! Do it like I did and your
file fill also work. If it's not working, check where did you make mistake. It's wery simple actually.
Ok, file works, but file on my drive is 64 MB in size! That's a big waste of space. Do not wory, you can reduce size of it
with LordPE or just pack it with some packer (WinUpack can pack it to ~250 kb). Rared file is less than 300 kb. But still,
this file waste lot of memory space for nothing. Second way of solwing splicing problem is much better.
Dumped file that I got with this way of unpacking I didn't put in archive to keep it smaller.
3.2 Redirecting spliced block
If you find OEP couple times in the same packed file, you will see that jumps to spliced block change thir values. This block
is loaded every time under some other address. And there is idea, maybe we can redirect that block to some closer place in
memory or even in our dump. Since base address of spliced block is some random value, you can conclude that armadillo uses
some function like GetTickCount to get/generate some random values. Indeed, I found that armadillo uses "time" API from
msvcrt.dll. You going to find it in this way, place bp on VirtualAlloc and press Shift+F9 3 times. At this time most of dll's
needed for operation are loaded. Then place bp on time API and pree Shift+F9 (remove bp from VirtualAlloc before). You will
break in msvcrt.dll, remove breakpoint and return tu code:
003D1EAF CALL DWORD PTR DS:[3DB2A8] ; msvcrt.time
003D1EB5 POP ECX <-------------------------------------- You are here!!!
003D1EB6 MOV DWORD PTR SS:[EBP-3110],EAX <-------------- EAX holds random value.
003D1EBC MOV EAX,DWORD PTR SS:[EBP-3110]
003D1EC2 MOV DWORD PTR SS:[EBP-1998],EAX
003D1EC8 AND DWORD PTR SS:[EBP-1980],0
003D1ECF CMP DWORD PTR SS:[EBP-1980],0
003D1ED6 JNZ 003D1FA3
003D1EDC MOV EAX,DWORD PTR SS:[EBP-1984]
003D1EE2 DEC EAX
003D1EE3 MOV DWORD PTR SS:[EBP-1984],EAX
003D1EE9 CMP DWORD PTR SS:[EBP-1984],0
003D1EF0 JNB SHORT 003D1F13
003D1EF2 MOV EAX,DWORD PTR SS:[EBP+8]
003D1EF5 MOV EAX,DWORD PTR DS:[EAX]
003D1EF7 AND DWORD PTR DS:[EAX],0
003D1EFA PUSH 3E1A9C ; ASCII "Location CS1"
003D1EFF MOV EAX,DWORD PTR SS:[EBP+8]
003D1F02 PUSH DWORD PTR DS:[EAX+4]
003D1F05 CALL 003DA258 ; JMP to msvcrt.strcpy
003D1F0A POP ECX
003D1F0B POP ECX
003D1F0C XOR EAX,EAX
003D1F0E JMP 003D512A
003D1F13 CMP DWORD PTR SS:[EBP-1984],0
003D1F1A JBE SHORT 003D1F33
003D1F1C LEA ECX,DWORD PTR SS:[EBP-1998]
003D1F22 CALL 003B511B
003D1F27 AND EAX,3FF0000 <---------------- The most important place vhere value gets final shape.
003D1F2C MOV DWORD PTR DS:[3E91D8],EAX <-- Save that value.
003D1F31 JMP SHORT 003D1F43
003D1F33 CMP DWORD PTR SS:[EBP-1984],0
003D1F3A JNZ SHORT 003D1F43
003D1F3C AND DWORD PTR DS:[3E91D8],0
003D1F43 PUSH 40
003D1F45 PUSH 2000
003D1F4A PUSH DWORD PTR SS:[EBP-1988]
003D1F50 PUSH DWORD PTR DS:[3E91D8]
003D1F56 CALL DWORD PTR DS:[3DB1B0] ; kernel32.VirtualAlloc <--- Allocating that memory.
003D1F5C MOV DWORD PTR SS:[EBP-1980],EAX <----------------------- We will change this EAX value!!!
003D1F62 CMP DWORD PTR SS:[EBP-1980],0
003D1F69 JE SHORT 003D1F9E <--------------- Check! This jump must not be taken!!
003D1F6B PUSH 40
003D1F6D PUSH 1000
003D1F72 PUSH DWORD PTR SS:[EBP-1988]
003D1F78 PUSH DWORD PTR DS:[3E91D8]
003D1F7E CALL DWORD PTR DS:[3DB1B0] ; kernel32.VirtualAlloc
003D1F84 MOV DWORD PTR SS:[EBP-1980],EAX <----------------------- We will change this EAX value!!!
003D1F8A CMP DWORD PTR SS:[EBP-1980],0
003D1F91 JE SHORT 003D1F9E <--------------- Check! This jump must not be taken!!
003D1F93 MOV EAX,DWORD PTR SS:[EBP-1980]
003D1F99 MOV DWORD PTR DS:[3E91D8],EAX
003D1F9E JMP 003D1ECF
003D1FA3 PUSH 1
You have just came from time function and EAX holds some random value. Most important place is that at 003D1F27 address.
There that random value will get final shape, something like 1C70000 and that is address where block will start. Than that
value is saved. Here we can force armadillo to extract that code where we like. Notice in memory dump what sections armadillo
has:
00400000 00001000 Armadill PE header Imag R RWE
00401000 00009000 Armadill .text Imag R RWE
0040A000 00001000 Armadill .rdata Imag R RWE
0040B000 00003000 Armadill .data Imag R RWE
0040E000 00030000 Armadill .text1 code Imag R RWE
0043E000 00010000 Armadill .adata code Imag R RWE
0044E000 00020000 Armadill .data1 data,imports Imag R RWE
0046E000 00030000 Armadill .pdata Imag R RWE
0049E000 00001000 Armadill .rsrc resources Imag R RWE
.text1, .adata, .data1 and .pdata are armadillo's sections. We can use some of that section as place for redirection. I will
force my redirection to .pdata section.
We will let armadillo to reserve block where he likes, but we gonna change address where it will extract code. Simply, when
you came to this two lines, change EAX to 46E000:
003D1F5C MOV DWORD PTR SS:[EBP-1980],EAX
003D1F84 MOV DWORD PTR SS:[EBP-1980],EAX
And that's it ;)! Armadillo will extract spliced plock in our dump. Now just place bp on CreateThread API and find OEP. When
you find OEP don't forget to fix PE header first. Than dump and then use that imports tree for ImpREC that you created and
saved in 2. chapter of this tutorial. With it , fix IAT of dumped file. Run it... It works!!! And our file size is now 650 kb
and not gigabytes like in first example. Maybe you can reduce size even more, but who cares. Armadillo with splicing code is
defeated.
4. Final words
I hope that my next tutorial will be about armadillo's import destruction or Debug Blocker features, but I will see can I do
that. With this knolwedge from this three tutorials, you can already unpack bunch of armadillo targets. Lot of targets don't
even use splicing code, just minimum protection.
-
Level : intermediate
=======================================
Armadillo 4.30a - unpacking armadillo with standard protection
=======================================
Welcome to next Armadillo tutorial! This tutorial is just second part of first one and heavily relies on it.
1. Requirements
- Windows XP
- Target
- OllyDbg 1.10
- ImpREC
- LordPE
Ofcourse, you must know how to use those tools. I will not explain how to set memory breakpoint on access,or hardware, or what window you need to open to find that what I'm talking about. It's pretty exousting to write in that way and if you wan't to deal with protectors you must already know all that.
Few words about our target :
- It uses same tricks as minimal protection;
- Encrypts loader code so it's harder to find redirection place;
- Decrypt/encrypt depends on CRC calculation, our changes affect target.
2. Reach OEP
You know how to reach OEP from first tutorial: use bp on OutputDebugStringA to kill Olly exploit, place bp on CreateThread to find CAL ECX that will throw you at OEP. Fix PE header by copy-paste bytes from another instance of target and dump file. You found OEP to be here:
004013FB PUSH EBP <--------------------- OEP!!!
004013FC MOV EBP,ESP
004013FE PUSH -1
00401400 PUSH Armadill.004040B8
00401405 PUSH Armadill.00401F30
0040140A MOV EAX,DWORD PTR FS:[0]
00401410 PUSH EAX
00401411 MOV DWORD PTR FS:[0],ESP
00401418 SUB ESP,58
0040141B PUSH EBX
0040141C PUSH ESI
0040141D PUSH EDI
0040141E MOV DWORD PTR SS:[EBP-18],ESP
00401421 CALL DWORD PTR DS:[40402C] <---- Missing import!!!
00401427 XOR EDX,EDX
00401429 MOV DL,AH
0040142B MOV DWORD PTR DS:[405544],EDX
3. Fixing imports
We will fix imports in the same way as we did in first tutorial, we will change magic jump from JNZ to JMP so it will never redirect imports. But there is two small problems - encryption and CRC. I hope that you didn't close olly after dumping. If you have, then again find OEP. Check that missing import:
00401421 CALL DWORD PTR DS:[40402C] <---- Missing import!!!
Follow it in dump:
0040402C 38ADAB00 567CAB00 197DAB00 0CE6E777 8...V|...}.....w
0040403C 959BE977 FCACE777 2FE0E977 E8E4E777 ...w...w/..w...w
Remember address of that redirected import -> 004042C. Now restart target in olly and get to OutputDebugStringA check. After you have fixed that check go to dump window and find this address. It will be empty but you place hardware bp on write on DWORD there (on first 4 bytes - zeroes) and press F9. You will stop first here (after nag window):
77C42F43 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
77C42F45 JMP DWORD PTR DS:[EDX*4+77C43058]
77C42F4C MOV EAX,EDI
77C42F4E MOV EDX,3
77C42F53 SUB ECX,4
77C42F56 JB SHORT msvcrt.77C42F64
77C42F58 AND EAX,3
...
...
Pres one more time F9 and you will stop where armadillo has wrote 4 bytes:
00ACCA31 CMP DWORD PTR DS:[EAX+8],0
00ACCA35 JE SHORT 00ACCA80
00ACCA37 PUSH 100
00ACCA3C LEA EAX,DWORD PTR SS:[EBP-3EA8]
00ACCA42 PUSH EAX
00ACCA43 MOV EAX,DWORD PTR SS:[EBP-3DA8]
00ACCA49 PUSH DWORD PTR DS:[EAX]
00ACCA4B CALL 00AA1FEC
00ACCA50 ADD ESP,0C
00ACCA53 LEA EAX,DWORD PTR SS:[EBP-3EA8]
00ACCA59 PUSH EAX
00ACCA5A LEA EAX,DWORD PTR SS:[EBP-3D98]
00ACCA60 PUSH EAX
00ACCA61 CALL DWORD PTR DS:[AD6388] ; msvcrt._stricmp <--- Remember this comparation from first tut!?!
00ACCA67 POP ECX
00ACCA68 POP ECX
00ACCA69 TEST EAX,EAX
00ACCA6B JNZ SHORT 00ACCA7E <------------------- Our magic jump! Remeber its address!!!
00ACCA6D MOV EAX,DWORD PTR SS:[EBP-3DA8]
00ACCA73 MOV EAX,DWORD PTR DS:[EAX+8]
00ACCA76 MOV DWORD PTR SS:[EBP-3598],EAX
00ACCA7C JMP SHORT 00ACCA80
00ACCA7E JMP SHORT 00ACCA1C
...
...
...
00ACCC1C MOV DWORD PTR DS:[EAX],ECX
00ACCC1E MOV EAX,DWORD PTR SS:[EBP-26F0] <------ You are here!!! Scroll up!!!
This code should be familiar to you from first tutorial. The most important part is our jump at 00ACCA6B. That address could be different on your computer, write it down. Now restart target in olly again and again get to the OutputDebugStringA. Fix it and then in CPU window go to 00ACCA6B expression (our jump):
00ACCA6B SAHF
00ACCA6C DAA
00ACCA6D JE SHORT 00ACC9F6
00ACCA6F IMUL EBX,EDX,-11
00ACCA72 AND BL,BYTE PTR DS:[ECX]
00ACCA74 ADC AH,BYTE PTR DS:[EDX-36]
00ACCA77 JMP FAR 7F51:23BB3B3D
00ACCA7E INT 0B6
00ACCA80 XCHG EAX,ECX
00ACCA81 MOV AH,2F
00ACCA83 INC EBX
00ACCA84 ADC BYTE PTR DS:[EBX+E602C4CA],BH
00ACCA8A SBB BYTE PTR DS:[ESI+ESI*8-57],91
00ACCA8F INT3
...
...
Instead of our jump, you will see some junk code as above. That is because in standard protection Armadillo dll is encrypted and decrypted on the fly. But we can easily solve this problem; on address 00ACCA6B where our jump should be, place hardware breakpoint on execution and just run olly. After nag window, code will decrypt and olly will stop on your breakpoint:
00ACCA6B JNZ SHORT 00ACCA7E
00ACCA6D MOV EAX,DWORD PTR SS:[EBP-3DA8]
00ACCA73 MOV EAX,DWORD PTR DS:[EAX+8]
00ACCA76 MOV DWORD PTR SS:[EBP-3598],EAX
00ACCA7C JMP SHORT 00ACCA80
00ACCA7E JMP SHORT 00ACCA1C
00ACCA80 MOV EAX,DWORD PTR SS:[EBP-2B58]
00ACCA86 INC EAX
...
...
...
Remove breakpoint and change JNZ to JMP and just run our target (F9). Soon target will just crush on some exception. Problem is that encrypt/decrypt process depends on some integrity check and since we have changed some bytes, file has become useless. But don't panic, this is not problem at all. Our file is crushed, but import section .rdata contains valid thunks. So binary copy whole .rdata section and open another instance of olly. Open target in that olly and find OEP without messing with imports problem. When you reach OEP there, just binary paste data from clipboard to .rdata section. You can close first olly now. Open ImpREC now and attach to our target, find imports, cut all invalid ones and repair dumped file. That's it! Run it and it will work fine ;)
-
Level : intermediate
=======================================
Armadillo 4.30a - unpacking armadillo with minimum protection
=======================================
1. Preparation
You will need next tools to follow this tutorial:
- Windows XP
- OllyDbg 1.10;
- ImpREC;
- LordPE;
- PEiD 0.93 (optional).
Unpacking armadillo can be very simple if protected target is using only minimum protection and this kind of apps you can find all over the net. I really don't know why developers doesn't use all options, maybe double process slows down protected program what can be issue if program is some maintaince utility like reg cleaner, defrag tool or similar. Anyway, in this case we have to deal with next problems:
- Olly OutputDebugStringA exploit;
- PE header changes that locks file;
- Import redirection and emulation.
2. Reaching OEP
First ignore all exceptions in olly options. Then open target in olly, click "Go to"->"Expression", enter VirtualAlloc and click OK. You will land in kernel on that API:
77E7ABC5 PUSH EBP <--------------------- Start of VirtualAlloc API.
77E7ABC6 MOV EBP,ESP
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 bp here so Amadillo don't find it!!!
What acctually I wan't here? Armadillo will unpack and load it's own dll in memory so we must find where. When you break on this bp, AEX register will hold base address of allocated memory block where that dll will be unpacked. Press F9 once and when you stop on bp EAX will be =0. Press once more and EAX will now hold some value. On my machine EAX=00AA0000, for you it can differ. Now erase that bp and place bp in command bar on OutputDebugStringA API. Press F9 and you will land on it:
77E9B493 PUSH 22C <------------------------ You are here!!!
77E9B498 PUSH kernel32.77E9BE60
77E9B49D CALL kernel32.77E7A22B
...
...
...
77E9B4CB CALL kernel32.RaiseException
77E9B4D0 OR DWORD PTR SS:[EBP-4],FFFFFFFF
77E9B4D4 CALL kernel32.77E7A2F2
77E9B4D9 RETN 4
This is place where armadillo will try to crush olly. Olly cannot stand %s%s... string and that will just crush it. So we need to kill this check. It's not hard, just change first opcode of API to last one. So, remove bp and place instead PUSH 22C RETN 4:
77E9B493 RETN 4 <-------------------------- Changed!
77E9B496 NOP
77E9B497 NOP
77E9B498 PUSH kernel32.77E9BE60
...
...
...
77E9B4CB CALL kernel32.RaiseException
77E9B4D0 OR DWORD PTR SS:[EBP-4],FFFFFFFF
77E9B4D4 CALL kernel32.77E7A2F2
77E9B4D9 RETN 4
Now place bp on CreateThread API and run olly. You will break in kernel on CreateThread API (after nag window), remove bp from there, return to code with Alt+F9:
00AB94C4 POP EDI <----- You are now here!
00AB94C5 POP ESI
00AB94C6 LEAVE
00AB94C7 RETN <-------- Just trace and execute this RET with F7!
Do what I tell you and after exiting RET, you will see this:
00AC972D POP ECX
00AC972E MOV EDI,0AD8910
00AC9733 MOV ECX,EDI
...
...
...
00AC97ED CALL ECX
00AC97EF JMP SHORT 00AC9814
00AC97F1 CMP EDX,1
00AC97F4 JNZ SHORT 00AC9817
00AC97F6 PUSH DWORD PTR DS:[ESI+4]
00AC97F9 MOV EDX,DWORD PTR DS:[EAX+88]
00AC97FF XOR EDX,DWORD PTR DS:[EAX+84]
00AC9805 PUSH DWORD PTR DS:[ESI+8]
00AC9808 XOR EDX,DWORD PTR DS:[EAX+40]
00AC980B PUSH 0
00AC980D PUSH DWORD PTR DS:[ESI+C]
00AC9810 SUB ECX,EDX
00AC9812 CALL ECX <----------------------- Jump to OEP!!!
00AC9814 MOV DWORD PTR SS:[EBP-4],EAX
00AC9817 MOV EAX,DWORD PTR SS:[EBP-4]
00AC981A POP EDI
00AC981B POP ESI
00AC981C LEAVE
00AC981D RETN
You see that last CALL ECX? That is your jump to OEP. In previous versions 3.xx there was CALL EDI opcode instead CALL ECX, but armadillo developer has changed. He changes small deatails like that to prevent making of generic unpackers and olly scripts. I didn't get to that idea, others told me so I don't know is it truth but it could be. That call is your jump to OEP so execute it and you'll land on OEP at:
004013FB PUSH EBP <--------------------- OEP!!!
004013FC MOV EBP,ESP
004013FE PUSH -1
00401400 PUSH Armadill.004040B8
00401405 PUSH Armadill.00401F30
0040140A MOV EAX,DWORD PTR FS:[0]
00401410 PUSH EAX
00401411 MOV DWORD PTR FS:[0],ESP
00401418 SUB ESP,58
0040141B PUSH EBX
0040141C PUSH ESI
0040141D PUSH EDI
0040141E MOV DWORD PTR SS:[EBP-18],ESP
00401421 CALL DWORD PTR DS:[40402C] <----- Here it should b some import!!!
00401427 XOR EDX,EDX
00401429 MOV DL,AH
...
...
...
And you have found OEP. But if you dump file now it will be damage and locked because armadillo has changed three values in PE header. Also there is much bigger problem with stolen imports.
3. PE header issue
If you open memory map window you'll see that PE header is damaged and olly doesn't recognize it:
00400000 00001000 Armadill Imag R RWE <--- PE header!!!
00401000 00003000 Armadill .text Imag R RWE
00404000 00001000 Armadill .rdata Imag R RWE
00405000 00001000 Armadill .data Imag R RWE
00406000 00050000 Armadill .text1 code Imag R RWE
00456000 00010000 Armadill .adata Imag R RWE
00466000 00020000 Armadill .data1 data,imports Imag R RWE
00486000 00030000 Armadill .pdata Imag R RWE
004B6000 00002000 Armadill .rsrc resources Imag R RWE
Three values that armadillo has deleted are, PE header offset in DOS header, number of sections in PE header and EnryPoint of exe. To fix that just open another olly, open packed target in it, binary copy whole PE header and binary paste it insted this one. Now you can dump file with LordPE but there will be some number of unresolved thunks in ImpREC, in my case 16. Trace level 1 will gave false imports so do not relay on it.
4. IAT problem
.rdata section is one that holds import thunks. Take a look there (after you have reached OEP) and you will see that some values are not good:
00404020 FF 7E AB 00 7E 17 E6 77 AF 81 AB 00 A3 81 AB 00 .~..~..w........
00404030 D6 69 AB 00 99 6A AB 00 0C E6 E7 77 95 9B E9 77 .i...j.....w...w
00404040 FC AC E7 77 2F E0 E9 77 E8 E4 E7 77 9C A8 E7 77 ...w/..w...w...w
For example, first value on the abowe snippet is FF7EAB00 which is not value of some import. Second is good 7E17E677. As you can see, first value points to ArmDll in memory. We need to find where IAT is being redirected and prevent that redirection. Restart target in Olly, fix OutputDebugStringA problem and place hardware breakpoint on write in dump on 00404020. HW bp is on DWORD. Now just press F9 (that is after you have stop in kernel on debug string exploit) and you will stop here:
77C42F43 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
77C42F45 JMP DWORD PTR DS:[EDX*4+77C43058] ; MSVCRT.77C43068
77C42F4C MOV EAX,EDI ; Armadill.00404024
77C42F4E MOV EDX,3
77C42F53 SUB ECX,4
77C42F56 JB SHORT MSVCRT.77C42F64
77C42F58 AND EAX,3
77C42F5B ADD ECX,EAX
77C42F5D JMP DWORD PTR DS:[EAX*4+77C42F70]
77C42F64 JMP DWORD PTR DS:[ECX*4+77C43068]
77C42F6B NOP
77C42F6C JMP DWORD PTR DS:[ECX*4+77C42FEC]
...
...
This is first stop and it is not important. Press F9 once again and you will land on good spot:
00AC6979 CMP DWORD PTR DS:[EAX+8],0 <-------------- [5] It just compares does all APIs from list are checked!!!
00AC697D JE SHORT 00AC69C8
00AC697F PUSH 100
00AC6984 LEA EAX,DWORD PTR SS:[EBP-3BF4]
00AC698A PUSH EAX
00AC698B MOV EAX,DWORD PTR SS:[EBP-3AF4]
00AC6991 PUSH DWORD PTR DS:[EAX]
00AC6993 CALL 00ACCF05
00AC6998 ADD ESP,0C
00AC699B LEA EAX,DWORD PTR SS:[EBP-3BF4]
00AC69A1 PUSH EAX
00AC69A2 LEA EAX,DWORD PTR SS:[EBP-3AE4]
00AC69A8 PUSH EAX
00AC69A9 CALL DWORD PTR DS:[ACE384] ; MSVCRT._stricmp <--- [3] Compares API names !!!
00AC69AF POP ECX
00AC69B0 POP ECX
00AC69B1 TEST EAX,EAX
00AC69B3 JNZ SHORT 00AC69C6 <--------------- [4] If API is on the list, it will not jump!!!
00AC69B5 MOV EAX,DWORD PTR SS:[EBP-3AF4]
00AC69BB MOV EAX,DWORD PTR DS:[EAX+8]
00AC69BE MOV DWORD PTR SS:[EBP-32E4],EAX
00AC69C4 JMP SHORT 00AC69C8
00AC69C6 JMP SHORT 00AC6964
00AC69C8 MOV EAX,DWORD PTR SS:[EBP-28A4]
...
...
...
00AC6B64 MOV DWORD PTR DS:[EAX],ECX <--------- [2] But this is the place where value is written!!!
00AC6B66 MOV EAX,DWORD PTR SS:[EBP-24EC] <---- [1] You stopped here!!!
You have landed on place where redirected value has been written, but that is not so interesting and I just removed big chunk of code. Main part is at [5] where armadillo compare names of all API with some that it has on it's own list. If some API is on the list, jump [4] will not be executed and that API will be emulated. This is one of few part which we can change in order to prevent API redirection. Jump at [5] just compares does all API names from iner list are processed. So we need to change jump [4] from JNZ to JMP. But it is too late now because most of imports are already redirected. But remember where that jump is, on my computer it is at 00AC69B3. Write down your value and we gonna try again.
Restart target again in olly and fix Olly exploit. In CPU window select "Go to"->"Expression" and enter address of that jump, for me it is 00AC69B3. Follow it:
00AC69B3 JNZ SHORT 00AC69C6
...
...
As you can see, jump is there. Good! Now change it to JMP. And that's it, place bp on CreateThread and find OEP. Fire up ImpREC and get imports. Click show invalid and cut all invalid thunks. Fix dump, run it and it will work great! That's it ;)
6. Cosmetic surgery
Armadillo code that is added to packed exe is quite big. Packed file itself has 520 kb and my unpacked is now 740 kb. We can use LordPE to reduce size of exe. Open unpacked file in LordPE's PE editor. First change BaseOfCode from 6000 to 1000 and BaseOfData from 66000 to 5000. Then click on sections button. There you will see sections that Armadillo added; .text1, .adata, .data1 and .pdata. Right click on each section and select "wipe section header". Close section table. Click save button to save changes. Now open options in LordPE and for rebuilder, check Dumpfix,Realign file...->hardcore, and validate PE. Now rebuild unpacked file and it size will reduce up to 2% --> 22kb! Not bad ha :)
-
Level : newbie
Brief walkthrough of generic EXE32Pack v1.43 unpacking.
-=Soul12's Unpacking EXE32pack=- v0.1
Target: EXE32Pack v1.43
Generic: yes!
This Packer is a sligthy differnt packer compared to UPX is has a Limited amouny of protection
It Scrambles Imports(poorly) and attemts to hide OEP(again poorly) and tries to detect olly with IsDebuggerPresent ..
(why write about this packer, well knowledge is power..)
tools needed:
1. Olly+Ollydumb(imprec cant locate the imports)
Okay lets start load the file into olly and you see this:
0041500C > $ 3BC0 CMP EAX,EAX
0041500E 74 DB 74 ; CHAR 't'
0041500F 02 DB 02
00415010 81 DB 81
00415011 83 DB 83
00415012 . 55 PUSH EBP
00415013 . 3BC0 CMP EAX,EAX
00415015 . 74 02 JE SHORT exe32pac.00415019
00415017 . 8183 533BC974 >ADD DWORD PTR DS:[EBX+74C93B53],3B56BC01
Press f7 until you pass the push ebp and land on CMP EAX,EAX. if you look the Registers Window
youl notice that ESP has changed, right click on it and choose Follow in dump, Select the first 4 bytes and right click ->breakpoint ->hardware on access -> dword and press f9 you will break here:
00420156 3BE4 CMP ESP,ESP
00420158 74 01 JE SHORT exe32pac.0042015B
0042015A BF FFE0B801 MOV EDI,1B8E0FF
0042015F 0000 ADD BYTE PTR DS:[EAX],AL
00420161 003B ADD BYTE PTR DS:[EBX],BH
00420163 C9 LEAVE
now all you gotta do is press 3x f7 and your at OEP :) you will have to press ctrl+a in order to see it, now dump with ollydumb and youl have a fully working .exe .. the imports are still bit *censored*ed(maby somebody will borther fixing) but program runs and you can disassemble now :)
-
Level : intermediate
======================================
Anticrack Software Protector Pro v1.35 - manually unpacking
======================================
Hi and wellcome to my new tutorial ;) Today we gona talk about ACProtect 1.35 also known as Anticrack Protector or UltraProtect.
Some features of this protector:
- Anti debugging tricks like IsDebuggerPresent and CreateToolhep32Snapshot,
- Imports redirection,
- Stolen and obfuscated OEP,
- Code replacing,
- Embedded protection,
- Runtime encryption and decryption,
- Compression of executable,
- Some more minor things.
In this tutorial we will see how to unpack target, repair IAT, fix stolen OEP and we gona say couple words about code replacing. Our target will be AVIpreview.exe. AVIpreview is some freeware abandoned application which I packed with trial version of ACProtect so you don't think that I spreed cracks or warez trough mine tutorials. I chose this app because it's not so big but it has lot of imports which is better for practice than some small crackme. Tools that you'll need:
- OllyDbg 1.10
- LordPE
- ImpREC
- Hex Editor
1. QUICK ANALYSIS
1.1 Layers
ACProtector code is full of encrypted layers so it can be very annoying to trace trough. When you open target in olly, you are at the entry point here:
00421000 PUSHAD
00421001 CALL AVIPrevi.00421007
00421006 JNZ SHORT AVIPrevi.00420F8B
00421008 LES EAX,FWORD PTR DS:[EBX+EAX*2]
0042100B INC EDI
...
...
Trace little with F7 and you'll see where is end of first decryptor loop:
00421128 JNZ AVIPrevi.0042109E <------------ Jump that throw you back!
0042112E JMP SHORT AVIPrevi.00421131 <------- Place bp here to avoid it.
00421130 JNS SHORT AVIPrevi.00421198
That is example how ALL decryptor loops in this protector looks. Always is some JNZ that throws you back. Below it is usually some JMP or PUSH EAX or some CALL. Never mind for that, you just place bp below our JNZ and press F9 to decrypt. But there is a loot of such decryptor loops and manually is very hard to precede in this way. Because that I wrote small script that will find such jumps and place bp below it, the execute it to the that bp, remove bp and ask you do you want to continue. But we will get to this later.
1.2 Interesting weakness
Altough protector encrypts it's own code, it doesn't encrypt packed file. That is big, BIG, weakness, because if you pack app without compressing and using CRC check, file can be cracked without unpacking. This is very bad from ACProtector side.
1.2 Antidebug stuff
Now, let me show you how does ACProtect hides imports. But first, we must avoid anti-stuff. First, ignore ALL exceptions in Olly, then use some plugin to hide olly from IsDebuggerPresent check, then place in command line (or command bar) "bp CreateToolhelp32Snapshot+1". You will place bp on second instruction of this API because protector checks first instruction for software bp, and it also chechs for hardware breakpoints. Run it and you should break in kernel32 on that API:
77E999A6 PUSH EBP <--------------------- First API instruction.
77E999A7 MOV EBP,ESP <------------------ You are here!!!
77E999A9 SUB ESP,0C
77E999AC PUSH ESI
77E999AD MOV ESI,DWORD PTR SS:[EBP+C]
...
...
...
77E99A15 POP ESI
77E99A16 LEAVE
77E99A17 RETN 8 <---------------------- Return to protector code.
Little explanation: this API create snapshot of all processes, threads and heaps and it's purpose is to determ what program started our packed target. Our target needs to be started by EXPLORER.EXE. If target is started via olly or some other debugger, protector will terminate it and packed target. There is simple trick how to avoid this. I sow this trick in KaGra's tutorial: We will fool protector that there are no processes and rest by paching this API. Do not wory, all changes are temporary in memory so you will not screw your kernel32.dll. Watch this:
77E999A6 PUSH EBP
77E999A7 POP EBP <---------------------- Restoring pushed value from stack.
77E999A8 XOR EAX,EAX <------------------ EAX=0, so pretector think there are nothing to see.
77E999AA RETN 8 <----------------------- Return to protector code.
77E999AD MOV ESI,DWORD PTR SS:[EBP+C]
77E999B0 TEST ESI,ESI
77E999B2 JE kernel32.77EA800A
...
...
...
77E99A15 POP ESI
77E99A16 LEAVE
77E99A17 RETN 8
Now place memory breakpoint on code section
00401000 00014000 AVIPrevi .text code
and run target. You will first stop here because stolen bytes:
00412D15 RETN
00412D16 JMP DWORD PTR DS:[415570] ; MSVCRT._except_handler3
00412D1C JMP DWORD PTR DS:[41556C] ; MSVCRT._controlfp
Press F9 once more time and you'll get that ACProtect reminder nag. Click OK on it and you will now land on false OEP:
00412AEB CMP DWORD PTR DS:[41B020],EBX
00412AF1 JNZ SHORT AVIPrevi.00412AFF
00412AF3 PUSH AVIPrevi.00412D12
00412AF8 CALL DWORD PTR DS:[415584]
00412AFE POP ECX
00412AFF CALL AVIPrevi.00412D00
...
...
Above is some junk code to confuse you. Protector has done some import redirection to it's own section (perplex one). Let me show you, go to memory window and double click on last section - perplex. In dump window select disassemble view and check this:
00421044 PUSH 7F62A025
00421049 XOR DWORD PTR SS:[ESP],8850BE0
00421050 RETN
00421051 PUSH 476408A8
00421056 XOR DWORD PTR SS:[ESP],3083BFF8
0042105D RETN
...
...
004212F5 PUSH 7757B958
004212FA XOR DWORD PTR SS:[ESP],83EA58
00421301 RETN
00421302 PUSH 77521539
00421307 XOR DWORD PTR SS:[ESP],84C3D8
0042130E RETN
These are hidden imports. Value after push will be XOR-ed with second one and than RETN will throw us at that address whic is acctually some API address. This is not hard to fix but there is smarter way to do this. Now we gona unpack our target.
2. FIXING IMPORTS AND DUMPING
ACProtector has some more lacks; it doesn't encrypt import section and it doesn't change it's name. Take a look in the memory map window, .rdata is section which should hold imports. Place memory bp on access on it, hide olly from IsDebuggerPresent check and place CreateToolhelp32Snapshot bp like first time. Run target and you will break exactly at the procedure where protector redirects imports, from 435856 to 435AD7. I will not paste here whole code bacuse is't big, I will show you only the most important parts.
First it uses GetProcAddress to get API. Then it first ckecking is that API MessageBox or RegisterHotKey. Those two API's are important for protector and they are always redirected. I don't know why they are important, but we will prevent that redirecting:
00435986 CMP EAX,DWORD PTR SS:[EBP+41E2D8] ; USER32.MessageBoxA
0043598C JE SHORT AVIPrevi.004359AE <-------------------------------- NOP this jump!!!
0043598E NOP
0043598F NOP
00435990 NOP
00435991 NOP
00435992 CMP EAX,DWORD PTR SS:[EBP+4101AC] ; USER32.RegisterHotKey
00435998 JE SHORT AVIPrevi.004359A3 <-------------------------------- NOP this jump!!!
Simply NOP that two jumps. Next, protector checks which API needs to be redirected. Read comments:
004359C2 JE SHORT AVIPrevi.004359D9
004359C4 NOP
004359C5 NOP
004359C6 NOP
004359C7 NOP
004359C8 CMP DWORD PTR SS:[EBP+4023FE],ESI
004359CE JE SHORT AVIPrevi.004359D9
004359D0 NOP
004359D1 NOP
004359D2 NOP
004359D3 NOP
004359D4 JMP SHORT AVIPrevi.00435A39 <------ Good jump!!!
004359D6 NOP
004359D7 NOP
004359D8 NOP
004359D9 CMP BYTE PTR SS:[EBP+4159EB],0
004359E0 JE SHORT AVIPrevi.00435A39 <-------- Good jump!!!
004359E2 NOP
004359E3 NOP
004359E4 NOP
004359E5 NOP
004359E6 JMP SHORT AVIPrevi.004359EF <------- Bad jump!!!
004359E8 NOP
004359E9 NOP
004359EA NOP
004359EB ADD DWORD PTR DS:[EAX],EAX
004359ED ADD BYTE PTR DS:[EAX],AL
004359EF MOV ESI,DWORD PTR SS:[EBP+40FCF3]
004359F5 ADD ESI,0D
004359F8 SUB ESI,AVIPrevi.00401BEA
004359FE SUB ESI,EBP
00435A00 CMP ESI,0
00435A03 JG SHORT AVIPrevi.00435A39 <-------- Good jump!!!
This part redirect (or not) imports:
00435A16 MOV EBX,EAX
00435A18 POP EAX
00435A19 XOR EAX,EBX
00435A1B MOV BYTE PTR DS:[ESI],68
00435A1E MOV DWORD PTR DS:[ESI+1],EAX
00435A21 MOV DWORD PTR DS:[ESI+5],243481
00435A28 MOV DWORD PTR DS:[ESI+8],EBX
00435A2B MOV BYTE PTR DS:[ESI+C],0C3
00435A2F POP EBX
00435A30 MOV EAX,ESI
00435A32 ADD DWORD PTR SS:[EBP+40FCF3],0D
00435A39 POP ESI <-------------------------- Good place to jump, import OK!!!
00435A3A PUSHAD
00435A3B MOV EDX,EAX
00435A3D SUB EDI,DWORD PTR SS:[EBP+40FC2E]
00435A43 MOV EAX,EDI
00435A45 MOV ECX,101
00435A4A LEA EDI,DWORD PTR SS:[EBP+40F0D3]
00435A50 REPNE SCAS DWORD PTR ES:[EDI]
00435A52 OR ECX,ECX
00435A54 JE SHORT AVIPrevi.00435A69
That jump which I called "Bad jump" will trhow you at the place where import will be redirected. To fix that and prevent redirection, you need change that jump to jump where good jumps leads. So change:
004359E6 JMP SHORT AVIPrevi.004359EF
to this:
004359E6 JMP SHORT AVIPrevi.00435A39
Now scroll down and to the end of procedure and place bp there:
00435AB1 CMP ECX,0 <------------------------- Place bp here!!!
00435AB4 JL SHORT AVIPrevi.00435AD0
00435AB6 MOV EAX,DWORD PTR DS:[ESI+ECX*4]
00435AB9 MOV EBX,DWORD PTR DS:[ESI+ECX*4+4]
00435ABD SUB EAX,EBX
00435ABF ROR EAX,1B
00435AC2 XOR EAX,EDX
00435AC4 ADD EDX,668C0E89
00435ACA MOV DWORD PTR DS:[ESI+ECX*4],EAX
00435ACD DEC ECX
00435ACE JMP SHORT AVIPrevi.00435AB1
00435AD0 POPAD
00435AD1 POPAD
00435AD2 CALL AVIPrevi.004334BC
00435AD7 RETN
This procedure at the end will just destroy import redirection procedure when is done. Run our target to decrypt all imports and you'll stop on the abowe bp. You have just fixed imports! Bravo ;) Now you need break on the code section where false OEP is and dump file. I eaxplained how you will avoid anti-debug stuff already, after that use lordPE for dumping file. Fix imports with ImpREC where OEP is 00412AEB=00012AEB, our false OEP. There will not be neither one invalid import :) If you have just one or two invalid, then you have done something wrong.
Run dumped_.exe and file will just close or crush. That is expected since we have stolen OEP issue. We gona fix that now.
3. STOLEN OEP AND FIXNG DUMP
ACProtect has stole lot of instructiones from OEP and mix them with some junk. It is hard to clear false code and restore stolen bytes exactly, but we can fix this little different and easier. We gona just copy-paste all that code to our dumped file , not taking worry what is junk and what is not. First we need to prepare dumped file.
Open your favorite hex editor and add 1000(hex) bytes to the end of dumped_.exe (file that is fixed with ImpREC) and save changes. It could be less than 1000 but who cares. Then load that file in LordPE's PE editor, click on sections, select last one (.mackt) and click to edit section header. There change VirtualSize from 1000 to 2000 and RawSize from 1000 to 2000 too. Save changes. We have done that because there we will paste our code so wee need bigger last section. One more thing, use LordPE's rebuilder to rebuild dumped file so you can open it in olly. Bu be sure that in Rebuilder options is checked only Validate PE.
Now open packed file and get to the CreateToolhelp32Snapshot check. Fix it, and return to protector code where you'll see some api calls. Ignore that and scroll down to end of this procedure, place bp there:
0043200F JMP SHORT AVIPrevi.00431FF2
00432011 POPAD
00432012 POPAD
00432013 RETN <--------------------------- Place bp here!!!
Run and you'll break on it, remove bp, trace untill you see next one:
004334A1 JMP SHORT AVIPrevi.00433484
004334A3 POPAD
004334A4 POPAD
004334A5 RETN
Do the same and execute last RETN with F7 and you should be here:
00437005 JMP AVIPrevi.0043700D
0043700A ADC EBX,ECX
0043700C INC EBP
0043700D MOV EAX,3D
00437012 CALL AVIPrevi.00437018
00437017 JGE SHORT AVIPrevi.00436F9C
00437019 ADD AL,24
That is just beggining of decryptor layers. You need to decrypt them and find stolen bytes. As I sad on the begining, there are large number of decryption layers and it's hard to trace manualy. Now is time to use my small scrip that will make this easier. Run "ACProtector - pass layers.txt" script and that script will ask you do you want to decrypt next layer. Keep clicking "Yes" untill you get here:
00438652 PUSH EAX
00438653 CALL AVIPrevi.00438659
00438658 JS SHORT AVIPrevi.004385DD
0043865A LES EAX,FWORD PTR DS:[EAX+EBX*2]
0043865D CALL AVIPrevi.004325D9
00438662 JS AVIPrevi.0043866A
00438668 TEST EBP,ESI
0043866A POPAD
0043866B PUSH EAX
0043866C MOV EAX,AVIPrevi.00421E15
00438671 MOV DWORD PTR DS:[EAX],ESI
00438673 POP EAX
00438674 PUSH DWORD PTR DS:[421E15]
0043867A PUSH EAX
0043867B MOV DWORD PTR SS:[ESP],EDX
0043867E PUSH EDI
0043867F MOV EDI,AVIPrevi.00421C8D
00438684 MOV EDX,EDI
00438686 POP EDI
00438687 PUSH ECX
00438688 MOV ECX,EDX
0043868A MOV ESI,ECX
0043868C POP ECX
0043868D POP DWORD PTR DS:[421D01]
00438693 MOV EDX,DWORD PTR DS:[421D01]
00438699 MOV DWORD PTR DS:[ESI],EBP
0043869B POP DWORD PTR DS:[421DB9]
004386A1 PUSH EBX
004386A2 MOV EBX,AVIPrevi.00421DB9
004386A7 MOV ESI,DWORD PTR DS:[EBX]
004386A9 POP EBX
004386AA PUSH DWORD PTR DS:[421C8D]
004386B0 MOV DWORD PTR DS:[421D7D],ESP
004386B6 NOP
004386B7 NOP
004386B8 PUSHAD
This code between POPAD and PUSHAD is stolen OEP's code with some obfuscated code. Trace to 0043866B PUSH EAX, then select all that code from 0043866B 4386B8 and binary copy it. Open another instance of olly and open in it our dumped file and go to 441000 where you can see that there are lot of empty space. That is part of our last section which we expanded. Now, binary paste there all code that you have copied from first file. You will do that for all code that we found. Now use again our script untill you get at the next part and the binary copy-paste that code too right after first pasted part. Pasted code must continue on each other. You will find next stolen code at these addresses:
00438F8A POPAD
004398BA POPAD
0043A1DA POPAD
0043AB30 POPAD
0043B4BE POPAD
0043BE70 POPAD
0043C7A7 POPAD
0043D090 POPAD
0043D9EB POPAD
After this last one, you will get ACProtect nag window. Click OK on it and script will normally stop on next decrypted place and ask you do you want to continue. Click "No" because you should be at the very end. There should be some CALL's and this part below:
0043DEB0 LOOPD SHORT AVIPrevi.0043DEAA
0043DEB2 POPAD <---------------------------- Place bp here!
0043DEB3 JMP SHORT AVIPrevi.0043DEB6
Place bp, run and execute that jump and you will find yourself on jump that throws you at the false OEP:
0043DEB6 JMP DWORD PTR DS:[43DEF8] ; AVIPrevi.00412AEB
This is not much important for us, but you can also binary copy that jump too and paste it to dumped file. Then save all that changes to dumped file by selecting all that code and selecting "Copy to executable -> selection". Shit! I got message "Unable to locate date in the executable file.". Damn! That means that we need more space in our dumped file, I didn't added enough bytes or I started to paste bytes to far. But don't panic folks, we can fast fix this without doing all this job again. Just copy dumped_.exe in the came folder so you have now "Copy of dumped_.exe" there. Don't close your two olly so you do not lose all your job. Open copy of dumped in hex editor and add 2000 bytes more. Then in LordPE set VS and RS to 4000. Use PE rebuilder again. Then all code that you have pasted to dumped_.exe from 441000 to 44130A, binary copy it and paste it to "Copy of dumped_.exe" from the same address (altough addres isn't important). Now save changes, yes it works, and close that olly. Stolen code is retrived, we just need to set new OEP. Open dumped file in LordPE's PE editor again and for EntryPoint enter 41000, then save changes. Now, the moment of truth! Run file.... YES!!! It works! Open some avi file and program will work OK :) Congratulation, you have just unpacked ACProtector Pro 1.35.
4. CODE REPLACING
I will just sad couple words about code replacing. ACProtect substitute some simple instructions like MOV DWORD [xxxx],yyyy to some CALL zzzz which calls protector code. There that stolen instruction will be executed after lot of junk code. The best way to repair this would be to not touch nothing, just leave it there. Our AVIpreview had lot of this instructions which I didn't touch and our unpacked file works fine. This kind of code replacing needs more studing.
5. FINAL WORDS
That was all folks. There is some more things that needs to be discussed about ACPtotect , but maybe in next tutorial. ACProtect is not bad protector and it's not for begginers.
-
Level : beginner
Using gdb to retrieve a deadlisting of a linux binary, comment the algoritm, and writing a keygen in C++
First I fired up gdb on the program, set the disassembly type to intel instead of the default att and turned on asm-demangle. I then set a breakpoint on main and disassembled the program and got the following output.
PS - I did not do any stepping through of the code through gdb's debugger, I only used it to disassemble the code and then manually analyzed it.
--------------------------------------------------------------------------------
linux2[1]% gdb small
GNU gdb Red Hat Linux (6.1post-1.20040607.52rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) set disassembly-flavor intel
(gdb) set print asm-demangle on
(gdb) break *main
Breakpoint 1 at 0x80487bc
(gdb) run
Starting program: /afs/bla/bla/home/small
Breakpoint 1, 0x080487bc in main ()
(gdb) disassemble
Dump of assembler code for function main:
0x080487bc : push ebp
0x080487bd : mov ebp,esp
0x080487bf : sub esp,0xe8
0x080487c5 : and esp,0xfffffff0
0x080487c8 : mov eax,0x0
0x080487cd : sub esp,eax
0x080487cf : sub esp,0x8
0x080487d2 : push 0x80486d8
0x080487d7 : sub esp,0xc
0x080487da : push 0x8048ae0
0x080487df : push 0x8049db8
0x080487e4 : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x080487e9 : add esp,0x14
0x080487ec : push eax
0x080487ed : call 0x8048658
0x080487f2 : add esp,0x10
0x080487f5 : sub esp,0x8
0x080487f8 : push 0x8048b20
0x080487fd : push 0x8049db8
0x08048802 : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x08048807 : add esp,0x10
0x0804880a : sub esp,0x8
0x0804880d : lea eax,[ebp-72]
0x08048810 : push eax
0x08048811 : push 0x8049e48
0x08048816 : call 0x80486c8 >& std::operator>> >(std::basic_istream >&, char*)>
0x0804881b : add esp,0x10
0x0804881e : sub esp,0x8
0x08048821 : push 0x8048b60
0x08048826 : push 0x8049db8
0x0804882b : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x08048830 : add esp,0x10
0x08048833 : sub esp,0x8
0x08048836 : lea eax,[ebp-136]
0x0804883c : push eax
0x0804883d : push 0x8049e48
0x08048842 : call 0x80486c8 >& std::operator>> >(std::basic_istream >&, char*)>
0x08048847 : add esp,0x10
0x0804884a : mov DWORD PTR [ebp-204],0x0
0x08048854 : lea eax,[ebp-72]
0x08048857 : add eax,DWORD PTR [ebp-204]
0x0804885d : cmp BYTE PTR [eax],0x0
0x08048860 : jne 0x8048864
0x08048862 : jmp 0x80488a6
0x08048864 : lea eax,[ebp-200]
0x0804886a : mov ecx,eax
0x0804886c : add ecx,DWORD PTR [ebp-204]
0x08048872 : lea eax,[ebp-72]
0x08048875 : add eax,DWORD PTR [ebp-204]
0x0804887b : mov al,BYTE PTR [eax]
0x0804887d : movsx dx,al
0x08048881 : mov DWORD PTR [ebp-218],0xa
0x0804888a : mov eax,edx
0x0804888c : cwd
0x0804888e : data16
---Type to continue, or q to quit---
0x0804888f : idiv DWORD PTR [ebp-218]
0x08048895 : mov al,dl
0x08048897 : add eax,0x30
0x0804889a : mov BYTE PTR [ecx],al
0x0804889c : lea eax,[ebp-204]
0x080488a2 : inc DWORD PTR [eax]
0x080488a4 : jmp 0x8048854
0x080488a6 : lea eax,[ebp-200]
0x080488ac : add eax,DWORD PTR [ebp-204]
0x080488b2 : mov BYTE PTR [eax],0x0
0x080488b5 : lea eax,[ebp-136]
0x080488bb : lea edx,[ebp-200]
0x080488c1 : sub esp,0x8
0x080488c4 : push eax
0x080488c5 : push edx
0x080488c6 : call 0x8048668
0x080488cb : add esp,0x10
0x080488ce : test eax,eax
0x080488d0 : jne 0x80488fa
0x080488d2 : sub esp,0x8
0x080488d5 : push 0x80486d8
0x080488da : sub esp,0xc
0x080488dd : push 0x8048b84
0x080488e2 : push 0x8049db8
0x080488e7 : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x080488ec : add esp,0x14
0x080488ef : push eax
0x080488f0 : call 0x8048658
0x080488f5 : add esp,0x10
0x080488f8 : jmp 0x8048920
0x080488fa : sub esp,0x8
0x080488fd : push 0x80486d8
0x08048902 : sub esp,0xc
0x08048905 : push 0x8048b90
0x0804890a : push 0x8049db8
0x0804890f : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x08048914 : add esp,0x14
0x08048917 : push eax
0x08048918 : call 0x8048658
0x0804891d : add esp,0x10
0x08048920 : mov eax,0x0
0x08048925 : leave
0x08048926 : ret
0x08048927 : nop
End of assembler dump.
(gdb) quit
The program is running. Exit anyway? (y or n) y
linux2[2]%
After this I began going through the code manually and adding my own C Style comments as seen below.
Dump of assembler code for function main:
// function prolog
0x080487bc : push ebp
0x080487bd : mov ebp,esp
// allocate some memory on the stack (232 bits or 29 bytes)
0x080487bf : sub esp,0xe8
// rounds the last digit in esp to 0..... purpose being?
0x080487c5 : and esp,0xfffffff0
// set eax to 0
0x080487c8 : mov eax,0x0
// esp -= eax (huh, we just set eax to 0)
0x080487cd : sub esp,eax
// esp -= 8 //allocate another byte)
0x080487cf : sub esp,0x8
0x080487d2 : push 0x80486d8
0x080487d7 : sub esp,0xc
// print out..... "-> Small crackme for Stingduk : push 0x8048ae0
0x080487df : push 0x8049db8
0x080487e4 : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
// adjust stack pointer from making the c call....
0x080487e9 : add esp,0x14
// print out..... "Give me your name (max 50 chars):"
0x080487ec : push eax
0x080487ed : call 0x8048658
0x080487f2 : add esp,0x10
0x080487f5 : sub esp,0x8
0x080487f8 : push 0x8048b20
0x080487fd : push 0x8049db8
0x08048802 : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
// adjust stackframe
0x08048807 : add esp,0x10
0x0804880a : sub esp,0x8
// accepts the name from cin ebp-72 (72 to 22)
// load the address of the local mem storage we created
0x0804880d : lea eax,[ebp-72]
0x08048810 : push eax
0x08048811 : push 0x8049e48
0x08048816 : call 0x80486c8 >& std::operator>> >(std::basic_istream >&, char*)>
0x0804881b : add esp,0x10
0x0804881e : sub esp,0x8
// print out.......... Pass me the serial (max 50 chars):
0x08048821 : push 0x8048b60
0x08048826 : push 0x8049db8
0x0804882b : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x08048830 : add esp,0x10
0x08048833 : sub esp,0x8
// accepts the userSerial from cin ebp-136 (136 to 86)
// load the address of the local mem storage we created
0x08048836 : lea eax,[ebp-136]
// push the local address on stack
0x0804883c : push eax
0x0804883d : push 0x8049e48
0x08048842 : call 0x80486c8 >& std::operator>> >(std::basic_istream >&, char*)>
// adjust stackframe
0x08048847 : add esp,0x10
// fill a dword (4 bytes) 204 to 200
// counter = 0;
0x0804884a : mov DWORD PTR [ebp-204],0x0
ebp-72 to -22 holds Name
ebp-136 to -86 holds userSerial
//------Begin Loop--------/
// load the address of Name from local memory
0x08048854 : lea eax,[ebp-72]
// add whats in ebp-204 to eax... initially 0, incremented at the end of each loop.
0x08048857 : add eax,DWORD PTR [ebp-204]
// compare 1 byte at a time, contents of eax and 0
// check for null char termination
0x0804885d : cmp BYTE PTR [eax],0x0
// if (!null char)
// continue;
// else
// break;
0x08048860 : jne 0x8048864
0x08048862 : jmp 0x80488a6
// load address of ebp-200 to eax (calculatedSerial)
0x08048864 : lea eax,[ebp-200]
// using ecx as a temp var/reg
// ecx = eax
// ecx = calculatedSerial;
0x0804886a : mov ecx,eax
// ecx += contents of ebp-204 (counter/offset variable)
// ecx = &calculatedSerial[counter];
0x0804886c : add ecx,DWORD PTR [ebp-204]
// get first char of name
// eax = address of [ebp-72]
// eax = name;
0x08048872 : lea eax,[ebp-72]
// 204 to 200
// eax += contents of 4 bytes from ebp-204 (counter/offset variable)
// eax = &name[counter];
0x08048875 : add eax,DWORD PTR [ebp-204]
// al = name[counter];
// al = contents of eax
0x0804887b : mov al,BYTE PTR [eax]
// dx = al, converting a single byte al to double byte dx
0x0804887d : movsx dx,al
// move 0xa or 10 into contents of ebp-218 TO -214
// divideBy = 10;
0x08048881 : mov DWORD PTR [ebp-218],0xa
// move edx into eax..... what about high bits of edx?
0x0804888a : mov eax,edx
// sign extend ax to dx:ax
// word to dword, bit 15 of ax will be copied into all of dx (all 1's or 0's)
0x0804888c : cwd
0x0804888e : data16 // this ensures its a 16 bit operation even tho dword i think..
// DX = just the sign, AX = *name[counter];
// divide DX:AX by 10 and store result in AX and remainder in DX
0x0804888f : idiv DWORD PTR [ebp-218] // signed dividing by 10
// DX = ASCII value of currChar % 10
// AX = ASCII value of currChar / 10
// take low byte from remainder and store in al.... previously division
// al = dl
0x08048895 : mov al,dl
// add 30h to eax... makes sense 48 is 0 in ascii., mod 10 system will return us in 0 to 9
// range of possible ints for eax is 48 to 57, they will all be ASCII 0's to 1's
0x08048897 : add eax,0x30
// *calculatedSerial[counter] = al;
// *ecx = al (low byte is going into mem contents of ecx, remainder of previous division % 10...)
0x0804889a : mov BYTE PTR [ecx],al
// eax = address of ebp-204 or counter
0x0804889c : lea eax,[ebp-204]
// (*eax)++ or counter++ contained at ebp-204 to 200
0x080488a2 : inc DWORD PTR [eax]
0x080488a4 : jmp 0x8048854
//-------END LOOP------/
// eax = address of ebp-200 (calculatedSerial)
0x080488a6 : lea eax,[ebp-200]
// (add the counter to calculatedSerial store in eax)
// eax += contents of 4 bytes of ebp-204 to 200
0x080488ac : add eax,DWORD PTR [ebp-204]
// null delimit the calculatedSerial
// contents of eax = 0
0x080488b2 : mov BYTE PTR [eax],0x0
// load address of ebp-136 (User supplied serial) into eax
0x080488b5 : lea eax,[ebp-136]
// load address of ebp-200 (calculatedSerial)
0x080488bb : lea edx,[ebp-200]
// adjust stack pointer
0x080488c1 : sub esp,0x8
// push &userSerial and &calculatedSerial onto stack and compare the two with strcmp
0x080488c4 : push eax
0x080488c5 : push edx
0x080488c6 : call 0x8048668
// adjust stack frame
0x080488cb : add esp,0x10
// test to see what strcmp returns
0x080488ce : test eax,eax
// jump to main+318 if not equal (failed check)
0x080488d0 : jne 0x80488fa
// adjust stack frame
0x080488d2 : sub esp,0x8
// print out cracked message.... "Great work!"
0x080488d5 : push 0x80486d8
0x080488da : sub esp,0xc
0x080488dd : push 0x8048b84
0x080488e2 : push 0x8049db8
0x080488e7 : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x080488ec : add esp,0x14
0x080488ef : push eax
0x080488f0 : call 0x8048658
0x080488f5 : add esp,0x10
// must jump to very end of program regardless to end the program.
0x080488f8 : jmp 0x8048920
// adjust stack frame
0x080488fa : sub esp,0x8
// print out error message..... "No luck here mate :("
0x080488fd : push 0x80486d8
0x08048902 : sub esp,0xc
0x08048905 : push 0x8048b90
0x0804890a : push 0x8049db8
0x0804890f : call 0x8048698 >& std::operator >(std::basic_ostream >&, char const*)>
0x08048914 : add esp,0x14
0x08048917 : push eax
0x08048918 : call 0x8048658
0x0804891d : add esp,0x10
// put 0 in eax to exit the program
0x08048920 : mov eax,0x0
// destroy stack frame and buhbye
0x08048925 : leave
0x08048926 : ret
0x08048927 : nop
End of assembler dump.
--------------------------------------------------------------------------------
So basically the program loops through each char of the name, mods the ASCII's decimal value by 10 and then adds 48 to that remainder.
A mod system of 10 will always bring you back to the numbers 0 to 9 and then adding 48 will always bring you to the ASCII values 0 to 9.
My first test was to check my handle to see if I could get 'aa' to work.
a = 97 in ASCII decimal 97 % 10 = 7 7 + 48 = 55 55 in ASCII decimal = 7 so the key should be 77
--------------------------------------------------------------------------------
linux2[28]% ./small
-> Small crackme for Stingduk <-
Give me your name (max 50 chars): aa
Pass me the serial (max 50 chars): 77
Great work!
And as we can see it works.
Now to write a Keygen.....
//------------------------
// keygen.cpp
// tested with g++ 3.2.3
//------------------------
#include <iostream>
#include <string>
using namespace std;
int main()
{
string name;
cout << "Please enter a name: ";
cin >> name;
cout << "Your key is: ";
for (int i = 0; i < name.size(); i++)
cout << (int) name[i] % 10;
cout << endl;
return 0;
}
linux2[32]% g++ keygen.cpp -o keygen
linux2[33]% ./keygen
Please enter a name: Koffee
Your key is: 512211
linux2[34]% ./keygen
Please enter a name: BiWReversing
Your key is: 657218145503
linux2[35]% ./keygen
Please enter a name: somenamehere
Your key is: 519107914141
linux2[36]% ./small
-> Small crackme for Stingduk <-
Give me your name (max 50 chars): Koffee
Pass me the serial (max 50 chars): 512211
Great work!
linux2[37]% ./small
-> Small crackme for Stingduk <-
Give me your name (max 50 chars): BiWReversing
Pass me the serial (max 50 chars): 657218145503
Great work!
linux2[38]% ./small
-> Small crackme for Stingduk <-
Give me your name (max 50 chars): intentionalFail
Pass me the serial (max 50 chars): 1421532523532
No luck here mate :(
linux2[39]%
-
Krypton 0.5 - (half)manually unpacking
--------------------------------------------------------------------------------
This tutorial discuss about Krypton 0.5 protection features and their improvements from 0.3 version. Krypton 0.5 is not bad protector but it has one big lack, it is very unstable and lot of packed files will just crash. Let's go ride into new adventure ;)
1. Indruduction
--------------------------------------------------------------------------------
Hi and welcome to new tutorial! We will walk trough Krypton 0.5 protection features to see what is new and what is old comparing to previous 0.3 version (don't know where is 0.4). Like in the first tutorial, approach will be the same. We will try to fix dumped file after reaching OEP and OllyScript plugin will help me alot in that. This tutorial relies on last one so it is good thing to read that tutorial first.
You will need some tools and stuff for work:
- Target and utilities , here [bin]
- OllyDbg 1.10 (OllyScript & OllyDump plugins)
- ImpREC
- LordPE
- some hex editor
So what Krypton 0.5 have and what do not have? I found next:
- two possible jumps to OEP;
- obfuscation which is simmilar as in 0.3 version;
- breakpoint checks on API functions;
- API obfuscation , same as in the 0.3 version;
- API code execution , same as in the 0.3 version;
- code encryption I , same as in the 0.3 version;
- code substitution II, III, IV , new feature;
- code on-the-fly encryption/decryption , new feature;
- threads that doing all this (not too sure about this);
- it doesn't have CRC checks;
- it doesn't have anti-debug checks.
Target is Crud's CRACKME3.EXE which I packed with all options in Krypton (actually, one is missing but I will say few words about that at the end of tutorial).
---->
-
2. How to find OEP
--------------------------------------------------------------------------------
I was experimenting with memory and hardware breakpoints and I found that like in 0.3 version, here we also have two possible jumps to OEP. Which one will be used depends of packing options. Krypton allocates one memory block in which it unpacks its own loader. Loader unpacks packed file and then it jump to OEP. So you need to stop somewhere in loader code and you can done this like we did in 0.3 version, using VirtualAlloc API and memory breakpoint on one byte in allocated block (check first tutorial for details). Then you need to find possible OEP jumps. As I sad, there are two possible jumps to OEP. One is via JMP EDX where EDX=OEP, second is via PUSH-RETN combination. Since code is obfuscated you won't see those opcodes so I will give you bynary signature of them:
a) First one is executed if file is packed with minimal options. Search for this binary string in loader code:
8B9513784400
and you will find this code:
003BEAE6 MOV EDX,DWORD PTR SS:[EBP+447813] <--- DWORD is OEP address (when you break here)!
003BEAEC JMP SHORT 003BEB2F
003BEAEE FILD QWORD PTR DS:[ECX+4E]
003BEAF1 POP EAX
003BEAF2 FISTP WORD PTR DS:[ECX+71]
003BEAF5 LOCK JMP SHORT 003BEAF9
003BEAF8 FBSTP TBYTE PTR DS:[EAX-15]
003BEAFB FIST WORD PTR DS:[ECX-15]
003BEAFE HLT
003BEAFF POP ESI
003BEB00 XCHG EAX,EBX
003BEB01 RETF 4B91
003BEB04 MOV BL,0EB
003BEB07 HLT
003BEB08 BOUND EAX,QWORD PTR DS:[EAX-3B]
003BEB0B SUB EBX,EDI
003BEB0D STC
003BEB0E SBB AL,0D3
003BEB10 INC ECX
003BEB11 TEST AL,6A
003BEB13 JMP EDX <----------------------------- And here it jumps to OEP!
So just place bp on this last JMP EDX. But you don't know will this jump will be used, so you need to place bp on second possible jump
b) For second jump, search this binary string:
C3EB1EDF694E58DF5972F5EB01DF73F0DF599C83C1E79DFFE1 EB0D51E8F0FFFFFFE801000000
and you will land here:
003C3958 RETN <---------------------- Second possible jump to OEP! Place bp here.
003C3959 JMP SHORT 003C3979
003C395B FILD QWORD PTR DS:[ECX+4E]
003C395E POP EAX
003C395F FISTP WORD PTR DS:[ECX+72]
003C3962 CMC
003C3963 JMP SHORT 003C3966
003C3965 FBSTP TBYTE PTR DS:[EBX-10]
003C3968 FISTP WORD PTR DS:[ECX-64]
003C396B ADD ECX,-19
003C396E POPFD
003C396F JMP ECX
003C3971 JMP SHORT 003C3980
003C3973 PUSH ECX
003C3974 CALL 003C3969
003C3979 CALL 003C397F
003C397E JMP CA29BAE0
Note that Olly doesn't display correctly code because obfuscation so you will at first see this as a first line:
003C3957 E9 C3EB1EDF JMP DF5B251F
You see, C3=RETN is second byte so you just hit Ctrl+G and in "expression to follow" enter addres that point to C3 byte (3c3958 in my case).
When you had finish with bp's, hit F9 and you will break on one of these two bp's and that's it ;) OEP found. Now when you reached OEP we will see how Krypton interfear with packed file. Btw, for fast finding OEP you can use script in 9. chapter of this tutorial.
-
3. IAT redirection and obfuscation
--------------------------------------------------------------------------------
There is nothing new with IAT comparing to 0.3 version. We have exactly the same obfuscation:
0037wwww ADD DWORD[0037uuuu],CONSTANT
0037wwww MOV EAX,DWORD[00370000]
0037wwww SUB DWORD[0037uuuu],CONSTANT
0037wwww JMP EAX
0037uuuu SOME_4_BYTES
0037wwww XOR DWORD[0037uuuu],CONSTANT
0037wwww MOV EAX,DWORD[00370000]
0037wwww XOR DWORD[0037uuuu],CONSTANT
0037wwww JMP EAX
0037uuuu SOME_4_BYTES
And we have indentical API execution but just with different parameters and obfuscation is different. Check tutorial about 0.3 version because there is no need to write all over again the same thing. Two scripts for repairing imports, you can find in 9. chapter.
-
4. Code decryption/encryption I
--------------------------------------------------------------------------------
This is also same feature as in the 0.3 version where MOV XXX,DWORD[yyyyyyyy] or PUSH XXX,DWORD[yyyyyyyy] is substituded with CALL DWORD[zzzzzzzz]. Again, no need to repeating so read first tutorial and use script in 9. chapter. You need to edit it ofcourse.
-
5. Code substitution II
--------------------------------------------------------------------------------
This is new feature but also easy to fix using OllyScript. Check this in our CRACKME3.EXE:
0040100C . 50 PUSH EAX
0040100D . 53 PUSH EBX
0040100E . FF15 0000D800 CALL DWORD PTR DS:[D80000]
00401014 . 5B POP EBX
00401015 . 58 POP EAX
Code from 40100C to 401015 is injected instead original line below (I sow it in original exe before packing):
0040100C C705 F9204000 00000000 MOV DWORD PTR DS:[4020F9],0
But this line is executed within that D8???? block. Check comments below. First we enter in call, then;
00D80004 PUSH EBP
00D80005 PUSH EAX
00D80006 PUSH EBX
00D80007 PUSHFD
00D80008 CALL 00D8000D
00D8000D POP EBP
00D8000E SUB EBP,41B79A <----------------------- Calculating reference value.
00D80014 LEA EAX,DWORD PTR SS:[EBP+41B7CD] <- That value+constant gives pointer to correct value in internal tab.
00D8001A MOV EBX,DWORD PTR DS:[EAX] <-------- That encrypted value is taken.
00D8001C MOV EAX,DWORD PTR DS:[EAX+4] <------ It takes second value.
00D8001F XOR EBX,EAX <-------------------------- Decrypts first one with it, and that is 4020F9.
00D80021 ADD EAX,EBX <-------------------------- And EAX is 0.
00D80023 MOV DWORD PTR DS:[EBX],EAX <-------- So we have MOV DWORD[4020F9],0 here! Rest is not important for us.
00D80025 AND EAX,FFFF0000
00D8002A LEA EBX,DWORD PTR SS:[EBP+41B7C9]
00D80030 MOV EBX,DWORD PTR DS:[EBX]
00D80032 SHR EAX,18
00D80035 CMP EBX,EAX
00D80037 POPFD
00D80038 POP EBX
00D80039 POP EAX
00D8003A POP EBP
00D8003B RETN
Knowing this, I made new script "Krypton 0.5 - code pattern II" that will just emulate this and fix our substituded opcodes. You need to edit this script too.
-
6. Code substitution III
--------------------------------------------------------------------------------
This is new feature also and it substitute this kind of opcodes:
00401066 833D A0214000 12 CMP DWORD PTR DS:[4021A0],12
With this:
00401066 FF15 0000E000 CALL DWORD PTR DS:[E00000]
0040106C 90 NOP
And within that call, it will emulate this opcode execution. Inside there are little more code so I will show just important lines:
00E0000D POP EBP
00E0000E SUB EBP,41B7E6
00E00014 LEA EAX,DWORD PTR SS:[EBP+41B7F7] <--- Again is calculated reference value/pointer.
00E00031 MOV EBX,DWORD PTR DS:[EAX] <---------- Take encrypted value.
00E00039 MOV EAX,DWORD PTR DS:[EAX+4] <-------- Take second value.
00E00041 XOR EBX,EAX <---------------------------- Decrypt it. EBX will be 4021A0.
00E0004B ADD EAX,EBX <---------------------------- In EAX it must get that 12 byte so calculate...
00E00055 AND EAX,FFFF0000 <---------------------- ...calculate...
00E00067 MOV EBX,DWORD PTR DS:[EBX] <---------- EBX=DWORD PTR DS:[4021A0] now.
00E00075 SHR EAX,18 <----------------------------- ...calculate...
00E00089 CMP EBX,EAX <--------------------------- And here it is, emulation of that comparing.
00E000BB POP EBX <-------------------------------- Restoring values and returning.
00E000BC POP EAX
00E000BD POP EBP
00E000BE RETN
That's it! Now we can make script for emulating this and that will write original instruction in our exe. That script is "Krypton 0.5 - code pattern II". You need to edit this script too.
Now you can use all those scripts, little edit them, find OEP in CRACKME3.EXE, use all scripts we mentioned and fix image. Then dump file and rebuild IAT with ImpREC. That is it! Dumped file will work OK.
But there is still two kinds of encryption that we didn't see in this crackme. More about that in 7. chapter.
-
7. On-the-fly code decryption/encryption
--------------------------------------------------------------------------------
This on-the-fly or runtime decryption/encryption is nothing new in a world of protectors. You will not find it in CRACKME3.EXE because source code needs to have markers so Krypton can know what code blocks it needs to encrypt. I have tried to modify some sources but files just crushed after protecting, obviusly this feature is still buggy. But I will descrybe it on another example; Krypton.exe itself has this kind of encryption.
There are two kind of markers that Krypton uses, crypt and clear. In both cases certain code blocks are still encrypted after file is unpacked and OEP code is being executed. Example of such code block is below (picture is taken from Krypton.exe version 0.5):
Picture shows OEP of Kryptor.exe. At the 401025 you see call to algorithm that will decrypt code below it. Below call you can see some block of junky code, that is encrypted original code. Krypton will decrypt that code block but not in main image, it will decrypt it in some other virtual block. There it will execute that code and then jump back to continue normal work. There is one difference betveen clear and crypt algorithm. Both algos are the same only clear algorithm will erase encrypted code block after using it. Clear option is used only for code that can be exeuted only once. Crypt option will leave it so that code block can be executed more than once. Let we continue our example, what do we have here:
00401000 PUSH 0
00401002 CALL Krypton.00403A9D
00401007 MOV DWORD PTR DS:[4063B5],EAX
0040100C PUSH Krypton.0040682C
00401011 CALL Krypton.00403A91
00401016 CALL Krypton.00403AA3
0040101B PUSH 1E426 <---------------------- Parameter for decrypting.
00401020 PUSH 0B070C <--------------------- Second one.
00401025 CALL DWORD PTR DS:[B20000] <------ Decryptor call.
0040102B INTO <---------------------------- Start of encrypted code.
0040102C POP SS
0040102D SBB EAX,A9CBEDB6
00401032 SAHF
...
...
...
004010F7 INS BYTE PTR ES:[EDI],DX
004010F8 AAM 0D
004010FA LOOPDNE SHORT Krypton.00401135
004010FC MOV CH,1A
004010FE XCHG DWORD PTR SS:[ESP+69DFE2D8],ECX
00401105 INC EBP
00401106 XOR AL,8C
00401108 AAM 90
0040110A NOP
0040110B NOP
0040110C PUSH Krypton.00405452
00401111 CALL DWORD PTR DS:[3C8476]
00401117 CALL Krypton.00403B5D
If you enter to decryptor call at 401025, you will find similar algorithm like in IAT redirection or code obfuscation. Below are only important opcodes, junk is removed:
00B20014 POP EBP
00B20015 SUB EBP,416B3B
00B2001B MOV ESI,DWORD PTR SS:[EBP+416E1F]
00B20045 XOR DWORD PTR SS:[ESP+20],ESI
00B2006B XOR WORD PTR SS:[ESP+24],SI
00B20092 MOV ECX,DWORD PTR SS:[ESP+20]
00B200B8 MOV EBX,DWORD PTR SS:[ESP+24]
00B200E3 LEA EDI,DWORD PTR SS:[EBP+417248]
00B20110 MOV ESI,EAX
00B20139 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00B2015C MOV ECX,EDI
00B20184 ADD ECX,6
00B201AF MOV DWORD PTR DS:[EDI],25FF
00B201D4 MOV DWORD PTR DS:[EDI+2],ECX
00B201FD MOV DWORD PTR DS:[EDI+6],ESI
00B2021F MOV EBX,DWORD PTR SS:[ESP+24]
00B2024B AND EBX,FFFF0000
00B20278 CMP EBX,10000
00B202A5 JNZ 00B20443
00B20347 MOV ECX,DWORD PTR SS:[ESP+20]
00B2036C MOV ESI,EAX
00B2038E DEC ESI00B203D5 INC ESI
00B203F6 MOV BYTE PTR DS:[ESI],0 <------------ Erase code in exe.
00B2041F LOOPDNE SHORT 00B203B5 <------------- Do that untill ECX=0 (jump to INC ESI)00B2046A MOV EBX,DWORD PTR SS:[ESP+24]
00B20493 MOV ECX,DWORD PTR SS:[ESP+20]
00B204B8 LEA EDI,DWORD PTR SS:[EBP+417248]
00B204E3 XOR EAX,EAX
00B20508 MOV AX,BX00B20554 NEG CL
00B20577 ADD BYTE PTR DS:[EDI],CL
00B2059B XOR BYTE PTR DS:[EDI],CL
00B205BE ROL BYTE PTR DS:[EDI],CL
00B205E3 NEG CL
00B20606 SUB BYTE PTR DS:[EDI],AL
00B20629 ADD BYTE PTR DS:[EDI],AH
00B20653 XOR BYTE PTR DS:[EDI],AL
00B20674 ROL BYTE PTR DS:[EDI],CL
00B2069C XOR BYTE PTR DS:[EDI],AH
00B206E6 LOOPDNE SHORT 00B206EA
Ok, above code snippet is whole ago that is doing this decrypting but I will explain just what is happening with our encrypted code.
First, encrypted code is copied to allocated block:
00B20139 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
Then, it is decrypted there:
00B20721 50 PUSH EAX
00B20722 8BF8 MOV EDI,EAX
00B20724 32C0 XOR AL,AL
00B20726 B9 FFFFFFFF MOV ECX,-1
00B2072B F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00B2072D F7D9 NEG ECX
00B2072F 49 DEC ECX
00B20730 5E POP ESI ; Krypton.00401108
00B20731 8BD9 MOV EBX,ECX
00B20733 BF 1F564000 MOV EDI,40561F
00B20738 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00B2073A 8BC3 MOV EAX,EBX
00B2073C 33DB XOR EBX,EBX
00B2073E 33C9 XOR ECX,ECX
00B20740 BE 1F564000 MOV ESI,40561F
00B20745 803E 22 CMP BYTE PTR DS:[ESI],22
00B20748 75 01 JNZ SHORT 00B2074B
00B2074A 46 INC ESI ; Krypton.00401108
00B2074B 4E DEC ESI ; Krypton.00401108
00B2074C 49 DEC ECX
00B2074D 41 INC ECX
00B2074E 46 INC ESI ; Krypton.00401108
00B2074F 803E 2E CMP BYTE PTR DS:[ESI],2E
00B20752 ^75 F9 JNZ SHORT 00B2074D
00B20754 83C6 04 ADD ESI,4
00B20757 83C1 04 ADD ECX,4
00B2075A 55 PUSH EBP
00B2075B 8BE8 MOV EBP,EAX
00B2075D 803E 20 CMP BYTE PTR DS:[ESI],20
00B20760 75 05 JNZ SHORT 00B20767
00B20762 BB 01000000 MOV EBX,1
00B20767 8BD1 MOV EDX,ECX
00B20769 2BF1 SUB ESI,ECX
00B2076B BF AF544000 MOV EDI,4054AF
00B20770 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00B20772 8BCA MOV ECX,EDX
00B20774 890D 4E544000 MOV DWORD PTR DS:[40544E],ECX
00B2077A 8305 4E544000 03 ADD DWORD PTR DS:[40544E],3
00B20781 2BF1 SUB ESI,ECX
00B20783 BF 4A534000 MOV EDI,40534A
00B20788 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00B2078A C607 20 MOV BYTE PTR DS:[EDI],20
00B2078D C647 01 25 MOV BYTE PTR DS:[EDI+1],25
00B20791 C647 02 31 MOV BYTE PTR DS:[EDI+2],31
00B20795 8BCA MOV ECX,EDX
00B20797 8BD6 MOV EDX,ESI ; Krypton.00401108
00B20799 BE AF544000 MOV ESI,4054AF
00B2079E BF 35764000 MOV EDI,407635
00B207A3 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00B207A5 4F DEC EDI
00B207A6 803F 5C CMP BYTE PTR DS:[EDI],5C
00B207A9 ^75 FA JNZ SHORT 00B207A5
00B207AB 47 INC EDI
00B207AC B9 0C000000 MOV ECX,0C
00B207B1 BE 39774000 MOV ESI,407739 ; ASCII "Krypton.cfg"
00B207B6 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00B207B8 C607 00 MOV BYTE PTR DS:[EDI],0
00B207BB 8BF2 MOV ESI,EDX
00B207BD 85DB TEST EBX,EBX
00B207BF 74 28 JE SHORT 00B207E9
00B207C1 83C6 01 ADD ESI,1
00B207C4 8BC6 MOV EAX,ESI ; Krypton.00401108
00B207C6 8BDE MOV EBX,ESI ; Krypton.00401108
00B207C8 50 PUSH EAX
00B207C9 8BF8 MOV EDI,EAX
00B207CB 32C0 XOR AL,AL
00B207CD B9 FFFFFFFF MOV ECX,-1
00B207D2 F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00B207D4 F7D9 NEG ECX
00B207D6 49 DEC ECX
00B207D7 5E POP ESI ; Krypton.00401108
00B207D8 BF 7F574000 MOV EDI,40577F
00B207DD F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00B207DF C705 466E4000 01>MOV DWORD PTR DS:[406E46],1
00B207E9 8F05 0C684000 POP DWORD PTR DS:[40680C]
00B207EF EB 0E JMP SHORT 00B207FF
00B207F1 4B DEC EBX
00B207F2 44 INC ESP
00B207F3 45 INC EBP
00B207F4 45 INC EBP
00B207F5 0000 ADD BYTE PTR DS:[EAX],AL
00B207F7 0000 ADD BYTE PTR DS:[EAX],AL
00B207F9 0000 ADD BYTE PTR DS:[EAX],AL
00B207FB 0000 ADD BYTE PTR DS:[EAX],AL
00B207FD 0000 ADD BYTE PTR DS:[EAX],AL
00B207FF -FF25 0508B200 JMP DWORD PTR DS:[B20805] ; Krypton.00401109
00B20805 0911 OR DWORD PTR DS:[ECX],EDX
00B20807 40 INC EAX
00B20808 0000 ADD BYTE PTR DS:[EAX],AL
00B2080A 0000 ADD BYTE PTR DS:[EAX],AL
00B2080C 0000 ADD BYTE PTR DS:[EAX],AL
And then protector jumps to that block and execute that code from there:
00B20717 5D POP EBP
00B20718 5F POP EDI
00B20719 5E POP ESI ; Krypton.00401108
00B2071A 59 POP ECX
00B2071B 5B POP EBX
00B2071C 9D POPFD
00B2071D 58 POP EAX
00B2071E 83C4 0C ADD ESP,0C
00B20721 50 PUSH EAX
00B20722 8BF8 MOV EDI,EAX
00B20724 32C0 XOR AL,AL
00B20726 B9 FFFFFFFF MOV ECX,-1
00B2072B F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00B2072D F7D9 NEG ECX
00B2072F 49 DEC ECX
00B20730 5E POP ESI ; Krypton.00401108
00B20731 8BD9 MOV EBX,ECX
00B20733 BF 1F564000 MOV EDI,40561F
00B20738 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00B2073A 8BC3 MOV EAX,EBX
...
...
After execution it will jump to target code:
00B207DD F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00B207DF C705 466E4000 01>MOV DWORD PTR DS:[406E46],1
00B207E9 8F05 0C684000 POP DWORD PTR DS:[40680C]
00B207EF EB 0E JMP SHORT 00B207FF
00B207F1 4B DEC EBX
00B207F2 44 INC ESP
00B207F3 45 INC EBP
00B207F4 45 INC EBP
00B207F5 0000 ADD BYTE PTR DS:[EAX],AL
00B207F7 0000 ADD BYTE PTR DS:[EAX],AL
00B207F9 0000 ADD BYTE PTR DS:[EAX],AL
00B207FB 0000 ADD BYTE PTR DS:[EAX],AL
00B207FD 0000 ADD BYTE PTR DS:[EAX],AL
00B207FF -FF25 0508B200 JMP DWORD PTR DS:[B20805] ; Krypton.00401109
00B20805 0911 OR DWORD PTR DS:[ECX],EDX
00B20807 40 INC EAX
00B20808 0000 ADD BYTE PTR DS:[EAX],AL
00B2080A 0000 ADD BYTE PTR DS:[EAX],AL
00B2080C 0000 ADD BYTE PTR DS:[EAX],AL
And there you can see that all code before is erased:
So how could we fix that? Simply with binary copy-pasting. Problem could be if there is lot of these patherns that we should find them all and then force exe to decrypt that block. I was planing to write script that could decrypt all blocks like I made for IAT and rest of CODE tricks, but plugin at that time didn't support byte operands and some instructions like ROL, ROR, NEG, etc. so I couldn't make it. Todays new plugin supports some new operations and maybe it is possible to write such script but I lost interest for that.
-
And that was all for this tutorial about Krypton 0.5. This protector is really good one, but it should be more stabile. It was very fun to see what is going on inside. I have unpacked it by big help from OllyScript plugin, but there could be another approach. When Krypton unpacks file in memory it marks all calls that should point to some K-Execution with flags in dwords. Flags are from 0 to 5. On a base of that flag it knows what kind of redirection needs to place in that block. It is possible to force Krypton decrypt it code so there is no need for scripts. I couldn't find that way but you could try to dig something there.
اسکریپت های مربوط به این کرک در پست های بعدی ... >
-
Krypton 0.5 - OEP finder
This script will find OEP on target packed with Krypton 0.5. It does not require any kind of editing. Just copy below code in some text file, name it somehow and use it.
var VirtualAlloc
var start
var OEPI
var OEPII
msg "Ignore ALL exceptions and remove ALL breakpoints!!!"
gpa "VirtualAlloc","kernel32.dll"
cmp $RESULT,0
je error
mov VirtualAlloc,$RESULT
add VirtualAlloc,1
bp VirtualAlloc
esto
bc eip
rtr
bp eip
esto
bc eip
mov start,eax
bprm start,1
esto
esto
bpmc
find eip,#C3EB1EDF694E58DF5972F5EB01DF73F0DF599C83C1E79 DFFE1EB0D51E8F0FFFFFFE801000000#
cmp $RESULT,0
je error
mov OEPI,$RESULT
bphws OEPI,"x"
findop eip,#8B9513784400#
cmp $RESULT,0
je error
mov OEPII,$RESULT
add OEPII,2D
bphws OEPII,"x"
esto
bphwc OEPI
bphwc OEPII
sti
an eip
ret
error:
msg "ERROR! Sorry but some error occured :("
ret
-
Krypton 0.3/0.5 - script for standard IAT redirection
This is my old script from Krypton 0.3 tutorial. Both packer versions use this same algo. This script doesn't require editing, but it ask you for address of block where obfuscated imorts are. Copy below text to tome text file and use it as normal Olly script.
/*
================================================== =======
Krypton 0.3/0.5 - script for standard IAT redirection
================================================== =======
*/
var addr
var section
var x
var y
ask "Enter base address of redirected IAT section:"
cmp $RESULT,0
je exit
mov section,$RESULT
//First patern:
mov addr,section
ADD_SUB:
find addr,#8105????????????????A1????????812D?????????? ??????FFE0#
cmp $RESULT,0
je next
mov addr,$RESULT
add addr,2
mov x,[addr]
mov x,[x]
add addr,4
mov y,[addr]
add x,y
mov [$RESULT],000000e9
add $RESULT,1
sub x,$RESULT
sub x,4
mov [$RESULT],x
add $RESULT,4
fill $RESULT,1a,90
jmp ADD_SUB
//Second patern:
next:
mov addr,section
XOR_XOR:
find addr,#8135????????????????A1????????8135?????????? ??????FFE0#
cmp $RESULT,0
je exit
mov addr,$RESULT
add addr,2
mov x,[addr]
mov x,[x]
add addr,4
mov y,[addr]
xor x,y
mov [$RESULT],000000e9
add $RESULT,1
sub x,$RESULT
sub x,4
mov [$RESULT],x
add $RESULT,4
fill $RESULT,1a,90
jmp XOR_XOR
exit:
ret
-
Krypton 0.5 - KProtection on API fixer script
This script will fix KProtection redirection , but it needs to be manually edited from user side. Also , I have assumed that packed file has image base 400000 and that code section is at 401000. YOu can change all that in case that you file is different, but for most files this will be OK.
/*
================================================== =======
Krypton 0.5 , K-Protection on API - fixer script v0.1
================================================== =======
This script is ripped from Krypton's "K-Protect on API"
code. It will fix those CALL DWORD[xxxxxxxx] (where
xxxxxxxx is pointer to K-Protection on API code) to
JMP DWORD[yyyyyyyy] (where yyyyyyyy is pointer to
correct values in import section). After using this
script, use first one for standard IAT redirection and
then use ImpREC to grab imports.
Limitations:you need to manually edit script for any
new target; I assumed that image base is 400000;
script will probably be very slow on bigger files.
Regards, haggar.
================================================== ========
*/
var reax
var rebx
var recx
var redx
var rebp
var redi
var temp
var addr
var start
var imp
mov addr,401000
SearchCalls:
findop addr,#FF157A843C00# //Find import calls, you need edit this for new targets.
cmp $RESULT,0
je exit
mov addr,$RESULT
mov recx,$RESULT //ECX= address of our call
mov start,$RESULT
add start,2
mov start,[start]
mov start,[start]
find start,#5D81ED7CBD4100#
cmp $RESULT,0
je error
mov rebp,$RESULT
sub rebp,41BD7C
mov reax,rebp
add reax,42617D
mov rebx,42614D
add rebx,rebp
LABEL_01:
mov redx,[reax]
xor redx,[rebx]
sub redx,recx
cmp redx,0
je LABEL_02
add reax,0A
jmp LABEL_01
LABEL_02:
mov redx,reax
add redx,4
mov redx,[redx]
xor redx,[rebx]
mov temp,redx
and temp,0FF
cmp temp,0
jne error
mov redi,1
shr redx,10
mov recx,redx
mov temp,reax
add temp,8
mov temp,[temp]
and temp,0FFFF
and redx,0FFFF0000
add redx,temp
xor redx,[rebx]
and redx,0FFFF
shl redx,10
or redx,recx
mov reax,redx
cmp redi,0
je error
mov imp,addr
mov [imp],000025FF
add imp,2
mov [imp],reax
jmp SearchCalls
exit:
ret
error:
msg "ERROR!"
ret
-
Krypton 0.5 - script for fixing code pattern I
This script will fix those replaced 6-byte code patterns like
00401139 . FF15 76843D00 CALL DWORD PTR DS:[3D8476]
to original bytes.
/*
================================================== ===========
Krypton 0.5 - script for fixing code pattern I
================================================== ===========
*/
var reax
var rebx
var recx
var redx
var rebp
var temp
var addr
var start
mov addr,401000 //I have assumed that code section is at this address.
SearchCalls:
findop addr,#FF1576843C00# //Find signature of decryptor call. You need to edit this part.
cmp $RESULT,0
je exit
mov addr,$RESULT
mov recx,$RESULT
mov start,$RESULT //Find start of algorithm code (this is slowing script, but f*** it).
add start,2
mov start,[start]
mov start,[start]
find start,#5D81ED4FB84100# //Find reference address (slowing again).
cmp $RESULT,0
je error
mov rebp,$RESULT
sub rebp,41B84F //First constant.
//-------- OK, now find pointers to values -----------------
mov reax,rebp //Pointer to encrypted reference value.
add reax,41C509
mov rebx,41C4C0
add rebx,rebp
LABEL_01:
mov redx,[reax] //Taking encrypted reference value.
xor redx,[rebx] //Decrypting it.
sub redx,recx //Compare them.
cmp redx,0
je LABEL_02 //If match, go to decryption code.
add reax,0A
jmp LABEL_01
LABEL_02:
mov redx,reax
add redx,4
mov redx,[redx]
xor redx,[rebx]
mov [recx],redx //Place original dword in packed app.
and redx,0FFFF0000
mov temp,reax
add temp,8
mov temp,[temp]
and temp,0FFFF
add redx,temp
xor redx,[rebx] //Decrypt it.
and redx,0FFFF
mov temp,recx
add temp,4
and [temp],0FFFF0000
add [temp],redx
jmp SearchCalls
exit:
ret
error:
msg "ERROR! Oooops, some error has occured :("
ret
-
Krypton 0.5 - code pattern II
var reax
var rebx
var rebp
var addr
mov addr,401000 //Assumed that code section starts from here. You can edit this.
SearchCalls:
find addr,#5053FF15????E0005B58# //You need to edit this ????D800 signature.
cmp $RESULT,0
je exit
mov addr,$RESULT
add $RESULT,4
mov $RESULT,[$RESULT]
mov $RESULT,[$RESULT]
find $RESULT,#5D81ED9AB74100#
cmp $RESULT,0
je error
mov rebp,$RESULT
sub rebp,41B79A
mov reax,rebp
add reax,41B7CD
mov rebx,[reax]
add reax,4
mov reax,[reax]
xor rebx,reax
add reax,rebx
mov [addr],05C7
add addr,2
mov [addr],rebx
add addr,4
mov [addr],reax
jmp SearchCalls
exit:
ret
error:
msg "ERROR! Sorry but some error occured :("
ret
-
Krypton 0.5 - code pattern III fixer script
This script should find and fix this kind of code replacing:
00401066 FF15 0000E000 CALL DWORD PTR DS:[E00000]
0040106C 90 NOP
0040106D 75 C8 JNZ SHORT packed.00401037
to original one:
00401066 833D A0214000 12 CMP DWORD PTR DS:[4021A0],12
0040106D 75 C8 JNZ SHORT Copy_of_.00401037
You need to edit one part of script (change sig that is base
of allocated memory block).
/*
================================================== ==================
Krypton 0.5 - code III fixer script
================================================== ==================
*/
var reax
var rebx
var rebp
var temp
var addr
mov addr,401000
SearchCalls:
find addr,#FF15????E80090#
cmp $RESULT,0
je exit
mov addr,$RESULT
add $RESULT,2
mov $RESULT,[$RESULT]
mov $RESULT,[$RESULT]
find $RESULT,#5D81EDE6B74100#
cmp $RESULT,0
je error
mov rebp,$RESULT
sub rebp,41B7E6
mov reax,rebp
add reax,41B7F7
mov rebx,[reax]
add reax,4
mov reax,[reax]
xor rebx,reax
add reax,rebx
and reax,0FFFF0000
shr reax,18
add addr,7
mov temp,[addr]
sub addr,7
mov [addr],3D83
add addr,2
mov [addr],rebx
add addr,4
mov [addr],reax
add addr,1
mov [addr],temp
jmp SearchCalls
exit:
ret
error:
msg "Ooo sh** >:-( ! Sorry but some error has ocurred :("
ret