This time we have got a KeyGenMe, which means the task is to write a KeyGen and not just to patch it or find a serial.
0. Building
This step is just happening cause I have written the KeyGenMe by my own. The executable of the code was build with MinGW as an PE x86.
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char nameInp[128] = {'\0'}; fputs("Name:", stdout); fgets(nameInp, sizeof(nameInp), stdin); nameInp[strlen(nameInp)-1] = '\0'; char serialInp[sizeof(int)]; fputs("Serial:", stdout); fgets(serialInp, sizeof(serialInp), stdin); serialInp[strlen(serialInp)-1] = '\0'; unsigned int serial = 0; for(unsigned int i=0; i<strlen(nameInp); i++) { serial += nameInp[i]; } if(atoi(serialInp) != serial) { fputs("Access denied", stdout); return 1; } fputs("Access granted", stdout); return 0; }
1. Disassembling
The following interesting part of the code is shown from OllyDbg after disassembling the PE x86 executable with it.
00401620 MOV DWORD PTR SS:[ESP],a.00404044 ; ||||||ASCII "Name:" 00401627 CALL <JMP.&msvcrt.fwrite> ; |||||\fwrite 0040162C MOV EAX,DWORD PTR DS:[<&msvcrt._iob>] ; ||||| 00401631 MOV DWORD PTR SS:[ESP+8],EAX ; ||||| 00401635 MOV DWORD PTR SS:[ESP+4],80 ; ||||| 0040163D LEA EAX,DWORD PTR SS:[ESP+18] ; ||||| 00401641 MOV DWORD PTR SS:[ESP],EAX ; ||||| 00401644 CALL <JMP.&msvcrt.fgets> ; ||||\fgets 00401649 LEA EAX,DWORD PTR SS:[ESP+18] ; |||| 0040164D MOV DWORD PTR SS:[ESP],EAX ; |||| 00401650 CALL <JMP.&msvcrt.strlen> ; |||\strlen 00401655 SUB EAX,1 ; ||| 00401658 MOV BYTE PTR SS:[ESP+EAX+18],0 ; ||| 0040165D MOV EAX,DWORD PTR DS:[<&msvcrt._iob>] ; ||| 00401662 ADD EAX,20 ; ||| 00401665 MOV DWORD PTR SS:[ESP+C],EAX ; ||| 00401669 MOV DWORD PTR SS:[ESP+8],7 ; ||| 00401671 MOV DWORD PTR SS:[ESP+4],1 ; ||| 00401679 MOV DWORD PTR SS:[ESP],a.0040404A ; |||ASCII "Serial:" 00401680 CALL <JMP.&msvcrt.fwrite> ; ||\fwrite 00401685 MOV EAX,DWORD PTR DS:[<&msvcrt._iob>] ; || 0040168A MOV DWORD PTR SS:[ESP+8],EAX ; || 0040168E MOV DWORD PTR SS:[ESP+4],4 ; || 00401696 LEA EAX,DWORD PTR SS:[ESP+14] ; || 0040169A MOV DWORD PTR SS:[ESP],EAX ; || 0040169D CALL <JMP.&msvcrt.fgets> ; |\fgets 004016A2 LEA EAX,DWORD PTR SS:[ESP+14] ; | 004016A6 MOV DWORD PTR SS:[ESP],EAX ; | 004016A9 CALL <JMP.&msvcrt.strlen> ; \strlen 004016AE SUB EAX,1 004016B1 MOV BYTE PTR SS:[ESP+EAX+14],0 004016B6 MOV DWORD PTR SS:[ESP+9C],0 004016C1 MOV DWORD PTR SS:[ESP+98],0 004016CC /LEA EAX,DWORD PTR SS:[ESP+18] ; | 004016D0 |MOV DWORD PTR SS:[ESP],EAX ; | 004016D3 |CALL <JMP.&msvcrt.strlen> ; \strlen 004016D8 |CMP DWORD PTR SS:[ESP+98],EAX 004016DF |JNB SHORT a.00401705 004016E1 |LEA EDX,DWORD PTR SS:[ESP+18] 004016E5 |MOV EAX,DWORD PTR SS:[ESP+98] 004016EC |ADD EAX,EDX 004016EE |MOVZX EAX,BYTE PTR DS:[EAX] 004016F1 |MOVSX EAX,AL 004016F4 |ADD DWORD PTR SS:[ESP+9C],EAX 004016FB |ADD DWORD PTR SS:[ESP+98],1 00401703 \JMP SHORT a.004016CC 00401705 LEA EAX,DWORD PTR SS:[ESP+14] ; || 00401709 MOV DWORD PTR SS:[ESP],EAX ; || 0040170C CALL <JMP.&msvcrt.atoi> ; |\atoi 00401711 CMP DWORD PTR SS:[ESP+9C],EAX ; | 00401718 SETNE AL ; | 0040171B TEST AL,AL ; | 0040171D JE SHORT a.0040174E ; | 0040171F MOV EAX,DWORD PTR DS:[<&msvcrt._iob>] ; | 00401724 ADD EAX,20 ; | 00401727 MOV DWORD PTR SS:[ESP+C],EAX ; | 0040172B MOV DWORD PTR SS:[ESP+8],0D ; | 00401733 MOV DWORD PTR SS:[ESP+4],1 ; | 0040173B MOV DWORD PTR SS:[ESP],a.00404052 ; |ASCII "Access denied" 00401742 CALL <JMP.&msvcrt.fwrite> ; \fwrite 00401747 MOV EAX,1 0040174C JMP SHORT a.0040177B 0040174E MOV EAX,DWORD PTR DS:[<&msvcrt._iob>] ; | 00401753 ADD EAX,20 ; | 00401756 MOV DWORD PTR SS:[ESP+C],EAX ; | 0040175A MOV DWORD PTR SS:[ESP+8],0E ; | 00401762 MOV DWORD PTR SS:[ESP+4],1 ; | 0040176A MOV DWORD PTR SS:[ESP],a.00404060 ; |ASCII "Access granted" 00401771 CALL <JMP.&msvcrt.fwrite> ; \fwrite
2. Interpreting
At the beginning it reads a name and a serial from stdin. Then it calculates a value in a loop. After that it converts the entered serial to int and compares it with the calculated value from the loop. If it is the same it granted you access else it denied you access.
The following code shows the loop part.
004016B6 MOV DWORD PTR SS:[ESP+9C],0 ; val = 0 004016C1 MOV DWORD PTR SS:[ESP+98],0 ; idx = 0 004016CC /LEA EAX,DWORD PTR SS:[ESP+18] ; lbl 'START' 004016D0 |MOV DWORD PTR SS:[ESP],EAX ; get &name[0] 004016D3 |CALL <JMP.&msvcrt.strlen> ; len = strlen(name) 004016D8 |CMP DWORD PTR SS:[ESP+98],EAX ; cmp idx with len 004016DF |JNB SHORT a.00401705 ; if >= then jmp to 'EXIT' 004016E1 |LEA EDX,DWORD PTR SS:[ESP+18] ; get &name[0] 004016E5 |MOV EAX,DWORD PTR SS:[ESP+98] ; get i 004016EC |ADD EAX,EDX ; &name[i] = &name[0] + i 004016EE |MOVZX EAX,BYTE PTR DS:[EAX] ; get (high char) name[i] 004016F1 |MOVSX EAX,AL ; get (low char) name[i] 004016F4 |ADD DWORD PTR SS:[ESP+9C],EAX ; val += name[i] 004016FB |ADD DWORD PTR SS:[ESP+98],1 ; i++ 00401703 \JMP SHORT a.004016CC ; jmp to 'START' 00401705 LEA EAX,DWORD PTR SS:[ESP+14] ; lbl 'EXIT'
==> The calculated value is the sum of all ASCII codes of the chars in the name.
3. Generating
After we understood the algorithm of the serial calculation based on the entered name we can now program the KeyGen. In this case I have written it in C.
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char nameInp[128] = {'\0'}; fputs("Name:", stdout); fgets(nameInp, sizeof(nameInp), stdin); nameInp[strlen(nameInp)-1] = '\0'; unsigned int serial = 0; for(unsigned int i=0; i<strlen(nameInp); i++) { serial += nameInp[i]; } printf("Serial: %i", serial); return 0; }