Software Cracking (Example 2)

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;
}