An Easy Windows Crackme

Posted on Sat 02 August 2014 in Reverse Engineering

Here I'm going to show you how to crack this crackme. We'll use some basic reversing techniques to figure out how it works and how to break or bypass its copy protection.

Some knowledge of IA-32 assembly would be beneficial to understand what is going on but I'll try to explain it in enough detail that you should be able to follow anyway.

Prerequisites

If you want to follow along the setup is:

Initial Look

After you download the zip file and look inside it you should seee this:

Drag the folder they are sitting in to the desktop and run SomeCrypto~01.exe by double clicking on it.

You should see a rather intimidating window with no explaination like this:

Try to put some junk input in the 2 fields but nothing happens:

Let's close this, it tells us nothing, and have a closer look at the binary itself.

One of the first things you should always look at is the imports section of the binary, it tells you what functions are being imported by the application from other libraries (.dll files).

This tells you a lot about the application and there are nearly always imports because the application has to communicate with the OS.

dumpbin is an application that comes with Microsoft Visual Studio Express 2013 for Windows Desktop and on my test machine resides in C:\Program Files\Microsoft Visual Studio 12\VC\bin\.

It can be used to look at some of the sections in PE executables.

Open up a command prompt with admin privileges:

And run the command dumpbin /imports "C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe".

The location of the crackme file might be different depending on what your local username is for Windows (mine is user) and if you extracted it to somewhere other than the desktop.

You should see an output like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Microsoft (R) COFF/PE Dumper Version 12.00.21005.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    KERNEL32.dll
                402018 Import Address Table
                4024F4 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                  215 GetModuleHandleA

    USER32.dll
                402020 Import Address Table
                4024FC Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                  20E MessageBoxA
                  121 GetDC
                  265 ReleaseDC
                  114 GetClientRect
                  2BB SetTimer
                   DC EndPaint
                   DA EndDialog
                  1EE LoadImageA
                  129 GetDlgItemTextA
                   AB DialogBoxParamA
                  28F SetDlgItemTextA
                    E BeginPaint

    GDI32.dll
                402000 Import Address Table
                4024DC Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                  1FB GetObjectA
                   13 BitBlt
                   E6 DeleteObject
                  277 SelectObject
                   30 CreateCompatibleDC

  Summary

        1000 .data
        1000 .rdata
       69000 .rsrc
        1000 .text

This shows us the functions that are being imported and which dll file each function is in.

The first thing that stands out to me is the call to MessageBoxA on line 25.

These challenges normally create a message box saying "Success" or something when you have done it so a call to this might be where in the application we want to get to.

And once we are there we should be able to trace back through the code to see where the check was done.

The second thing I notice is the call to GetDlgItemTextA on line 33.

This function looks like it could be responsible for getting our input, if this is true we could follow the code from that point and find the code that checks our input and the success code.

Digging A Little Deeper

We can ignore the rest for now and go straight to opening the application in OllyDBG. Open Olly with administrator privileges:

Click File->Open and choose the SomeCrypto~01.exe file:

You should then see this:

The big section in the top left is the main disassembly window, this shows the disassembly of the application from the entry point of the application (004012DE), this is where execution of the application starts and these are the CPU instructions that are going to run.

We can check this using dumpbin with the following command: dumpbin /headers "C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe" | find /i "entry point"

1
            12DE entry point (004012DE)

The columns are from left to right, the memory address of the instruction, the hex representation of the instruction, the ia-32 assembly representation and finally notes that Olly puts there for us.

The top right section contains the values of the CPU registers, these are used primarily as storage for the CPU while it is running instructions. There are a couple of special ones which I'll explain if I need to.

The format is: [CPU register name] [value] [Olly notes]

The bottom right section is the stack window and shows the current status of the stack, the stack is used to store function arguments and local variables as well as a few other things.

The columns are from left to right, memory address, 4 byte hex value at that memory address, Olly notes

The bottom left section is the dump window and can be used to dump certain bits of memory to see what is there.

The dump window has titles for its columns.

Let's see if there are any interesting strings in here, right click anywhere and click on Search for->All referenced text strings:

You should see the strings window with 4 entries:

The third entry down looks promising (Success).

Let's have a look where in the application this is, right click on it and click Follow in Disassembler:

You should see something like this:

As you can see the function call is to MessageBoxA, this is where we want to end up.

