Sunday, August 27 2006 @ 02:23 PM CEST Contributed by: haggar Views: 2371
Level : intermediate
~~~~~~~~~~~~~~~~~~~
Bustme1 solution by haggar
~~~~~~~~~~~~~~~~~~~
The goal of this crackme is to unpack it and then dig a serial for some name while keygen is optional. Solution consit from two parts - unpacking and reversing serial. Tools that I used are OllyDbg 1.10, ImpREC, LordPE, hex editor.
Crackme is packed with custom protector. Protector is based on two-process protection (self debugging) which makes it more dificult for unpacking with Ring3 debuggers such as Olly. Protected file has some bad values in PE header (Number Of Rva And Sizes, for example) which Olly cannot dygest. We cannot change those values because crackme becomes corupted. But that doesn't prevent debugging in olly. However, if you would like to have nice analysis, try to find OllyAdvanced plugin that fixes these bugs in Olly.
Protector has few anti-debug tricks. It uses IsDebuggerPresent to detect debugger and BlockInput to block keyboard and mouse. Those are well known tricks (check Yoda Protectors tutorials on BIW reversing for more info about these tricks). But main protection is self-debugging.
Protector debugs it's own process to prevent debugging from Ring3 debuggers. Detaching processes will not give solution because import accessing requires both processes. unpacking can be separated in two parts; decrypting and import repairing.
- Decrypting and dumping -
Since this is a self-debugging based protection, processes must know which one is debugger and which one is debuggee. When we start process by double-click, that process must check is he a debuggee. There are warious techniques to accomplish this; command line arguments, mutex, etc. But this protector will use very simple solution. It will check does some string exist in it, if it does - that process is debuggee. If not, then it will start a new process and write that string in it. Second process will perform same check. For writing is used WriteProcessMemory that writes next string to 400002 (in PE header) of "son" process:
####INVIUS_L0RD_EXECUTABLE
So to avoid self-debugging, we just have to write this string in our process loaded in Olly. However, altough there will be no double processes, crackme will create debugg loop and try to debug something. Debugging loop is in separate thread while main one enters in infinite jump EB FE. Why it that happening , I didn't tried to find out. All sections of crackme are decrypted except import section at 406000. That is enough for dumping. Dump is OK, it just needs fixing some PE info (sections sizes needs to be rounded on 1000 alignment). OEP is easy to find since we have MingWin compiled executable. OEP is at 401220.
- Import repairing -
This is little harder thing to sove. For that I had to examne protector's debug loop. Secondary process, a debuggee, has redirected all import jumps to allocated block. For example, if we detach processes and follow first import call at 40122D:
0040122D FF15 5C614000 CALL DWORD PTR DS:[40615C]
at the call destination (I removed junk jumps from pasted code):
EAX holds IMPORT_ID value which is maybe some hash. Then it jumps to IMPORT_ALGO where some small calculations are performed and then HLT opcode is triggered that couses exception C0000096 (PRIVILEGED INSTRUCTION):
003E5AD5 F4 HLT ; Privileged command
That exception is caugth by debugger, ei. first process which now takes controll. If we examne debug loop of first process, we will see how this exception is handled:
003E4C5D 813D 494D3E00 96>CMP DWORD PTR DS:[3E4D49],C0000096 ; Our exception code.
003E4C67 74 31 JE SHORT 003E4C9A
003E4C69 813D 494D3E00 04>CMP DWORD PTR DS:[3E4D49],80000004
003E4C73 74 58 JE SHORT 003E4CCD
003E4C75 813D 494D3E00 03>CMP DWORD PTR DS:[3E4D49],80000003
003E4C7F 74 05 JE SHORT 003E4C86
003E4C81 ^E9 DAFDFFFF JMP 003E4A60
Protector then retrieves resgisters state of debugged process (via GetThreadContext) and reads some data from debuggee (ReadProcessMemory). With that data it decrypts apropriate strings (API ordinals and DLL names) in 406000 section, loads libraries, retrieves API address and uses SetThreadContext as a jump directly to API. Then it continues debug event.
To retrieve imports, I wrote small script that plays with thread context buffer. After couple minutes and manually work, I placed all pointers in dumped file, then I loaded needed libraries in Olly (kernel32.dll, user32.dll, msvcrt.dll) and used ImpREC to rebuild IAT. Then I noticed that I still has some problems.
- Runtime decryption-encryption -
Protector has runtime decryption-encryption. For example, this part will decrypt 185 bytes starting from 00401C2D address:
00401C08 $ 55 PUSH EBP
00401C09 . 89E5 MOV EBP,ESP
00401C0B . 83EC 78 SUB ESP,78
00401C0E > 50 PUSH EAX
00401C0F . 68 85010000 PUSH 185 ; Number of bytes to decrypt.
00401C14 . 68 2D1C4000 PUSH bustme1.00401C2D ; Starting address.
00401C19 . B8 00009B00 MOV EAX,9B0000 : Block where decryptor loop is placed.
00401C1E . FFD0 CALL EAX
00401C20 . 58 POP EAX
There are about 20~30 this paterns. They doesn't have to be decrypted. We can place those decryptor loops in dump and change MOV EAX,009B0000 to new address , MOV EAX,0040xxxx. And dump will run fine. But it is better to decrypt all those parts and NOP all those opcodes. After decrypting, I removed last two sections and now I have nice and clean dump.
[2] REVERSING KEYCHECK
Keycheck is not hard , but I'm totally rusty. Check is performed in separate thread. Also, crackme goes to Sleep for a 500 miliseconds every little. Don't know why, maybe just to annoy us. Keycheck consist from two main parts.
- First part -
00401A7C |. 83F8 11 |CMP EAX,11 ; Serial must be 11h bytes long.
00401A7F |. 75 25 |JNZ SHORT dump.00401AA6
00401A81 |. C70424 9050400>|MOV DWORD PTR SS:[ESP],dump.00405090
00401A88 |. E8 71F9FFFF |CALL dump.004013FE
00401A8D |. 83F8 03 |CMP EAX,3 ; Name must have min 4 chars.
00401A90 |. 76 14 |JBE SHORT dump.00401AA6
00401A92 |. 803D 14514000 >|CMP BYTE PTR DS:[405114],2D ; Serial format is XXXX-XXX-XXXXXXXX
00401A99 |. 75 0B |JNZ SHORT dump.00401AA6
00401A9B |. 803D 18514000 >|CMP BYTE PTR DS:[405118],2D
00401AA2 |. 75 02 |JNZ SHORT dump.00401AA6
00401AA4 |. EB 14 |JMP SHORT dump.00401ABA
Crackme will then take midlle part of serial (those 3 chars between "-") and INC/DEC them to create new three byte string "S1,S2,S3":
Then it creates 100h byte array and fills it with values 00,01,02,03,04,....,FD,FE,FF. This array is then changed with our new three byte string values. One string is taken:
If they match - first check is passed. This check depends only on 3 bytes from our serial, and it is actually hardcode part same for every name. I tought that it was too big job to rip all that code, and all in order to find out 3 bytes from serial. So I patched algo to loop untill it finds right 3 bytes that give correct hash. Three bytes that gives correct hash are
9G7
which gives new serial shape
XXXX-9G7-XXXXXXXX
- Second part -
Second part of serial is harder to catch and this time I needed some brain usage. I'll start from end. We will see GoodBoy message if this loop gets EAX=3 as incoming parameter. In case of EAX=1, serial is bad, other values are possible bugs in crackme (judging by author comments):
As it can be seen, EAX=3 if [00405010]=0, and that will be if CALL at 00401B93 returns EAX=0. So we must start from the end of that procedure. I will not paste code, but just reversed pseudo code:
That was reversing from the end. _lrotl is ROL and reg represent DWORD value in register format (in memory bytes are swapped). Before that _lrotr is used on dword given from name hash and last part of serial. Procedure for name hash is at 004016A8. Procedure for serial hash is at 0040195C and it is not actually a hash. It is just characters formated to hex value (egxample "12345678" -> 12345678h). Those two hashes must give A0003C32 value to _lrorl.
[78560F0D] -> 0D0F5678 -> This value must be A0003C32 to pass last check.
And here is solution: We can take name hash as constant (nhash) and we need to find serial hash that will give needed DWORD. Since serial hash is nothing but last part of serial formated as hex DWORD, we can reverse it easily.
0D0F(5678) -> Last four serial chars from false serial -> "5678"
A000(3C32) -> True last four chars -> "3C32"
XOR 1F3B0000,12340000 = 0D0F0000
but it must be
XOR 1F3B0000,xxxxxxxx = A0000000
which gives
BF3B0000
and serial for my name is
1234-9G7-BF3B3C32
For the end I need to mention that name hash depends on firs four characters from serial too. I didn't botheredd to reverse that part too because keygen is optional, but it is not hard and generic keygen is easy to code.
[3] THE END
And would be it. Greets to bpx , this was nice crackme but not so hard as I tought it would be.