Here is a nice crackme written by Orp. At first I did take only a little glance at the crackme but I decided to give it a try after reading this comment made by Orp “im not using code obfuscation, import wrapping or antidebugging stuff at all, but im pretty sure the exe isn’t patchable”. Hmm, a challenge :)
There are no classic anti-debug tricks nor anti-disasm tricks, only a good protection around the name/serial algorithm. The name/serial algorithm is very easy but it’s not the core of the crackme, the core is the protection that is used to hide the algorithm in order to make the reversing session a little bit hard.
The crackme is composed by two files, an exe and a dll. I started looking at the exe without luck so I passed to the dll which is the file to analyse. As I said before the file is not packed so everything is in front of my eyes. From all the functions one of them caught my attention: “check”. This function, which is called by the exe, contains the protector and the protection routine.
Protector
The protector is used to hide the protection routine. Let’s see how it looks like!
The ‘check’ function starts allocating a memory space from the process heap. After that the protector starts:
1000D5F4 mov ebx, eax ; eax is the start address of the allocated block of memory
1000D5F6 mov [ebx+14h], esi ; esi = 0x10003010 (xm.dll address)
1000D5F9 lea eax, [ebx+100h]
1000D5FF mov [ebx+1Ch], eax
1000D602 mov [ebx+0Ch], esp
1000D605 lea eax, [ebx+101D0h] ; Some little initializations
1000D60B mov [ebx], eax
1000D60D mov [ebx+4], eax
1000D610 mov esi, 0A642h ; esi = 0xA642
1000D615 mov eax, 555E0D3Ch ; eax = starting key
1000D61A mov ecx, 11h ; ecx = number of dword to decrypt
1000D61F add esi, [ebx+14h] ; esi = 0x10003010 + 0xA642 = 0x1000D652
1000D622 or eax, eax
1000D624 jnz short loc_1000D628 ; jump…
1000D626 jmp esi
1000D628 mov edi, [ebx+1Ch] ; edi = 0x1562B8, a pointer to a buffer inside the allocated block of memory
1000D62B add edi, [ebx+20h] ; edi = 0x1562B8 + 0x0
1000D62E add word ptr [ebx+20h], 8000h ; [ebx+0x20] = 0x0 + 0x8000 = 0x8000
1000D634 mov edx, [esi+ecx*4-4] ; Get 4 bytes from the dll
1000D638 xor edx, eax ; xor the bytes with the key
1000D63A mov [edi+ecx*4-4], edx ; Put the new 4 bytes inside the allocated block of memory
1000D63E rol eax, cl ; Change the initial key
1000D640 add eax, ecx ; Change the initial key
1000D642 loop loc_1000D634
1000D644 jmp edi ; The jump brings me at 1562B8
A decryption routine, nothing interesting you would say but you have to see the next part of code before saying something:
001562B8 MOV EAX,28
001562BD MOV DS:[EBX+68],EAX
001562C3 MOV ESI,0A69A ; esi = 0xA69A
001562C8 MOV EAX,106A109C ; eax = starting key
001562CD MOV ECX,11 ; ecx = number of dword to decrypt
001562D2 ADD ESI,DS:[EBX+14] ; esi = 0x10003010 + 0xA69A = 0x1000D6AA
001562D5 OR EAX,EAX
001562D7 JNZ SHORT 001562DB ; jump…
001562D9 JMP ESI
001562DB MOV EDI,DS:[EBX+1C] ; edi = 0x1562B8 points to a buffer inside the allocated block of memory
001562DE ADD EDI,DS:[EBX+20] ; edi = 0x1562B8 + 0x8000
001562E1 ADD WORD PTR DS:[EBX+20],8000 ; [ebx+0x20] = 0x8000 + 0x8000 = 0x0
001562E7 MOV EDX,DS:[ESI+ECX*4-4] ; Get 4 bytes from the dll
001562EB XOR EDX,EAX ; Xor the bytes with the key
001562ED MOV DS:[EDI+ECX*4-4],EDX ; Put the new 4 bytes inside the allocated block of memory
001562F1 ROL EAX,CL ; Change the initial key
001562F3 ADD EAX,ECX ; Change the initial key
001562F5 LOOPD SHORT 001562E7
001562F7 JMP EDI ; The jump brings me at 15E2B8
Well, as you can see the snippets posted above are really similar. The last 17 instructions are the same. Before making some considerations I’d like to show you some instructions from the next snippets:
0015E2B8 MOV EAX,DS:[EBX+C] ; Algorithm instructions
0015E2BE ADD DS:[EBX+68],EAX ; Algorithm instructions
0015E2C4 MOV ESI,0A6F7
0015E2C9 MOV EAX,37A878F4
…
0015E2F8 JMP EDI ; Jump to 1562B8
Some considerations:
– The snippets are executed starting from 2 different addresses, one from 1562B8 and one from 15E2B8; in this way you can’t see all the code but only two little pieces at a time. These are the instructions used to perform this trick:
ADD EDI,DS:[EBX+20]
ADD WORD PTR DS:[EBX+20],8000
– Every decrypted snippet is composed by 2 parts:
1. name/serial_algorithm: only few instructions
2. decryption_routine: instructions used to decrypt the next snippet
This is not totally true because looking at the trace log I’ve seen some exceptions, sometimes you can find only the decryption_routine and sometimes you can find a different decryption_routine:
001562B8 MOV EAX,2C3F
001562BD ADD EAX,DS:[EBX+14]
001562C0 MOV ECX,5
001562C5 XOR EDX,EDX
001562C7 XOR EDX,DS:[EAX+ECX*4-4]
001562CB LOOPD SHORT 001562C7
001562CD MOV ESI,8D89285A
001562D2 XOR ESI,EDX
001562D4 MOV EAX,C6C892A5
001562D9 XOR EAX,EDX
001562DB MOV ECX,8D898278
001562E0 XOR ECX,EDX ; ecx = number of dword to decrypt
001562E2 ADD ESI,DS:[EBX+14] ; You already know the code below this instruction
001562E5 OR EAX,EAX
001562E7 JNZ SHORT 001562EB
001562E9 JMP ESI
001562EB MOV EDI,DS:[EBX+1C]
001562EE ADD EDI,DS:[EBX+20]
001562F1 ADD WORD PTR DS:[EBX+20],8000
001562F7 MOV EDX,DS:[ESI+ECX*4-4]
001562FB XOR EDX,EAX
001562FD MOV DS:[EDI+ECX*4-4],EDX
00156301 ROL EAX,CL
00156303 ADD EAX,ECX
00156305 LOOPD SHORT 001562F7
00156307 JMP EDI
The code from 1562B8 to 1562E0 is used to obtain the value of ecx (which represents the number of dword to decrypt), the value comes out using some bytes inside xm.dll. I think the author uses this trick in order to make things a little hard;
suppose you want to write a little tool able to extract the needed instructions avoiding the decryption_routine; well with this new addiction you’ll still have useless instructions inside your code. Not so hard but usefull to confuse reversers.
– The decrypted code comes out from a simple xor instruction and the number of decrypted bytes depend on the value putted inside ecx register(it’s not always 0x11!).
Now that I know how the protector works I can try to read the protection routine. How can you read it?
The first bytes of the decrypted snippet belongs to the name/serial algorithm; to identify them you have to identify the decryption_routine and then extract the other instructions. You can’t step every instructions for sure! As I mentioned before an elegant method would be to write an application able to dump the decrypted code avoiding unnecessary instructions but it’s only doable if you are sure enough about the nature of the various decrypted snippets. Well, not impossible but requires an in depth analysis.
I solved the problem using the trace options available in OllyDbg. Once you have the entire log (saved on a file…) you can remove unnecessary instructions (i.e. decryption_routine) and read the protection routine.
Protection routine
It’s impossible to post all the code here because it’s too long, here is a little sum up:
1. Checksum over the bytes of the exe file. The crackme checks 0x2000 bytes starting from address 0x401000. I don’t know why it needs such a checksum because the exe file it’s not related with the protection, maybe I missed something…
2. Check over the serial, it has to be 19 bytes long with this form: xxxx-xxxx-xxxx-xxxx.
3. Sum of the four parts of the serial, F
4. Calculation based on the name’s bytes, R. I’m not interested in the algorithm because I’ll patch the dll but if you want to look at the calculation you have to decrypt the code starting from 0x1000E1B8.
5. Compare between the right (R) and the fake (F) value:
15E2c0 CMP DS:[EBX+64],ECX ; Compare R with F
15E2C6 JE SHORT 0015E2C9 ; jump means registered, this is what I want to patch!
15E2C8 INC EAX
15E2C9 MOV DS:[EBX+64],EAX
The algo is all here…
Patch the code
The final compare is encrypted inside the snippet which starts at 1000CDE4. Here is how the decryption works:
@loop:
mov edx, [esi+ecx*4-4] ; Get 4 bytes from the dll
xor edx, eax ; xor the bytes with the key
mov [edi+ecx*4-4], edx ; Put the new 4 bytes inside the allocated block of memory
rol eax, cl ; Change the initial key
add eax, ecx ; Change the initial key
loop @loop
It decrypts 4 bytes using a single xor operation, nothing else. To patch the instruction it’s not so hard, you only have to change a single byte. To register the crackme with any kind of serial (in the xxxx-xxxx-xxxx-xxxx form) I patched the byte at offset 0xCDF0, from 0x8D to 0x8C; the ‘je’ instruction becomes a ‘jne’ instruction.
Final notes
Pretty nice crackme with a good protector. The algorithm is really easy but I’m pretty sure about the fact that the algorithm is here just as a proof of concept. Orp attached the original script and the front end code at Woodmann’s forum; read it to see how easy is the algorithm (and how hard is to understand it :P)
Ciao!
ZaiRoN