Crackme for newbies #1 - by woody^drn Here is a crackme made in c++, I've compiled the program, so you can see some of the variables ... thought it might be helpfull for some of the newbies around. This time I'll try to show you how file checking and reading is done. Lets start the program ... it says "u n r e g i s t e r e d ! !" .. Well lets load it into w32dasm. There are 3 places where it writes unregistered: * Possible StringData Ref from Data Obj ->"U N R E G I S T E R E D ! !" | :004104A9 68D7004200 push 004200D7 and ... * Possible StringData Ref from Data Obj ->"U N R E G I S T E R E D ! !" | :0041054C 68F4004200 push 004200F4 and the last ... * Possible StringData Ref from Data Obj ->"U N R E G I S T E R E D ! !" | :0041059B 6811014200 push 00420111 Lets check the first place where it will write the string. :00410483 6A20 push 00000020 * Possible StringData Ref from Data Obj ->"register.dat" | :00410485 68CA004200 push 004200CA :0041048A 6A00 push 00000000 :0041048C 8D8570FFFFFF lea eax, dword ptr [ebp+FFFFFF70] :00410492 50 push eax * Reference To: cw3215.ifstream::ifstream(const char*,int,int),Ord:0000h | :00410493 E8F4050000 Call 00410A8C :00410498 83C414 add esp, 00000014 :0041049B 8B8570FFFFFF mov eax, dword ptr [ebp+FFFFFF70] :004104A1 F6400C86 test [eax+0C], 86 :004104A5 7433 je 004104DA :004104A7 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"U N R E G I S T E R E D ! !" Okay .. it looks for a file called "register.dat", then it call the command ifstream(). But just before that it "lea eax, dword ptr ..." and pushes eax. It's just like a messageboxa, it pushes all the variable and strings it needs to open the file. Then it check if the file is there, test [eax+0c],86 ... if the file isn't there eax+0c will not be 86h. Lets create the file .. Okay .. the file is there now, so lets see whats on 4104DA: :004104DA 6A0A push 0000000A :004104DC 6A13 push 00000013 :004104DE 8D853CFFFFFF lea eax, dword ptr [ebp+FFFFFF3C] :004104E4 50 push eax :004104E5 8D45B4 lea eax, dword ptr [ebp-4C] :004104E8 50 push eax * Reference To: cw3215.istream::getline(char*,int,char), Ord:0000h | :004104E9 E8B6050000 Call 00410AA4 :004104EE 83C410 add esp, 00000010 :004104F1 6A0A push 0000000A :004104F3 6A13 push 00000013 :004104F5 8D8528FFFFFF lea eax, dword ptr [ebp+FFFFFF28] :004104FB 50 push eax :004104FC 8D45B4 lea eax, dword ptr [ebp-4C] :004104FF 50 push eax * Reference To: cw3215.istream::getline(char*,int,char), Ord:0000h | :00410500 E89F050000 Call 00410AA4 :00410505 83C410 add esp, 00000010 :00410508 33DB xor ebx, ebx :0041050A EB17 jmp 00410523 It reads two lines from the file, the first line is the name and the second is the serial for the name. well lets write two lines in the file, I write woody and 111666111. First it puts the text in line 1 into [ebp+FFFFFF3C], and the second line into [ebp+FFFFFF28]. When it has read those two lines, it will jump to 410523. :00410523 8D853CFFFFFF lea eax, dword ptr [ebp+FFFFFF3C] :00410529 50 push eax * Reference To: cw3215._strlen, Ord:0000h | :0041052A E833050000 Call 00410A62 :0041052F 59 pop ecx :00410530 3BC3 cmp eax, ebx :00410532 73D8 jnb 0041050C :00410534 33F6 xor esi, esi :00410536 33DB xor ebx, ebx :00410538 EB4B jmp 00410585 Now it will load the first line into eax, and the push the text in line 1. Then it calls strlen which is a command that will see how many characters there are in the line (eax). It xored ebx just before it jumped here, so now it will compare eax with ebx. My first line was woody, and it has 5 chars, so eax will be 4 (it starts with 0 .. 0,1,2,3 ,4 .. that's five chars .....!). After comparing those two numbers it will jump if eax is higher than ebx (jnb jump if not below), so the program will jump here. Lets check whats there at 41050C: :0041050C 0FBE841D3CFFFFFF movsx eax, byte ptr [ebp+ebx-000000C4] :00410514 50 push eax :00410515 E85AFBFFFF call 00410074 :0041051A 59 pop ecx :0041051B 88841D14FFFFFF mov byte ptr [ebp+ebx-000000EC], al :00410522 43 inc ebx Here it moves the char from the first line into eax, it's ebp+ebx-0c4, and ebx is zero now. Then it calls 410074, lets check what it does: :00410074 55 push ebp :00410075 8BEC mov ebp, esp :00410077 83C4F0 add esp, FFFFFFF0 :0041007A 53 push ebx :0041007B 56 push esi :0041007C 57 push edi :0041007D 8B5508 mov edx, dword ptr [ebp+08] :00410080 B848024200 mov eax, 00420248 :00410085 BB40024200 mov ebx, 00420240 * Possible StringData Ref from Data Obj ->"0123456789ABCDEF0123456701" | :0041008A BE70004200 mov esi, 00420070 This is kinda hard to explain, but when you execute the call, it just takes the pushed char and makes the hex value of it. Like my name woody is 77 6F 6F 64 79, but this time is only the first char (w) and that is 77. The call returns the hex value in eax, more precise al. So after that it moves al into ebp+ebx-0ec at line 41051B, and then increses ebx (next char) .. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0041050A(U) | :00410523 8D853CFFFFFF lea eax, dword ptr [ebp+FFFFFF3C] :00410529 50 push eax * Reference To: cw3215._strlen, Ord:0000h | :0041052A E833050000 Call 00410A62 :0041052F 59 pop ecx :00410530 3BC3 cmp eax, ebx :00410532 73D8 jnb 0041050C :00410534 33F6 xor esi, esi :00410536 33DB xor ebx, ebx :00410538 EB4B jmp 00410585 Now we're back to the strlen again, it moves the first line into eax, and compares the length of line 1 to ebx, ebx is now 1 .. but my first line is 5 bytes long (4 ebx). So it will jump to 41050C again. It will do this until ebx is equal to the length of line 1. After that it xors esi and ebx. So now we have our name in hex at ebp+ebx-0ec. It's finished with this routine, so it jumps to 410585. Lets check that out: :00410585 8D8528FFFFFF lea eax, dword ptr [ebp+FFFFFF28] :0041058B 50 push eax * Reference To: cw3215._strlen, Ord:0000h | :0041058C E8D1040000 Call 00410A62 :00410591 59 pop ecx :00410592 3BC3 cmp eax, ebx :00410594 73A4 jnb 0041053A :00410596 4E dec esi :00410597 7435 je 004105CE :00410599 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"U N R E G I S T E R E D ! !" | :0041059B 6811014200 push 00420111 :004105A0 FF37 push dword ptr [edi] Okay .. remember when the program loaded those two lines, it moved the lines into ebp+fffff3c and ebp+fffff28. fffff3c was the first line and fffff28 was the second. So what it does here is move the second line into eax, then it pushes eax and calls the strlen. The call returns the length of the string in eax, then it compares eax to ebx .. and ebx is zero and my second line was 111666111. and eax would now be 8. Now it will jump if eax is greater than ebx, infact it says - jump if eax is not below ebx, and eax is not below ebx so it will jump to 4105CE :0041053A 8A841D14FFFFFF mov al, byte ptr [ebp+ebx-000000EC] :00410541 3A841D28FFFFFF cmp al, byte ptr [ebp+ebx-000000D8] :00410548 7435 je 0041057F :0041054A 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"U N R E G I S T E R E D ! !" | :0041054C 68F4004200 push 004200F4 Here it moves the hex value of our name into al, then it compares al with our password/serial. But it doesn't take the hole number, only the first byte ... the first char in my name is w, and the hex value of that is 77, so it takes the first byte in the hex - 7, and the decimal for 7 is 37 in hex .. understand ?? sure you do :) Now it compares 37 with my password which was 111666111, the value for 1 is 31 in hex, so the first number in our serial should be 37, but it's 31 (1 in decimal). The program will jump to 41057F if the numbers was equal .. but it isn't so it will display the unregged message. So lets go change our serial. woody -> 77 6F 6F 64 79 -> 76667. Now the program jumps to 41057F. Lets check it out: :0041057F BE01000000 mov esi, 00000001 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0041057D(U) | :00410584 43 inc ebx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00410538(U) | :00410585 8D8528FFFFFF lea eax, dword ptr [ebp+FFFFFF28] :0041058B 50 push eax * Reference To: cw3215._strlen, Ord:0000h | :0041058C E8D1040000 Call 00410A62 :00410591 59 pop ecx :00410592 3BC3 cmp eax, ebx :00410594 73A4 jnb 0041053A :00410596 4E dec esi :00410597 7435 je 004105CE And we're back to the strlen, it just moves 1 into esi and increses ebx (next char) and compares eax (length of password) to ebx. Lets say we're done matching the name in hex to the password, then it will jump to 4105CE: :004105CE 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"R E G I S T E R E D ! !" | :004105D0 682E014200 push 0042012E Nice :) So the password for my name is 76667 ... easy ? -wOODY^dRN