Sat for an evenin’ o’ fun this holiday season. I like these easy ones. Last month I tried a harder one and found it discouraging. I don’t have the sort of time to work on these for a full day, so these couple hour ones are a lot more fun to me at this point.
I found the binary on crackmes.de, here is a mirror.
Part 1: Find the Password.
Really easy. Even me a beginner figured this out in about half an hour. First thing it does is make sure the input is 8 characters long. If it’s not it exits the program.
004013B7 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; | 004013BA |. E8 B1060000 CALL <JMP.&msvcrt.strlen> ; strlen 004013BF |. 83F8 08 CMP EAX,8 004013C2 |. 74 05 JE SHORT CrackMe#.004013C9 ; if strlen(pass) == 8 (if it's not, 'something went wrong') 004013C4 |. E9 2C010000 JMP CrackMe#.004014F5
Next it goes through a little loop which compares each letter of the password you entered with the real password
004013C9 |> C745 F4 000000>MOV DWORD PTR SS:[EBP-C],0 004013D0 |> 837D F4 07 /CMP DWORD PTR SS:[EBP-C],7 ; looks like for (i =0; i<7; i++) probably 004013D4 |. 7F 20 |JG SHORT CrackMe#.004013F6 004013D6 |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] 004013D9 |. 0345 F4 |ADD EAX,DWORD PTR SS:[EBP-C] 004013DC |. 8D50 E0 |LEA EDX,DWORD PTR DS:[EAX-20] ; edx = ptr(ebp - 8) + ptr(ebp - c) 004013DF |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] 004013E2 |. 0345 F4 |ADD EAX,DWORD PTR SS:[EBP-C] 004013E5 |. 83E8 20 |SUB EAX,20 ; eax = edx - 20 004013E8 |. 0FB600 |MOVZX EAX,BYTE PTR DS:[EAX] 004013EB |. FEC0 |INC AL 004013ED |. 8802 |MOV BYTE PTR DS:[EDX],AL 004013EF |. 8D45 F4 |LEA EAX,DWORD PTR SS:[EBP-C] 004013F2 |. FF00 |INC DWORD PTR DS:[EAX] 004013F4 |.^EB DA \JMP SHORT CrackMe#.004013D0 004013F6 |> 8D45 C8 LEA EAX,DWORD PTR SS:[EBP-38] ; |||||||| 004013F9 |. 8D55 D8 LEA EDX,DWORD PTR SS:[EBP-28] ; |||||||| 004013FC |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; |||||||| 00401400 |. 891424 MOV DWORD PTR SS:[ESP],EDX ; |||||||| 00401403 |. E8 58060000 CALL <JMP.&msvcrt.strcmp> ; |||||||\strcmp 00401408 |. 85C0 TEST EAX,EAX ; |||||||if this doesn't = 0 exit 'something went wrong' 0040140A |. 0F85 E5000000 JNZ CrackMe#.004014F5 ; ||||||| 00401410 |. C70424 FE31400>MOV DWORD PTR SS:[ESP],CrackMe#.004031FE ; |||||||ASCII 0A,"Stage 1 co"
If you look closely it compares every letter + 1 to QbTTx1sE. Meaning of course that the password is simply: PaSSw0rD
Part 2: KeyGen
The next part was a little more tricky for me.
On the screen you enter a name and a corresponding serial.
STAGE 2 00401423 |. E8 68060000 CALL <JMP.&msvcrt.printf> ; |||||\printf 00401428 |. C70424 D331400>MOV DWORD PTR SS:[ESP],CrackMe#.004031D3 ; |||||ASCII "******* 0040142F |. E8 5C060000 CALL <JMP.&msvcrt.printf> ; ||||\printf 00401434 |. C70424 1E32400>MOV DWORD PTR SS:[ESP],CrackMe#.0040321E ; ||||ASCII 0A,"Name [2<=c" 0040143B |. E8 50060000 CALL <JMP.&msvcrt.printf> ; |||\printf 00401440 |. 8D45 B8 LEA EAX,DWORD PTR SS:[EBP-48] ; ||| 00401443 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; |||ebp -48 or esp +4 = name 00401447 |. C70424 FB31400>MOV DWORD PTR SS:[ESP],CrackMe#.004031FB ; |||ASCII "%s" 0040144E |. E8 2D060000 CALL <JMP.&msvcrt.scanf> ; ||\scanf 00401453 |. C70424 3632400>MOV DWORD PTR SS:[ESP],CrackMe#.00403236 ; ||ASCII "Serial : " 0040145A |. E8 31060000 CALL <JMP.&msvcrt.printf> ; |\printf 0040145F |. 8D45 B0 LEA EAX,DWORD PTR SS:[EBP-50] ; | 00401462 |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; |ebp -50 = serial 00401466 |. C70424 4132400>MOV DWORD PTR SS:[ESP],CrackMe#.00403241 ; |ASCII "%d" 0040146D |. E8 0E060000 CALL <JMP.&msvcrt.scanf> ; \scanf 00401472 |. C745 F4 000000>MOV DWORD PTR SS:[EBP-C],0 00401479 |. C745 F4 000000>MOV DWORD PTR SS:[EBP-C],0 00401480 |> 8D45 B8 /LEA EAX,DWORD PTR SS:[EBP-48] ; | 00401483 |. 890424 |MOV DWORD PTR SS:[ESP],EAX ; |strlen (name) 00401486 |. E8 E5050000 |CALL <JMP.&msvcrt.strlen> ; \strlen 0040148B |. 3945 F4 |CMP DWORD PTR SS:[EBP-C],EAX ; while loop (for i = len(name) 0040148E |. 77 1A |JA SHORT CrackMe#.004014AA 00401490 |. 8D45 F8 |LEA EAX,DWORD PTR SS:[EBP-8] 00401493 |. 0345 F4 |ADD EAX,DWORD PTR SS:[EBP-C] 00401496 |. 83E8 40 |SUB EAX,40 00401499 |. 0FBE00 |MOVSX EAX,BYTE PTR DS:[EAX] 0040149C |. 0345 B4 |ADD EAX,DWORD PTR SS:[EBP-4C] 0040149F |. 48 |DEC EAX 004014A0 |. 8945 B4 |MOV DWORD PTR SS:[EBP-4C],EAX 004014A3 |. 8D45 F4 |LEA EAX,DWORD PTR SS:[EBP-C] 004014A6 |. FF00 |INC DWORD PTR DS:[EAX] ; pointer to 0,1,...,length 004014A8 |.^EB D6 \JMP SHORT CrackMe#.00401480 004014AA |> 8B45 B4 MOV EAX,DWORD PTR SS:[EBP-4C] ; |||||eax calculated in loop, compared with serial 004014AD |. 3B45 B0 CMP EAX,DWORD PTR SS:[EBP-50] ; ||||| 004014B0 |. 75 43 JNZ SHORT CrackMe#.004014F5 ; ||||| 004014B2 |. C70424 4432400>MOV DWORD PTR SS:[ESP],CrackMe#.00403244 ; |||||ASCII 0A,"Stage 2 Co"
Although it can be a bit hard to read because of all the pointers (at least for me), it is definitely possible to discern the for loop and figure out it’s computing some value based on the name and writing to the same space (ebp – 4c) at every iteration. This value is then compared to the serial to determine if the serial is valid or not. After setting some breakpoints it becomes clear that every value of the name -1 is added together and compared to the serial. Here is a working keygen.
#include <iostream> #include <string> using namespace std; int main() { string name = ""; cout << "name: "; cin >> name; int total = 0; for(int i=0; i<name.length(); i++) { total += name[i]; } total -= (name.length() + 1); cout << "Serial should be " << total << endl; system("PAUSE"); return 0; }
Part 3
This part was theoretically easy, but a bit hard for me to execute. The goal is to remove a console nag.
004014DD |. E8 AE050000 CALL <JMP.&msvcrt.printf> ; |\printf
That line needs to turn to a nop. In ollydbg just right click as say fill with nop, and it should be good.
