Contribute  :  Web Resources  :  Past Polls  :  Site Statistics  :  Downloads  :  Forum  
    BiW ReversingThe challenge is yours    
 Welcome to BiW Reversing
 Sunday, March 29 2020 @ 07:27 PM CEST

solution for detten's crackme7 (some anti-debug tricks)


TutorialsLevel : newbie

A simple target to keygen, but beware the anti-debugging tricks ;)
Includes explanation of all anti-debugging checks, and sourcecode for keygen in C.


solution for detten's crackme7 - tutorial by haggar



Detten has made small and not hard crackme to solve, but still very interesting. Our objective is to avoid any kind of anti-debug staff and to dig-up serial for our name. So let's begin.
Crackme here


- OllyDbg 1.10
- paper and pencile, some time and a will to do litlle Googling.


2.1 First problem

Ok, first thing that happened to me is that crackme crashed while starting. Is this a bug or intended? Maybe both? That happend on my Windows XP Pro SP1, but I don't know for other systems. So lets open crackme in Olly and see what we have here. Right at the beginning you will see the reason for crushing:

00401000 >/$ B4 43 MOV AH,43
00401002 |. CD 68 INT 68
00401004 |. 66:3D 86F3 CMP AX,0F386
00401008 |. 74 2D JE SHORT crackme7.00401037

So right at the second line we have INT 68 interrupt and that is why crackme crashes. Let we try to analyze what is going on:
- first, to AH register is given 43 value
- then it's executed INT 68
- imagine that crackme doesn't crash at above inerrupt, but continues. Code is checking does AX contains now 0F386 value.
- if AX=0F386, conditional jump at 401008 will be executed and we will jump at 401037 offset where we can see message "Please unload debugger :("

So this is some check for debugger and crackme shouldn't crash but to work properly, or show that message if we run it in Olly. Since I never sow this trick before, I just used Google to find some info about INT 68 interrupt. I didn't find much, just some references that I don't quite understand, but one of listed links was to some Yates tutorial. To not make long story out of this (check yor self that article), this is SoftICE check. Crackme crashes because this trick works only on 9x systems, but in case that we have SI installed on our computer, INT 68 should return 0F386 to EAX, we should get error message and crackme should unload itself. If SI isn't installed, crackme should just procede to GetModuleHandle API and run normally. To solve this issue, just fill with NOP's all opcodes from 401000 to 401009 and save changes to crackme:

00401000 > $ 90 NOP
00401001 . 90 NOP
00401002 . 90 NOP
00401003 . 90 NOP
00401004 . 90 NOP
00401005 . 90 NOP
00401006 . 90 NOP
00401007 . 90 NOP
00401008 . 90 NOP
00401009 . 90 NOP
0040100A . 6A 00 PUSH 0 ; /pModule = NULL
0040100C . E8 A5020000 CALL <JMP.&KERNEL32.GetModuleHandleA> ; GetModuleHandleA

Now run crackme and you'll see that it works fine.

2.2 Junk code

Next thing to notice is that crackme has some small junky opcodes, mostly jumps like this one:

00401016 . EB 02 JMP SHORT crackme7.0040101A
00401018 68 DB 68 ; CHAR 'h'
00401019 A1 DB A1
0040101A > 6A 00 PUSH 0 ; /lParam = NULL
0040101C . 68 50104000 PUSH crackme7.00401050 ; |DlgProc = crackme7.00401050
00401021 . 6A 00 PUSH 0 ; |hOwner = NULL
00401023 . 68 0C304000 PUSH crackme7.0040300C ; |pTemplate = "MyDialog"
00401028 . FF35 C4314000 PUSH DWORD PTR DS:[4031C4] ; |hInst = NULL
0040102E . E8 65020000 CALL <JMP.&USER32.DialogBoxParamA> ; DialogBoxParamA

You see, jump at 401016 jumps above two bytes at 401018 and 401019. Those bytes will NEVER be executed and those jumps+bytes had acctually no purpose to our program. They are there just to confuse reverser and made reading of code a bit dificult. Most today's protectors have such code, only that they have realy harder patterns. What we can do is to patch those junky bytes with NOP's just to make code litlle cleaner, it's not acctually necesery but I like it that way. There is 11 such locations to patch (some jumps+bytes, or just bytes), so do it (if you want) and save changes to crackme.

2.3 Breakpoints directly on API's - check

