Keyboard hook crackme by Basse

Thursday, April 14 2005 @ 07:13 PM CEST

Contributed by: detten

Level : beginner

Not every application uses a simple GetDlgItemText or simular API to get the input from an editbox.
This crackme by basse uses an alternate way to grab the keyboard input.


The next crackme has some interesting but unusual approaches. Curious? Read on :)

I'm going to explain how this crackme works, but you won't find a valid solution in here, if you want to find one, you'll have to brute it...
Get the crackme here : attachment


Let's dig in :)

Where to start?

Ok, Disassemble the crackme with WDasm. We see it is not packed. And because it is written using MASM, it's pretty clear code :)
After a quick look in the string table and the import table we can notice the following :

There is no GetDlgItemTextA or GetWindowText
The author uses a hook (SetWindowsHookExA) to get the input...

If we try to enter a combination 3 times, the program crashes...ans possibly your computer too! How is that possible?
The author uses the 'foofbug' here, an opcode combination that crashes Pentium and Pentium MMX procesors. (Find more info on this subject here)
Followed by some garbage wonder we crash :)

Ok, time to check out the code!

The code
; Beginning of  DlgProc

0040102C 55                      push ebp
0040102D 8BEC mov ebp, esp
0040102F 8B4508 mov eax, dword ptr [ebp+08]
00401032 A30D314000 mov dword ptr [0040310D], eax
00401037 817D0C11010000 cmp dword ptr [ebp+0C], 00000111 ;WM_COMMAND ?
0040103E 0F85FF000000 jne 00401143
00401044 8B4510 mov eax, dword ptr [ebp+10]
00401047 663DB90B cmp ax, 0BB9 ; Test-button pressed ?
0040104B 0F85D8000000 jne 00401129
00401051 FF35FF304000 push dword ptr [004030FF] ; Push hash
00401057 C705FF304000ADDED0BA mov dword ptr [004030FF], BAD0DEAD ; Restore init values
00401061 C6050831400035 mov byte ptr [00403108], 35 ; Restore init values
00401068 6A00 push 00000000
* Possible Reference to Dialog: MYDIALOG, CONTROL_ID:0BB8, ""

0040106A 68B80B0000              push 00000BB8
0040106F FF7508                  push [ebp+08]
* Reference To: USER32.SetDlgItemTextA, Ord:0228h

00401072 E827020000              Call 0040129E ; Empty the edit box
00401077 58                      pop eax ; pop hash
00401078 3DF700FB02              cmp eax, 02FB00F7 ; check hash

0040107D 7529                    jne 004010A8 ; If not equal, inc counter
0040107F 6A40                    push 00000040 ; Else good-guy message :)
* Possible StringData Ref from Data Obj ->"Rev"
       00401081 68AA304000              push 004030AA
* Possible StringData Ref from Data Obj ->"Good job! You made it!"

        00401086 6879304000              push 00403079
0040108B FF7508                  push [ebp+08]
* Reference To: USER32.MessageBoxA, Ord:01BBh
0040108E E8FF010000              Call 00401292 ; Display good-guy message
* Possible StringData Ref from Data Obj ->"Success!"
         00401093 6870304000              push 00403070
00401098 FF350D314000            push dword ptr [0040310D]
* Reference To: USER32.SetWindowTextA, Ord:0259h
0040109E E807020000              Call 004012AA ; Put "success" in captionbar


Ok, what do we have here? If we press the 'test' button, a hash value get's checked with 02FB00F7h, if we would like to patch, the jnz is the place :)
As said before, we do not find the code in the DlgProc that calculates the hash. There is no WM_CHAR message too... but we get a hint from the import table (SetWindowsHookExA) that this proggy uses a hook to get the input...

This is what the API reference says about SetWindowsHookExA :

The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. An application installs a hook procedure to monitor the system for certain types of events. A hook procedure can monitor events associated either with a specific thread or with all threads in the system. This function supersedes the SetWindowsHook function.

HHOOK SetWindowsHookEx(

int idHook, // type of hook to install
HOOKPROC lpfn, // address of hook procedure
HINSTANCE hMod, // handle of application instance
DWORD dwThreadId // identity of thread to install hook for

We find it here :
:00401143 817D0C10010000          cmp dword ptr [ebp+0C], 00000110 ; WM_INITDIALOG
:0040114A 7543 jne 0040118F
:0040114C C6050831400035 mov byte ptr [00403108], 35 ; init var
:00401153 6A00 push 00000000 ; hook all treads
:00401155 FF3509314000 push dword ptr [00403109] ; handle
:0040115B 68C0114000 push 004011C0 ; HookProcedure
:00401160 6A03 push 00000003 ; WH_GETMESSAGE
* Reference To: USER32.SetWindowsHookExA, Ord:025Dh
00401162 E849010000               Call 004012B0
00401167 0BC0                     or eax, eax ; if Hook succeeds
00401169 7405                     je 00401170
0040116B A303314000               mov dword ptr [00403103], eax ; Save hook handle
Next, the focus is set on the editbox, we are ready for input :)

