Krypton 0.3 - (half)manually unpacking

Tuesday, October 11 2005 @ 09:57 PM CEST

Contributed by: haggar

Level : beginner

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

- Targets , here http://www.reversing.be/binaries/articles/20051011215429630.rar
- 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.


Greets goes to good folks on biw reversing, on crackmes.de and arteam. See you in the next tutorial.




[ haggar ]

0 comments



http://www.reversing.be/article.php?story=2005101121571920