Next check is common check in today's protectors too. It's checking did we put bp directly on some API function. I don't know how SoftICE is working with breakpoints, I assume that it's identicall as Olly, but I will explain this part on Olly. Also, you must have Windows XP for this kind of breakpoints if using Olly, because only XP is alowing seting bp directly on dll's.

Open "Executable Modules" window in Olly (Alt+E), left click select our crackme, right click on it and "view names". There you have for example this API which our crackme uses to get input from window:

00402014 .rdata Import USER32.GetDlgItemTextA

If you right click on it, you see option "Toggle breakpoint on import". That option will not put bp in our crackme, but in USER32.DLL on address where GetDlgItemTextA API function starts. This is very usefull option so select it. Now run crackme enter name haggar and serial 12345 and click check button. Boom! You get "Please unload debugger :(" message and crackme exits.

Let me explain you what happened, but first set same bp again on same API. Now open again "Executable Modules", but this time click on USER32.DLL and "view names". Find our API:

77D665FE .text Export GetDlgItemTextA

If you placed toggle bp on that import, then 77D665FE address should be marked with red colour now (maybe 77D665FE will be different on your system). Double click on that address and you'll find your self in user32.dll right where GetDlgItemTextA API starts:

77D665FE > 55 PUSH EBP
77D66604 FF75 08 PUSH DWORD PTR SS:[EBP+8]
77D66607 E8 BF1AFEFF CALL USER32.GetDlgItem
77D6660C 85C0 TEST EAX,EAX
77D6660E 0F84 162C0200 JE USER32.77D8922A
77D66614 FF75 14 PUSH DWORD PTR SS:[EBP+14]
77D66617 FF75 10 PUSH DWORD PTR SS:[EBP+10]
77D6661A 50 PUSH EAX
77D6661B E8 DB19FEFF CALL USER32.GetWindowTextA
77D66620 5D POP EBP
77D66621 C2 1000 RETN 10

First line should be red and that means that Olly had put bp on that address.

How Olly sets breakpoints? What is breakpoint at all? Olly uses INT3 = CC (hex) interrupt to put bp (probably others debuggers too), so Olly substitutes byte at 77D665FE (which is original 55) with CC in memory. When program reaches CC byte, it stops and windows give control to debugger. So altough you see 55 in Olly, actualy in memory is on that place CC.

So our crackme somehow checks did we placed bp on some API functions. How? Reastart crackme in Olly and find this place:

00401124 . 68 97314000 PUSH crackme7.00403197 ; /ProcNameOrOrdinal = "GetDlgItemTextA"
00401129 . FF35 30324000 PUSH DWORD PTR DS:[403230] ; |hModule = NULL
0040112F . E8 88010000 CALL <JMP.&KERNEL32.GetProcAddress> ; GetProcAddress
00401134 . 8038 CC CMP BYTE PTR DS:[EAX],0CC
00401137 . 75 05 JNZ SHORT crackme7.0040113E
00401139 .^E9 F9FEFFFF JMP crackme7.00401037

You see, crackme will call GetProcAddress APA. That API will return address of "GetDlgItemTextA" API function to EAX register. Then, at line 401134, it will compare BYTE value at [EAX] address (our API) with CC. If Olly has placed bp on our API, on this place will be then CC byte value (which is INT3=our breakpoint) and crackme will know that so it will procede to JMP crackme7.00401037 that throw us to BadBoy message. Check others usable API's like, GetDlgItemInt and MessageBoxA, and you'll see that crackme checks them too.

There is one more interesting thing here: check where bad jump JMP crackme7.00401037 is throwing us:

00401035 . 68 056A0068 PUSH 68006A05
0040103A . 15 30400068 ADC EAX,68004030 ; |
0040103F . 76 31 JBE SHORT crackme7.00401072 ; |
00401041 . 40 INC EAX ; |
00401042 . 006A 00 ADD BYTE PTR DS:[EDX],CH ; |
00401045 . E8 60020000 CALL <JMP.&USER32.MessageBoxA> ; MessageBoxA
0040104A > 50 PUSH EAX ; /ExitCode
0040104B . E8 60020000 CALL <JMP.&KERNEL32.ExitProcess> ; ExitProcess

As you can see, there are no address 401037 ?!? Ansver is simple and it's called "obfuscation". That is one more trick that modern protectors use, of course in much bigger rate. First two bytes at 401035 are usles and they will never be executed too, so replace them with 9090 so you can get clear picture:

