Writing a standalone Keygenerator
 
Intro

In this tutorial, we are going to keygen Bengaly crackme #3. Get it here.

TOOLS USED : W32Dasm, Borland C++
DIFFICULTY : 1/10

Trace the code...

Load the crackme...enter your name and a fake serial. Put a breakpoint on GetDlgItemTextA, and press ok.
We land here :


004012B1 6A40                    push 00000040    
004012B3 683F304000 push 0040303F ; Pointer to name
* Possible Reference to Dialog: MAINWINDOW, CONTROL_ID:006A, ""
         
004012B8 6A6A                    push 0000006A     ; Edit Box Name
004012BA FF7508                  push [ebp+08]
* Reference To: USER32.GetDlgItemTextA, Ord:0102h


004012BD E80C010000              Call 004013CE
004012C2 83F800                  cmp eax, 00000000  ; If namelength=0
004012C5 7418                    je 004012DF        ; Jump MessageBoxA
004012C7 6A40                    push 00000040
004012C9 683F314000              push 0040313F      ; Pointer to serial
* Possible Reference to Dialog: MAINWINDOW, CONTROL_ID:006B, ""
         
004012CE 6A6B                   push 0000006B       ; Edit Box Serial
004012D0 FF7508                 push [ebp+08]
* Reference To: USER32.GetDlgItemTextA, Ord:0102h


004012D3 E8F6000000             Call 004013CE 
004012D8 83F800                 cmp eax, 00000000   ; If namelength=0
004012DB 7402                   je 004012DF         ; Jump MessageBoxA
004012DD EB17                   jmp 004012F6        ; else continue
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: 004012C5(C), :004012DB(C)


004012DF 6A00                   push 00000000
* Possible StringData Ref from Data Obj ->"KeygenMe #3"
         
004012E1 688C344000             push 0040348C
* Possible StringData Ref from Data Obj ->" Please Fill In 1 Char to Continue!!"

004012E6 6800304000             push 00403000
004012EB 6A00                   push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh


004012ED E800010000             Call 004013F2
004012F2 C9                     leave
004012F3 C21000                 ret 0010
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 004012DD(U)


004012F6 683F304000             push 0040303F    ; Pointer to name
* Reference To: KERNEL32.lstrlenA, Ord:02E2h


004012FB E834010000             Call 00401434     ; eax contains NameLength 
00401300 33F6                   xor esi, esi
00401302 33DB                   xor ebx, ebx
00401304 8BC8                   mov ecx, eax      ; Move nameLength to ecx
00401306 B801000000             mov eax, 00000001 ; Table index = 1
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401331(C)

; Important part for our keygen :
0040130B 8B1D3F304000           mov ebx, dword ptr [0040303F] ; First Dword of name loaded
00401311 0FBE901F354000         movsx edx, byte ptr [eax+0040351F] ; Table byte loaded
00401318 2BDA                   sub ebx, edx      
0040131A 0FAFDA                 imul ebx, edx
0040131D 8BF3                   mov esi, ebx
0040131F 2BD8                   sub ebx, eax
00401321 81C343353504           add ebx, 04353543
00401327 03F3                   add esi, ebx
00401329 33F2                   xor esi, edx
0040132B B804000000             mov eax, 00000004 ; Change table index to 4
00401330 49                     dec ecx
00401331 75D8                   jne 0040130B      ; Jump if ecx = 0
00401333 56                     push esi          ; Push our hash on the stack
00401334 683F314000             push 0040313F     ; Pointer to serial
00401339 E84A000000             call 00401388     ; Convert ascii string to dword
0040133E 5E                     pop esi           ; Restore our hash in esi
0040133F 3BC6                   cmp eax, esi      ; If fake serial and hash are not equal,
00401341 7515                   jne 00401358      ; jump to Bad guy message
00401343 6A00                   push 00000000     ; Else good guy message
* Possible StringData Ref from Data Obj ->"KeygenMe #3"

00401345 688C344000             push 0040348C
* Possible StringData Ref from Data Obj ->" Great, You are ranked as Level-3 at Keygening now"

0040134A 68DD344000             push 004034DD
0040134F 6A00                   push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh


00401351 E89C000000             Call 004013F2
00401356 EB13                   jmp 0040136B
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401341(C)


00401358 6A00                   push 00000000   ; Bad guy message
* Possible StringData Ref from Data Obj ->"KeygenMe #3"

0040135A 688C344000             push 0040348C
* Possible StringData Ref from Data Obj ->" You Have Entered A Wrong Serial, Please Try Again"

0040135F 68AA344000             push 004034AA
00401364 6A00                   push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh


00401366 E887000000             Call 004013F2
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:004012A1(C), :004012AB(C), :00401356(U)


0040136B EB15 jmp 00401382


I don't think this code needs a lot of explanation, it's very straightforward. Except for the following :

- The algorithm is looped Namelength times, the hash is stored in esi, and esi gets overwritten every loop. So only the last loop execution is of importance for our keygen. I don't think the author entended it that way. (a bug)


- At the end of every loop '4' is moved to eax. Eax is the index of a random bytes table. Another bug here : It is useless to define a random table, if you only use 1 byte of it. I think the author wanted to add '4' to the index every loop. But it makes the keygen easier ;)

   00403518 65 20 2D 20 23 33 00 20  e - #3. 
00403520 25 40 24 65 72 77 72 23 %@$erwr#
00403528 40 24 24 21 40 23 32 31 @$$!@#21
00403530 24 40 5E 26 2A 26 28 25 $@^&*&(%
00403538 72 74 68 64 68 64 66 77 rthdhdfw
00403540 34 32 33 25 23 44 53 67 423%#DSg
00403548 66 59 24 25 5E 23 24 25 fY$%^#$%
00403550 62 72 65 23 42 40 40 25 bre#B@@%
00403558 23 47 33 72 65 00 00 00 #G3re...


The coloured bytes are the elements of the table, the red byte is the only one used : Table[eax] = Table[4]


- Only the first 4 letters of our name are used for the serial calculation.

The keygen
------------------------------------------------------------------------
#include <iostream.h>
#include <conio.h>

int main () {

char name[4]; // We only need the first 4 bytes of the name
char t = 101; // The only element from the table we need 65h = 101

cout<<"Name :";
cin>>name; // Get name

int *nameint=(int*)name; // Make dword pointer that points to name
unsigned int hash=*nameint; // Store dword as init value for hash (in win32 an int = 4 bytes = dword)

hash - = t;
hash = hash * t;
unsigned int hash1 = hash;
hash1 - = 4;
hash1 + = 70595907;
hash + = hash1;
hash = hash^t;
cout<<"Serial : "<<hash; // Display serial


getch();
}

---------------------------------------------------------------------------

That's all, compile the above code, and you have a working keygen.

Detten or Dettaaaaaaa or Dettzzz = 3893153104

If you have questions about this tutorial or remarks, mail me at Detn@hotmail.com

Greetings,

Detten
Detn@hotmail.com

www.biw-reversing.cjb.net

back to tutorials