What happens in the above code? Well, when the DialogBox is created the WM_INITDIALOG message is sent, so this code gets executed once at the beginning.
First the variables for the hash calculation are initialized. (BYTE ptr [403108] = 35 ) and DWORD ptr [4030FF] is already initialized at BAD0DEADh

Next, the hook is set up. It' s a hook for WH_GETMESSAGE and the hook routine starts at address 4011C0h.

Let's check out this hook routine :

     004011C0 55                      push ebp
004011C1 8BEC mov ebp, esp
004011C3 837D0800 cmp dword ptr [ebp+08], 00000000
004011C7 731A jnb 004011E3 ; If we have input, go to 'calculate hash'
004011C9 FF7510 push [ebp+10] ; Else pass to next
004011CC FF750C push [ebp+0C] ; hook in the chain
004011CF FF7508 push [ebp+08]
004011D2 FF3503314000 push dword ptr [00403103]
* Reference To: USER32.CallNextHookEx, Ord:0014h
004011D8 E89D000000              Call 0040127A 
004011DD C9                      leave
004011DE C20C00                  ret 000C
004011E1 EB73                    jmp 00401256
* Referenced by a (U)nconditional or (C)onditional Jump at Address:004011C7(C)
004011E3 837D0800                cmp dword ptr [ebp+08], 00000000 ; Check again
004011E7 756D                    jne 00401256
004011E9 8B5510                  mov edx, dword ptr [ebp+10]
004011EC 817A0402010000          cmp dword ptr [edx+04], 00000102 ;WM_CHAR ?
004011F3 7561                    jne 00401256
004011F5 8B4208                  mov eax, dword ptr [edx+08] ; Move CHAR to eax
004011F8 8B4A08                  mov ecx, dword ptr [edx+08] ; Move CHAR to ecx

004011FB 2A0508314000            sub al, byte ptr [00403108] 
00401201 880D08314000            mov byte ptr [00403108], cl
00401207 D315FF304000            rcl dword ptr [004030FF], cl ; Start calculation
0040120D A008314000              mov al, byte ptr [00403108]
00401212 33C8                    xor ecx, eax
00401214 C1E007                  shl eax, 07
00401217 33C8                    xor ecx, eax
00401219 C1E007                  shl eax, 07
0040121C 33C8                    xor ecx, eax
0040121E C1E007                  shl eax, 07
00401221 33C8                    xor ecx, eax
00401223 C1E003                  shl eax, 03
00401226 33C8                    xor ecx, eax
00401228 310DFF304000            xor dword ptr [004030FF], ecx
0040122E FF35FF304000            push dword ptr [004030FF] ; Store hash

The next part, displays the hash in the captionbar (I think) Although I didn't see it (in XP) :(

... 0040126F C9 leave 00401270 C20C00 ret 000C

Ok, in the hook procedure all the CHARS are intercepted and the hash is further calculated every time you press a key.
If you want to know a valid serial, you have to write a bruter with the code from the hook routine. keep in mind that the hash is initialized BAD0DEADh. The hash result should be 02FB00F7h.

The second byte var, is not needed for the algo, so we can cut it out to speed up the bruting.
This is the hash calculation part out of the hook routine (a little optimized :)

        mov al, byte ptr [serial+edi]<br>
        rcl dword ptr [hash],al <br>
        shl eax, 7<br>
        xor ecx, eax<br>

        shl eax, 7<br>
        xor ecx ,eax<br>
        shl eax, 7<br>
        xor ecx, eax<br>
        shl eax, 3<br>
        xor ecx, eax<br>

        xor dword ptr [hash], ecx<br>
        inc edi </p>

The serial points to an address where you should store the combination currently bruting. edi is the counter.
Let it run till byte ptr [serial+edi] = 0
A little problem is that we can't know the keylength...if this crackme uses 10 chars and only lower cased chars, we have :

SUM (26^i) = 146.813.779.479.510 possibilities.
i 10

To put that in perspective, if you process 1 milion serials each second, you would still need 4.6 years :(
And then the serial could be longer, and we don't even know the range (1-9, a-z,A-Z,...) So feel free to brute it :)
Although the algorithm probably allows a lot of valid keys :)

=> Note from the author if you want to start bruting

It's only numbers, 0 - 9, and the length is 10.
If you get bored, try *******967 (the * you have to brute :-)


Now you should be able to brute it in little time...

Final notes

That's it. The advantage of getting the input through a hook, is that it's not buffered by windows (so no Hmemcpy, or translatemessage breaking to get at the right place!)
The disadvantage is that you cannot use backspace or del because they generate a keycode too... It requires extra coding to handle every keypress right.

If you have questions, or remarks about this tutorial, feel free to drop a comment here, or ask on the BiW forum.


Greetz to Basse for this nice crackme! Keep on sending them :)
and to CoDe_InSiDe for pointing out the 'foof bug' thanx :)