00401035 90 NOP
00401036 90 NOP
00401037 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401039 . 68 15304000 PUSH crackme7.00403015 ; |Title = "Crackme 7"
0040103E . 68 76314000 PUSH crackme7.00403176 ; |Text = "Please unload debugger :("
00401043 . 6A 00 PUSH 0 ; |hOwner = NULL
00401045 . E8 60020000 CALL <JMP.&USER32.MessageBoxA> ; MessageBoxA
0040104A > 50 PUSH EAX ; /ExitCode
0040104B . E8 60020000 CALL <JMP.&KERNEL32.ExitProcess> ; ExitProcess

Is this better ;)?

There is simple way to bypass this checks. You can set in Command Line plugin "bp GetDlgItemTextA+1" breakpoint. That will set bp on 77D665FE+1=77D665FF address in memory, ie. on second instruction in API.

2.4 Breakpoints in crackme - check

If you set bp in crackme, for example, on this line

0040123C > 6A 64 PUSH 64 ; /Count = 64 (100.)
0040123E . 68 CC314000 PUSH crackme7.004031CC ; |Buffer = crackme7.004031CC
00401243 . 68 BB0B0000 PUSH 0BBB ; |ControlID = BBB (3003.)
00401248 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
0040124B . E8 54000000 CALL <JMP.&USER32.GetDlgItemTextA> ; GetDlgItemTextA

you will also get bad message when you enter name/serial and press check button. Reason is here:

004010DD . 60 PUSHAD
004010DE . BF 11114000 MOV EDI,crackme7.00401111
004010E3 . B9 92124000 MOV ECX,<JMP.&USER32.DestroyWindow> ; Entry address
004010E8 . 2BCF SUB ECX,EDI
004010EA . B0 CC MOV AL,0CC
004010EE . 75 0F JNZ SHORT crackme7.004010FF
004010F0 . FF05 00304000 INC DWORD PTR DS:[403000]
004010F6 . B9 92124000 MOV ECX,<JMP.&USER32.DestroyWindow> ; Entry address
004010FD .^EB ED JMP SHORT crackme7.004010EC
004010FF > 833D 00304000 >CMP DWORD PTR DS:[403000],5
00401106 . 74 05 JE SHORT crackme7.0040110D
00401108 .^E9 2AFFFFFF JMP crackme7.00401037

This algo will count how many there are CC bytes from 401111 to 401292 address and if there are different number of them than 5, it will jump at JMP crackme7.00401037 bad massage. That means, that in code between those addresses there are 5 CC bytes by default. Their purpose is not important to us (3 of them are parts of that breakpoints checks, 2 are used in some addresses), but if we place bp in that range, this algo will find it and count it, so final number will be bigger than 5 and crackme will find that something is wrong. To trick this check, just patch last bad boy jump JMP crackme7.00401037 with NOP's.

There is one more debugger check, but we will meet it while traing to find serial number, so we are going to keygening now.


Crackme has two well known API's which uses to get informatin from user: GetDlgItemTextA and GetDlgItemInt.

GetDlgItemTextA - this one will take our name from window.

GetDlgItemInt - this one will take our serial. This API purpose is to retrieve numeric value from window so our serial must have only decimal digits. That API will return that number to EAX register. Also, if numeric value is greater that 4294967295 (that is in hex FFFFFFFF, one double word - size of one register) , to EAX register will be returned zero value. Conclusion is that serial must be some numeric value betveen 0 and 4294967295.

Address 40123C is place where crackme reads serial and name so place bp on 40123C, run crackme, enter haggar/12345 for name/serial and press check button. Olly will stop on our bp and now we can analyze what crackme is doing:

00401261 MOV DWORD PTR DS:[403008],EAX ; EAX=3039(hex)=12345(dec) - our serial number is placed in memory
00401266 XOR ECX,ECX
00401268 MOV ESI,0DEE1 ; To ESI is given some initial value.
0040126D XOR EAX,EAX
0040126F MOV AL,BYTE PTR DS:[ECX+4031CC] ; AL= char from our name which is placed on 4031CC address.
00401275 OR AL,AL
00401277 JNZ SHORT crackme7.0040128A ; If AL<>0 it will jump to some calculations below.
00401279 OR ECX,ECX
0040127B JNZ SHORT crackme7.0040127F ; In case that we didn't enter name,
0040127D JMP SHORT crackme7.00401215 crackme will show no message box.
0040127F MOV DWORD PTR DS:[403004],ESI ; Placing calculated value to this address.
00401285 JMP crackme7.00401181 ; After algo is over, it jumps to last debug check.
0040128A ADD ESI,EAX ; It will add char value to initial value in ESI (some sum).
0040128C INC CL ; CL is counter of characters.
0040128E XOR ESI,EAX ; summ=summ xor char
00401290 JMP SHORT crackme7.0040126F ; Loop again.