Just above the function call are the instructions that decide whether or not the bit of code that calls MessageBoxA is run (I've highlighted the relevant rows).

Understanding The Authentication Logic

This is basically just calling some internal function at 00401000 then using the return value to decide whether or not to jump to 004012CA. If the return value is 0 the jump happens.

This is the code at 004012CA:

1
2
3
4
5
6
7
004012CA  |> 5F                POP EDI
004012CB  |. 5E                POP ESI
004012CC  |. 33C0              XOR EAX,EAX
004012CE  |. 5B                POP EBX
004012CF  |. 8BE5              MOV ESP,EBP
004012D1  |. 5D                POP EBP
004012D2  \. C2 1000           RETN 10

It just exits so we don't want to end up here, this is the fail case.

Let's look inside this function to see what it does, right click on the function call (at 0040129D) and click Follow:

You should see this:

This is the function that decides if our name and/or serial are correct. Here is the full disassembly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
00401000   $ 55             PUSH EBP
00401001   . 8BEC           MOV EBP,ESP
00401003   . 8A01           MOV AL,BYTE PTR DS:[ECX]
00401005   . 83EC 20        SUB ESP,20
00401008   . 56             PUSH ESI
00401009   . 33F6           XOR ESI,ESI
0040100B   . 84C0           TEST AL,AL
0040100D   . 0F84 B3000000  JE SomeCryp.004010C6
00401013   . 8D55 E0        LEA EDX,DWORD PTR SS:[EBP-20]
00401016   . 2BD1           SUB EDX,ECX
00401018   > 3C 61          CMP AL,61
0040101A   . 0F8C A6000000  JL SomeCryp.004010C6
00401020   . 3C 7A          CMP AL,7A
00401022   . 0F8F 9E000000  JG SomeCryp.004010C6
00401028   . 88040A         MOV BYTE PTR DS:[EDX+ECX],AL
0040102B   . 8A41 01        MOV AL,BYTE PTR DS:[ECX+1]
0040102E   . 41             INC ECX
0040102F   . 46             INC ESI
00401030   . 84C0           TEST AL,AL
00401032   .^75 E4          JNZ SHORT SomeCryp.00401018
00401034   . 83FE 1A        CMP ESI,1A
00401037   . 0F85 89000000  JNZ SomeCryp.004010C6
0040103D   . 33C0           XOR EAX,EAX
0040103F   . 90             NOP
00401040   > 8A88 10304000  MOV CL,BYTE PTR DS:[EAX+403010]
00401046   . 8888 40314000  MOV BYTE PTR DS:[EAX+403140],CL
0040104C   . 40             INC EAX
0040104D   . 84C9           TEST CL,CL
0040104F   .^75 EF          JNZ SHORT SomeCryp.00401040
00401051   . 33C9           XOR ECX,ECX
00401053   . 380D 40314000  CMP BYTE PTR DS:[403140],CL
00401059   . 74 2D          JE SHORT SomeCryp.00401088
0040105B   . EB 03          JMP SHORT SomeCryp.00401060
0040105D     8D49 00        LEA ECX,DWORD PTR DS:[ECX]
00401060   > 8A81 40314000  MOV AL,BYTE PTR DS:[ECX+403140]
00401066   . 3C 61          CMP AL,61
00401068   . 7C 14          JL SHORT SomeCryp.0040107E
0040106A   . 3C 7A          CMP AL,7A
0040106C   . 7F 10          JG SHORT SomeCryp.0040107E
0040106E   . 0E             PUSH CS
0040106F   . BE C08A9405    MOV ESI,5948AC0
00401074   . 7F FF          JG SHORT SomeCryp.00401075
00401076     FF             DB FF
00401077     FF             DB FF
00401078   . 8891 40314000  MOV BYTE PTR DS:[ECX+403140],DL
0040107E   > 41             INC ECX
0040107F   . 80B9 40314000 >CMP BYTE PTR DS:[ECX+403140],0
00401086   .^75 D8          JNZ SHORT SomeCryp.00401060
00401088   > 83C8 FF        OR EAX,FFFFFFFF
0040108B   . BA 40314000    MOV EDX,SomeCryp.00403140
00401090   . 85C9           TEST ECX,ECX
00401092   . 74 19          JE SHORT SomeCryp.004010AD
00401094   > 0FB632         MOVZX ESI,BYTE PTR DS:[EDX]
00401097   . 33F0           XOR ESI,EAX
00401099   . 81E6 FF000000  AND ESI,0FF
0040109F   . C1E8 08        SHR EAX,8
004010A2   . 3304B5 5820400>XOR EAX,DWORD PTR DS:[ESI*4+402058]
004010A9   . 42             INC EDX
004010AA   . 49             DEC ECX
004010AB   .^75 E7          JNZ SHORT SomeCryp.00401094
004010AD   > F7D0           NOT EAX
004010AF   . 3D 18B291F8    CMP EAX,F891B218
004010B4   . 75 10          JNZ SHORT SomeCryp.004010C6
004010B6   . 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
004010B9   . C700 40314000  MOV DWORD PTR DS:[EAX],SomeCryp.00403140
004010BF   . B0 01          MOV AL,1
004010C1   . 5E             POP ESI
004010C2   . 8BE5           MOV ESP,EBP
004010C4   . 5D             POP EBP
004010C5   . C3             RETN
004010C6   > 32C0           XOR AL,AL
004010C8   . 5E             POP ESI
004010C9   . 8BE5           MOV ESP,EBP
004010CB   . 5D             POP EBP
004010CC   . C3             RETN

Here I will go over this code in detail and try to understand what it is doing.

First it starts with the function prologue:

1
2
00401000   $ 55             PUSH EBP
00401001   . 8BEC           MOV EBP,ESP

This is common among all stdcall and cdecl functions, it just sets up the stack frame (for more information about stack frames check out the "Stack Frames" section of my post on basic binary auditing).

The next instruction is interesting:

1
00401003   . 8A01           MOV AL,BYTE PTR DS:[ECX]

This is using the ECX register before anything has been done to it in this function. This means that whatever is stored in ECX was stored there in the calling function and it was passed as an argument to this current function.

The reason it was passed in a register instead of on the stack (how arguments are normally passed) is because the compiler knew it was in control of all points of entry into this function.

The most likely way that the compiler would have known this is if the function was explicitly defined with the static keyword. This means that only functions inside the same source file can call this function.

To figure out what is stored in ECX at this point in the application without running it, we will need to look back through the code that called this function, here is that code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
00401267  |. 8B3D 40204000     MOV EDI,DWORD PTR DS:[<&USER32.GetDlgItemTextA>]     ;  USER32.GetDlgItemTextA
0040126D  |. 6A 40             PUSH 40                                              ; /Count = 40 (64.)
0040126F  |. 8D8C24 C4000000   LEA ECX,DWORD PTR SS:[ESP+C4]                        ; |
00401276  |. 51                PUSH ECX                                             ; |Buffer
00401277  |. 68 E9030000       PUSH 3E9                                             ; |ControlID = 3E9 (1001.)
0040127C  |. 56                PUSH ESI                                             ; |hWnd
0040127D  |. FFD7              CALL EDI                                             ; \GetDlgItemTextA
0040127F  |. 6A 40             PUSH 40                                              ; /Count = 40 (64.)
00401281  |. 8D9424 84000000   LEA EDX,DWORD PTR SS:[ESP+84]                        ; |
00401288  |. 52                PUSH EDX                                             ; |Buffer
00401289  |. 68 EA030000       PUSH 3EA                                             ; |ControlID = 3EA (1002.)
0040128E  |. 56                PUSH ESI                                             ; |hWnd
0040128F  |. FFD7              CALL EDI                                             ; \GetDlgItemTextA
00401291  |. 8D4424 0C         LEA EAX,DWORD PTR SS:[ESP+C]
00401295  |. 50                PUSH EAX
00401296  |. 8D8C24 84000000   LEA ECX,DWORD PTR SS:[ESP+84]
0040129D  |. E8 5EFDFFFF       CALL SomeCryp.00401000

As you can see there are 2 calls to GetDlgItemTextA and we have 2 fields (Name and Serial). But at this time we don't know which field is which however we do have their ID's.

The prototype for GetDlgItemTextA is:

1
2
3
4
5
6
UINT WINAPI GetDlgItemText(
  _In_   HWND hDlg,
  _In_   int nIDDlgItem,
  _Out_  LPTSTR lpString,
  _In_   int nMaxCount
);

And from the manual page for it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
hDlg [in]
    Type: HWND
    A handle to the dialog box that contains the control.
nIDDlgItem [in]
    Type: int
    The identifier of the control whose title or text is to be retrieved.
lpString [out]
    Type: LPTSTR
    The buffer to receive the title or text.
nMaxCount [in]
    Type: int
    The maximum length, in characters, of the string to be copied to the buffer pointed to by lpString. If the length of the string, including the null character, exceeds the limit, the string is truncated.

So the second argument is the ID of the field and the third is the buffer that the text is going to be stored in.

This means that we are looking for the control with ID 3EA. Line 16 (on the disassembly of the 2 calls to GetDlgItemTextA above) shows that ECX is being loaded with the address of ESP+84, just before ESP+84 is loaded as the buffer argument to GetDlgItemTextA with an ID of 3EA.

If you remember back to when we used dumpbin to list all of the imported functions, there was also a function called SetDlgItemTextA being imported.

This function is likely used to set the values to "Enter you name..." and "Enter your serial...". We can use this to figure out which of these ID's (3E9 or 3EA) is the serial field and which is the name field; and ultimately which is being passed to our checking function in ECX.

We could use the strings window again and find out where "Enter you name..." and "Enter your serial..." are referenced but I'll show you a different way to find them using the function name (SetDlgItemTextA).

First close OllyDBG, open it again and open the crackme again so that you get to this point again:

Right click anywhere and click Search for->All intermodular calls:

After that, this window should pop up:

You can see the second and third entries are calls to SetDlgItemTextA, looking at the addresses on the left these calls are right next to each other.

Right click on 1 of them and click Follow in Disassembler or press Enter:

You should see this:

Here is the disassembly of these calls:

1
2
3
4
5
6
7
8
9
00401116  |. 8B3D 48204000  MOV EDI,DWORD PTR DS:[<&USER32.SetDlgIte>;  USER32.SetDlgItemTextA
0040111C  |. 68 60244000    PUSH SomeCryp.00402460                   ; /Text = "Enter your name..."
00401121  |. 68 E9030000    PUSH 3E9                                 ; |ControlID = 3E9 (1001.)
00401126  |. 56             PUSH ESI                                 ; |hWnd
00401127  |. FFD7           CALL EDI                                 ; \SetDlgItemTextA
00401129  |. 68 74244000    PUSH SomeCryp.00402474                   ; /Text = "Enter your serial..."
0040112E  |. 68 EA030000    PUSH 3EA                                 ; |ControlID = 3EA (1002.)
00401133  |. 56             PUSH ESI                                 ; |hWnd
00401134  |. FFD7           CALL EDI                                 ; \SetDlgItemTextA

The prototype for this function is:

1
2
3
4
5
BOOL WINAPI SetDlgItemText(
  _In_  HWND hDlg,
  _In_  int nIDDlgItem,
  _In_  LPCTSTR lpString
);

Looking at this its obvious that the control with ID 3EA is the serial number field because it is being set to "Enter your serial...".

We can verify this by setting a breakpoint at the top of the serial checking function, running the application and checking the value of the ECX register.

First go to the check function again:

Then right click on the top instruction (at address 00401000) and click Breakpoint->Toggle or press F2:

You should then see the background of the address section (on the far left) turn red:

Then run the application by clicking Debug->Run or pressing F9:

You should see this after the breakpoint is hit (it shouldn't take long for the breakpoint to hit):

As you can see in the registers window (in the top right), the value of ECX is the address that contains the string "Enter your serial..." so our static analysis of the code was correct.

Now we can get back to analysing the code in this serial checking function.

The following is the start of the function excluding the prologue:

1
2
3
4
5
6
7
8
00401003   . 8A01           MOV AL,BYTE PTR DS:[ECX]
00401005   . 83EC 20        SUB ESP,20
00401008   . 56             PUSH ESI
00401009   . 33F6           XOR ESI,ESI
0040100B   . 84C0           TEST AL,AL
0040100D   . 0F84 B3000000  JE SomeCryp.004010C6
00401013   . 8D55 E0        LEA EDX,DWORD PTR SS:[EBP-20]
00401016   . 2BD1           SUB EDX,ECX

The first line loads the first byte of our serial into the AL register (The lower byte of the EAX register).

Some space is then reserved on the stack for a local variable. On line 3 the value of the ESI register is saved on the stack and zero'ed out (xor'ing anything with itself makes the result 0).

The byte in the AL register (at this point in time the first character in our serial) is checked for 0 on line 5 and if it is 0 execution jumps to 004010C6.

Let's look at the code at 004010C6:

1
2
3
4
5
004010C6   > 32C0           XOR AL,AL
004010C8   . 5E             POP ESI
004010C9   . 8BE5           MOV ESP,EBP
004010CB   . 5D             POP EBP
004010CC   . C3             RETN

This clearly just sets the return value to 0 and returns, we already know that we don't want a return value of 0 so this is our failure case.

Following the jump we have an LEA instruction, which loads the value of our local variable, and a SUB command, which calculates the distance from the local variable to where our serial is in memory.

The result of these 2 instructions (the distance from the local variable to where our serial is in memory) is stored in the EDX register.

Next we have the following loop:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
00401018   > 3C 61          CMP AL,61
0040101A   . 0F8C A6000000  JL SomeCryp.004010C6
00401020   . 3C 7A          CMP AL,7A
00401022   . 0F8F 9E000000  JG SomeCryp.004010C6
00401028   . 88040A         MOV BYTE PTR DS:[EDX+ECX],AL
0040102B   . 8A41 01        MOV AL,BYTE PTR DS:[ECX+1]
0040102E   . 41             INC ECX
0040102F   . 46             INC ESI
00401030   . 84C0           TEST AL,AL
00401032   .^75 E4          JNZ SHORT SomeCryp.00401018

This is checking if the value in AL is below 61 (lines 1 and 2) or above 7A (lines 3 and 4) and jumping to 004010C6 if it is.

Looking at the ascii table 61 is a and 7A is z.

So if the first character is not a lowercase letter, execution will jump to the same failure case as before.

If the jumps aren't taken the byte is moved to the address pointed to by EDX+ECX on line 5. This will point to the right position in the local variable due to the earlier SUB command.

Then (on line 6) the next byte in the serial is moved into AL, both ECX and ESI are incremented. Lastly, on lines 9 and 10, AL is checked for 0 and the jump to 00401018 is only taken if AL is 0.

This is clearly just making sure only lowercase letters are part of the serial, so at least we now know the possible different characters that are allowed in the serial.

Another thing to notice here is that ESI is being used as a counter and at the end will contain the number of characters in the serial.

Let's look at the 2 lines following this loop:

1
2
00401034   . 83FE 1A        CMP ESI,1A
00401037   . 0F85 89000000  JNZ SomeCryp.004010C6

If you'll remember, ESI contains the number of characters in the serial and here its being checked against 1A (or 26 in decimal). If ESI isn't equal to 26 then our failure case is taken again (004010C6).

We then zero out EAX and onto the next loop:

1
2
3
4
5
00401040   > 8A88 10304000  MOV CL,BYTE PTR DS:[EAX+403010]
00401046   . 8888 40314000  MOV BYTE PTR DS:[EAX+403140],CL
0040104C   . 40             INC EAX
0040104D   . 84C9           TEST CL,CL
0040104F   .^75 EF          JNZ SHORT SomeCryp.00401040

This is simply moving a string from 403010 to 403140 and only stops once it hits a 0.

The data at 403010 we can see by right clicking on the line (line 1 here) and click Follow in Dump -> Address constant:

You should see this:

It will show the following in the dump window:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
00403010  49 78 20 6C 7A 63 74 75  Ix lzctu
00403018  73 64 7A 65 74 67 63 2C  sdzetgc,
00403020  20 65 78 20 6E 2D 66 73   ex n-fs
00403028  62 20 28 6E 76 66 6E 75  b (nvfnu
00403030  6A 75 76 75 6A 73 78 2D  juvujsx-
00403038  66 73 62 29 20 6A 6E 20  fsb) jn
00403040  65 20 66 65 6E 6A 6C 20  e fenjl
00403048  6C 73 61 74 73 78 72 78  lsatsxrx
00403050  75 20 73 77 20 6E 63 61  u sw nca
00403058  61 72 75 7A 6A 6C 20 71  aruzjl q
00403060  72 63 20 65 68 64 73 7A  rc ehdsz
00403068  6A 75 67 61 6E 20 70 67  jugan pg
00403070  6A 6C 67 20 74 72 7A 77  jlg trzw
00403078  73 7A 61 6E 20 6E 76 66  szan nvf
00403080  6E 75 6A 75 76 75 6A 73  nujuvujs
00403088  78 2E 20 49 78 20 66 68  x. Ix fh
00403090  73 6C 71 20 6C 6A 74 67  slq ljtg
00403098  72 7A 6E 2C 20 75 67 72  rzn, ugr
004030A0  63 20 65 7A 72 20 75 63  c ezr uc
004030A8  74 6A 6C 65 68 68 63 20  tjlehhc
004030B0  76 6E 72 6D 20 75 73 20  vnrm us
004030B8  73 66 6E 6C 76 7A 72 20  sfnlvzr
004030C0  75 67 72 20 7A 72 68 65  ugr zrhe
004030C8  75 6A 73 78 6E 67 6A 74  ujsxngjt
004030D0  20 66 72 75 70 72 72 78   fruprrx
004030D8  20 75 67 72 20 71 72 63   ugr qrc
004030E0  20 65 78 6D 20 75 67 72   exm ugr
004030E8  20 6C 6A 74 67 72 7A 75   ljtgrzu
004030F0  72 62 75 2E 00           rbu..

This is everything before and including the first 0.

Once this loop has completed we have the following:

1
2
3
4
00401051   . 33C9           XOR ECX,ECX
00401053   . 380D 40314000  CMP BYTE PTR DS:[403140],CL
00401059   . 74 2D          JE SHORT SomeCryp.00401088
0040105B   . EB 03          JMP SHORT SomeCryp.00401060

This zero's out ECX and checks the value at 403140 against CL (0) and if they match jumps to 00401088, otherwise jumps to 00401060.

Let's see what happens if you jump on line 3 is taken:

1
2
3
4
00401088   > 83C8 FF        OR EAX,FFFFFFFF
0040108B   . BA 40314000    MOV EDX,SomeCryp.00403140
00401090   . 85C9           TEST ECX,ECX
00401092   . 74 19          JE SHORT SomeCryp.004010AD

Another jump is taken if ECX is 0 and we know it will at this point. Here is the code at that location:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
004010AD   > F7D0           NOT EAX
004010AF   . 3D 18B291F8    CMP EAX,F891B218
004010B4   . 75 10          JNZ SHORT SomeCryp.004010C6
004010B6   . 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
004010B9   . C700 40314000  MOV DWORD PTR DS:[EAX],SomeCryp.00403140
004010BF   . B0 01          MOV AL,1
004010C1   . 5E             POP ESI
004010C2   . 8BE5           MOV ESP,EBP
004010C4   . 5D             POP EBP
004010C5   . C3             RETN

This is the end of the function.

Its pretty obvious that this function creates a checksum of a modified version of the string we found earlier and checks it against F891B218, if it is equal the function returns 1, otherwise it return 0.

At this point I remember that there are no rules to this crackme so patching is allowed.

Exit OllyDBG completely and make a copy of the application like this:

This isn't needed, I just do it to be careful.

Ope the copy and go to the serial checking function and the line under CMP AL,61, where it says JL SomeCryp.004010C6 at 0040101A, double click, type je 4010A2 and click Assemble.

The little window should have stayed open, type jmp 4010C6 and click Assemble again.

You should see the following:

This should check if the first character in the serial is a and if it is jump to 4010A2, otherwise jump to 4010C6 which is the failure case.

Click Cancel and scroll down the the memory address 4010A2. Double click there, type mov eax, 0xf891b218 and click Assemble.

You should see this:

Now just fill the rest with NOP's until the cmp at 004010AF like this:

This should ensure that if the serial contains an a at the start it should set the value of EAX accordingly.

Save these modifications to the application file by right clicking anywhere and clicking Copy to exe->All modifications:

It should open this window:

Click Copy all and you should see something like this:

Click the close button in the top right corner of this window and you should get the following dialog:

Now if you browse to the directory with the crackme files in you should see a new file:

The file ending in .bak is the backup created by Olly, and the 1 named SomeCrypto~01 - Copy.exe is our patched file.

Just run it and put anything as the name and any serial starting with an a:

CRACKED!!! :-)

Conclusion

You don't need to fully understand every part of an application while reverse engineering it, it depends on what you are trying to achieve and the complexity of the application.

Try to concentrate as much as possible on the important areas and ignore everything else.

When beating a protection mechanism sometimes its easiest to just bypass the protection as opposed to trying to break it.

Further Reading

The best book I've read on this topic is Reversing: Secrets of Reverse Engineering by Eldad Eilam.

Happy Hacking :-)