Algorithm is:

1. ESI= 0DEE1
2. ESI= (ESI + character) XOR character
3. Loop to 2. for all name characters.

This is the first part of algo. After all characters are procesed, algo is continuing at 401181:

00401181 PUSH crackme7.00403244 ; /pSystemTime = crackme7.00403244
00401186 CALL <JMP.&KERNEL32.GetSystemTime> ; GetSystemTime
0040118B MOV EAX,crackme7.00403252
00401193 MOV EAX,crackme7.00403242
0040119B SUB BX,CX
0040119E CMP BX,5
004011A2 JBE SHORT crackme7.004011BE
004011A4 MOV EBX,DWORD PTR DS:[403008]
004011B0 JNZ SHORT crackme7.004011B7
004011B2 MOV EAX,1
004011B7 JMP crackme7.00401037

This is the last anti-debug check and I'll explain it after serial, so for now just pass it, be sure to execute JBE at 4011A2 or you will fail to pass this check. You will jump at second part of algo:

004011BE MOV EAX,DWORD PTR DS:[403008] ; EAX=my serial 12345, only in hex form 3039h
004011C3 SHL EAX,4 ; EAX=EAX*10
004011CB ROL EAX,2 ; EAX=EAX ROL 2
004011CE CMP EAX,DWORD PTR DS:[403004] ; compare EAX with that value calculated from name
004011D4 JNZ SHORT crackme7.004011DD ; If values are not equal, jump to bad boy
004011D6 MOV EAX,1
004011DB JMP SHORT crackme7.004011DF ; else jump/procede to good one
004011DF CMP AL,1
004011E1 JNZ SHORT crackme7.004011FE

Crackme calculates some sums from name and serial,then it compares them and if they are equal, we have good serial. How we going to get right serial? We have two choices; to reverse it, or to brute it. Both are very easy, but for some names serial numbers doesn't exist. That includes my nickname "haggar". Now, let we see how keygen algorithm should go:

[1] First we take name algo:

1. x= 0DEE1
2. x= (x + character) XOR character
3. Loop to 2. for all name characters.

[2] Then we reverse serial algo:

4. x= x ROR 2
5. x= x - 0DEAD
6. x= x/10
7. x= serial in hex, just transform it to decimal.

That's it! x is size of one register.

This is C++ source for simple keygen. If serial given by this kegen doesn't work, that's because serial doesn't exist for that name.

using namespace std;
int main()
unsigned long int i,l,x,y;
char Name[200];
cout<<" User name: ";
for(i=0;i<l;i++){ x=(x+Name[i])^Name[i]; }
cout<<" User code: "<<x<<"nn ";
return 0;

And finally, we have to analyze last anti-debug check, but let this be for your homework. I will just give you a tip: crackme checks time before taking user information from dialog box, it calculate ESI value from our name, then again checks time and calculates how big is difference, and if it's bigger than he expects, it will find out that somebody has been checking it's code.

That's all from me.

-=[ haggar , 10.05.2005 ]=-

What's Related

Story Options

solution for detten's crackme7 (some anti-debug tricks) | 3 comments | Create New Account
The following comments are owned by whomever posted them. This site is not responsible for what they say.
solution for detten's crackme7 (some anti-debug tricks)
Authored by: detten on Tuesday, May 10 2005 @ 06:51 PM CEST

Very nice solution, good explanation :)
Next check is common check in today's protectors too
I really was revolutionary in the old days :D
I think I wrote this crackme in the year 1999-2000. Aspack, UPX and vbox were all I knew and they didn't use that trick yet then :)

/me feels old all of a sudden :p
solution for detten's crackme7 (some anti-debug tricks)
Authored by: thorpe on Wednesday, May 11 2005 @ 02:27 AM CEST
First off awesome tutorial! Secondly, I have to say detten has made some awesome crackmes which incorporate multiple protections and techniques - It's almost as if each builds upon the other and you really gain a perspective on cracking when you have done most of them.

Hats off to you both
 Copyright © 2020 BiW Reversing
 All trademarks and copyrights on this page are owned by their respective owners.
Powered By Geeklog 
Created this page in 0.80 seconds