Docstoc

Keygen Creation part 1

Document Sample
Keygen Creation part 1 Powered By Docstoc
					 Keygen Creation part 1
Difficulty: Medium
Learn how to create a keygen for mIRC v5.7
Creator: m101


The basic purpose of this tutorial is to teach you how to find an
algorithm in assembly, and create a keygen to create working
keys for it. The target of this tutorial is mIRC 5.7. I have
chosen this program for its simplicity in key creation. For this
tutorial you require Win32DASM at a minimum, and for extra
verification, softice.

First open up mIRC and try to register it. "Sorry, your
registration name and number don't match blah blah blah". Kill
mIRC and open up Win32DASM and decompile mirc32.exe.
Open up the dead listing in Refs > String Data References and
go through till you find the message you got from a failed
registration. Double click it and you will find yourself in the
following code:
:00498B79 6A00                    push 00000000

* Possible Reference to String Resource ID=01912: "mIRC Registration!"
                                  |
:00498B7B 6878070000              push 00000778
:00498B80 E8F365F8FF              call 0041F178
:00498B85 50                      push eax
:00498B86 6A00                    push 00000000

* Possible Reference to String Resource ID=01913: "Sorry, your
registration name and number don't match! Pleas"
                                  |
:00498B88 6879070000              push 00000779
:00498B8D E8E665F8FF              call 0041F178
:00498B92 50                      push eax
:00498B93 8B4508                  mov eax, dword ptr [ebp+08]
:00498B96 50                      push eax


Trace this back to its call at the following code:
:00498A8A 52                      push edx

* Reference To: USER32.SendDlgItemMessageA, Ord:0000h
                                  |
:00498A8B E841800500              Call 004F0AD1
:00498A90 68334A5000              push 00504A33
:00498A95 684C465000              push 0050464C
:00498A9A E8E5FBFFFF              call 00498684
:00498A9F   85C0                  test eax, eax
:00498AA1   0F849B000000          je 00498B42
:00498AA7   BE3C9D4F00            mov esi, 004F9D3C
:00498AAC   BF4C465000            mov edi, 0050464C


Notice the SendDlgItemMessageA? Well if you open up softice,
you can set a breakpoint on it and you will find yourself at this
code after the second call. To make this all really easy for you,
you should comment the code, but because you are probably
really slack, ive done it for you:
:00498A90 68334A5000              push 00504A33        <serial
:00498A95 684C465000              push 0050464C        <username
:00498A9A E8E5FBFFFF              call 00498684        <username
and serial operations
:00498A9F 85C0                    test eax, eax        <test eax
:00498AA1 0F849B000000            je 00498B42          <bad
cracker if eax=1
:00498AA7 BE3C9D4F00              mov esi, 004F9D3C


As you can see, the username and serial are pushed then a
call is made to some operations on the pushed values. The
result must result in eax being 0 or the routine will fail and we
will get an 'unregistered' result. At this point you could patch
the code to trick mIRC into being registered, but only until you
restard, which ofcourse is totally useless to us. Follow the code
through the call and you will get this:
* Referenced by a CALL at Addresses:
|:004987DF   , :004988B2   , :00498A9A
|
:00498684 55                      push ebp
:00498685 8BEC                    mov ebp, esp
:00498687 53                      push ebx

*CUT*

:004986D6 83E103                   and ecx, 00000003
:004986D9 F3                       repz
:004986DA A4                       movsb
:004986DB 5E                       pop esi             <serial
:004986DC 68204A5100               push 00514A20       <serial
:004986E1 6820495100               push 00514920       <username
:004986E6 E8A1FEFFFF               call 0049858C       <must
return eax=1 <<<first check!!!
:004986EB 85C0                     test eax, eax
:004986ED 7407                     je 004986F6         <eax must
be true to perform second check!!!
:004986EF B801000000               mov eax, 00000001   <bad
cracker
:004986F4 EB74                     jmp 0049876A
Well as you can see, our serial is pushed, and if the result is
wrong the serial will fail. So lets check out the the call there
and see what it does for us:
:0049858C 55                      push ebp
:0049858D 8BEC                    mov ebp, esp
:0049858F 83C4F4                  add esp, FFFFFFF4
:00498592 53                      push ebx
:00498593 56                      push esi
:00498594 57                      push edi
:00498595 8B750C                  mov esi, dword ptr [ebp+0C]
:00498598 8B4508                  mov eax, dword ptr [ebp+08]
:0049859B 50                      push eax
:0049859C E8778DF6FF              call 00401318               <strlen on
username
:004985A1 59                      pop ecx
:004985A2 83F805                  cmp eax, 00000005           <is
username = 5?
:004985A5 7307                    jnb 004985AE                        <if
equal or larger jump to good cracker
:004985A7 33C0                    xor eax, eax
        <bad cracker
:004985A9 E9CA000000              jmp 00498678
Very interesting, as you can see, it performs a strlen operation
on the username and returns its length. If the username isnt
atleast five characters then the the function returns false and
we get an 'unregistered' message. If you follow the 'bad
cracker' jump then you get the following code:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0049866D(C)
|
:00498673 B801000000              mov eax, 00000001          <good
cracker

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004985A9(U), :004985C1(U), :004985DE(U), :00498631(U), :00498671(U)       <all
|
:00498678 5F                      pop edi
:00498679 5E                      pop esi
:0049867A 5B                      pop ebx
:0049867B 8BE5                    mov esp, ebp
:0049867D 5D                      pop ebp
:0049867E C20800                  ret 0008


Now this is interesting to us, check out all those unconditional
jumps. All of them return bad cracker apart from the one call.
The next peice of code basically checks for the split in the
serial:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004985A5(C)
|
:004985AE 6A2D                      push 0000002D                   <check for
"-" in serial
:004985B0 56                        push esi                        <serial
:004985B1 E8C28CF6FF                call 00401278
:004985B6 83C408                    add esp, 00000008
:004985B9 8BD8                      mov ebx, eax
:004985BB 85DB                      test ebx, ebx
:004985BD 7507                      jne 004985C6
:004985BF 33C0                      xor eax, eax
        <bad cracker
:004985C1 E9B2000000                jmp 00498678


See that 2D? It refers to "-". This is one thing you must
remember, its all over most protections. Follow the code
through and you will get this:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004985BD(C)
|
:004985C6 C60300                  mov byte ptr [ebx], 00      <remove
the "-" from the string
:004985C9 56                      push esi
:004985CA E84514F7FF              call 00409A14               <check for
"-" problems in string and generate checksums
:004985CF 59                      pop ecx
:004985D0 8945FC                  mov dword ptr [ebp-04], eax
:004985D3 C6032D                  mov byte ptr [ebx], 2D
:004985D6 43                      inc ebx
:004985D7 803B00                  cmp byte ptr [ebx], 00
:004985DA 7507                    jne 004985E3
        <good cracker
:004985DC 33C0                    xor eax, eax
        <bad cracker
:004985DE E995000000              jmp 00498678


This should all be pretty self explanitory to you, so just keep
following the code:
* Referenced by a (U)nconditional   or (C)onditional Jump at Address:
|:004985DA(C)
|
:004985E3 53                        push ebx
:004985E4 E82B14F7FF                call 00409A14
:004985E9 59                        pop ecx
:004985EA 8945F8                    mov dword ptr [ebp-08], eax
:004985ED 8B5508                    mov edx, dword ptr [ebp+08]
:004985F0 52                        push edx
:004985F1 E8228DF6FF                call 00401318
        <username length
:004985F6 59                        pop ecx
        <username
:004985F7 8945F4                    mov   dword ptr [ebp-0C], eax
:004985FA 33C0                      xor   eax, eax                               <eax = 0
:004985FC 33DB                      xor   ebx, ebx                               <ebx = 0
:004985FE BA03000000                mov   edx, 00000003
:00498603 8B4D08                  mov ecx, dword ptr [ebp+08] <ecx =
username
:00498606 83C103                  add ecx, 00000003
        <set pointer to 4th character
:00498609 3B55F4                  cmp edx, dword ptr [ebp-0C]
:0049860C 7D1C                    jge 0049862A
Now we are into the point where the real serial is created. As
you can see more string lengths are called and it leads onto
the main key creation code:
* Referenced by a (U)nconditional   or (C)onditional Jump at Address:
|:00498628(C)
|
:0049860E 0FB631                    movzx esi, byte ptr [ecx]               <set esi to
:00498611 0FAF34859C9C4F00          imul esi, dword ptr [4*eax+004F9C9C]    <multiply b
:00498619 03DE                      add ebx, esi
:0049861B 40                        inc eax                                        <nex
:0049861C 83F826                    cmp eax, 00000026                              <26
:0049861F 7E02                      jle 00498623
:00498621 33C0                      xor eax, eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0049861F(C)
|
:00498623 42                      inc edx
        <edx = edx + 1
:00498624 41                      inc ecx
        <next character of username
:00498625 3B55F4                  cmp edx, dword ptr [ebp-0C] <end of
username?
:00498628 7CE4                    jl 0049860E                         <if
more then loop

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0049860C(C)
|
:0049862A 3B5DFC                  cmp ebx, dword ptr [ebp-04]
        <compare result with serial
:0049862D 7404                    je 00498633                               <good crack
:0049862F 33C0                    xor eax, eax                                      <bad
:00498631 EB45                    jmp 00498678
Ok, so what the hell does this do? It generates the first half of
the serial, something like '1234-XXXXXX'. The second half is
generated next. Basically as you can see, it takes the fourth
value of the username, multiplies it by a value from a table,
then adds the result to ebx to create the serial, but ofcourse
the serial is in hex so you will have to convert it for use. Each
loop a new value is taken from the table and the next
character is taken to be processed. I bet your wondering about
that table? Well its like an array of all the values, if you check
it in softice, you will find the following values:
0B 06 11 0C 0C 0E 05 0C 10 0A 0B 06 0E 0E 04 0B 06 0E 0E 04 0B 09 0C 0B
0A 08 0A 0A 10 08 04 06 0A 0C 10 08 0A 04 10 00


Only the first twenty six values are used. Next we have the
creation of the second half of the serial:
* Referenced by a (U)nconditional   or (C)onditional Jump at Address:
|:0049862D(C)
|
:00498633 33C0                      xor   eax,   eax                           <reset eax
:00498635 33DB                      xor   ebx,   ebx                           <reset ebx
:00498637 BA03000000                mov   edx,   00000003
:0049863C 8B4D08                    mov   ecx,   dword ptr [ebp+08] <set ecx
to username
:0049863F 83C103                    add ecx, 00000003
        <move to 4th character
:00498642 3B55F4                    cmp edx, dword ptr [ebp-0C]
:00498645 7D23                      jge 0049866A

* Referenced by a (U)nconditional   or (C)onditional Jump at Address:
|:00498668(C)
|
:00498647 0FB631                    movzx esi, byte ptr [ecx]                  <set esi to
:0049864A 0FB679FF                  movzx edi, byte ptr [ecx-01]                       <set
:0049864E 0FAFF7                    imul esi, edi                                      <esi
:00498651 0FAF34859C9C4F00          imul esi, dword ptr [4*eax+004F9C9C]       <esi = esi
:00498659 03DE                      add ebx, esi
:0049865B 40                        inc eax                                           <nex
:0049865C 83F826                    cmp eax, 00000026                                 <is
:0049865F 7E02                      jle 00498663
:00498661 33C0                      xor eax, eax

* Referenced by a (U)nconditional   or (C)onditional Jump at Address:
|:0049865F(C)
|
:00498663 42                        inc edx
        <edx = edx+1
:00498664 41                        inc ecx
        <next byte in character
:00498665 3B55F4                    cmp edx, dword ptr [ebp-0C] <end of
username?
:00498668 7CDD                      jl 00498647
        <no, then loop

* Referenced by a (U)nconditional   or (C)onditional Jump at Address:
|:00498645(C)
|
:0049866A 3B5DF8                    cmp ebx, dword ptr [ebp-08] <compare
second half of serial
:0049866D 7404                      je 00498673
        <good cracker
:0049866F 33C0                      xor eax, eax                               <bad cracke
:00498671 EB05                      jmp 00498678
Next the algorithm takes the third character, multiplies it by
the fourth value and then multiplies the result by the values
from the table. After that the fourth and fifth value are taken
and so on until the checksum is entirely created. From now, i
dont know if im just being lazy or not, but ive created the
keygen entirely in assembly with debug to prove it can be
done. To code a keygen, all you really have to do is translate
the assembly code into another language. The following
keygen is only able to do small five letter keys, and nothing
that returns too higher a value, also the result still has to be
converted into decimal.
------------------------------------------------

debug
a 100
mov byte ptr   [500],4e   ;N
mov byte ptr   [501],61   ;a
mov byte ptr   [502],6d   ;m
mov byte ptr   [503],65   ;e
mov byte ptr   [504],3a   ;:
mov byte ptr   [505],24   ;$
mov ah,09                 ;string mode
lea dx,[500]              ;set string start [0500]
int 21                    ;print string
lea di,[5ff]              ;set first input
;loop
stosb                    ;save input
xor ax,ax                ;clear for loop
int 16                   ;input user to al
cmp al,0d                ;check for enter
jnz 12a                  ;if enter loop
mov al,24                ;set last char $
stosb                    ;save $
;setup table   values at [0800]
mov word ptr   [800],0b
mov word ptr   [802],06
mov word ptr   [804],11
mov word ptr   [806],0c
mov word ptr   [808],0c
mov word ptr   [80a],0e
mov word ptr   [80c],05
mov word ptr   [80e],0c
mov word ptr   [810],10
mov word ptr   [812],0a
mov word ptr   [814],0b
mov word ptr   [816],06
mov word ptr   [818],0e
mov word ptr   [81a],0e
mov word ptr   [81c],04
mov word ptr   [81e],0b
mov word ptr   [820],06
mov word ptr   [822],0e
mov word ptr   [824],0e
mov word ptr [826],04
mov word ptr [828],0b
mov word ptr [82a],09
mov word ptr [82c],0c
mov word ptr [82e],0b
mov word ptr [830],0a
mov word ptr [832],08
mov word ptr [834],0a
mov word ptr [836],0a
mov word ptr [838],10
mov word ptr [83a],08
mov word ptr [83c],04
mov word ptr [83e],06
mov word ptr [840],0a
mov word ptr [842],0c
mov word ptr [844],10
mov word ptr [846],08
mov word ptr [848],0a
mov word ptr [84a],04
mov word ptr [84c],10
mov word ptr [84e],00
;continue with first checksum
sub di,1               ;calculate user length
mov word ptr [900],di ;place length in [0900]
xor bx,bx              ;clear bx
xor cx,cx              ;clear cx
lea si,[603]           ;set si to 3rd character
lea di,[800]           ;set di to 1st value
;loop
xor ax,ax              ;clear ax
lodsb                  ;load username from si
mov cx,ax              ;set cx = input from si
xor ax,ax              ;clear ax
xchg di,si             ;switch di and si values
lodsb                  ;load table from si
inc si                 ;skip space in table
xchg si,di             ;switch values back
mul cx                 ;multiply char by table
add bx, ax             ;accumulate values
mov dx,[900]           ;set dx = user length
cmp si,dx              ;last character?
jl 239                 ;if so loop
push bx                ;add 1st value to stack
xor ax,ax              ;clear ax
xor bx,bx              ;clear bx
xor cx,cx              ;clear cx
xor dx,dx              ;clear dx
xor si,si              ;clear si
xor di,di              ;clear di
lea si,[602]           ;point si to 3rd char
lea di,[800]           ;point di to table
xor ax,ax              ;clear ax
;loop
lodsb                  ;load unername from si
mov cx,ax              ;set cx = character
xor ax,ax              ;clear ax
lodsb                  ;load next char from si
mov dx,ax             ;set dx = next character
dec si                ;si = si - 1
xor ax,ax             ;clear ax
xchg di,si            ;switch di and si
lodsb                 ;load table from si
inc si                ;skip space in table
xchg si,di            ;switch si and di back
push ax               ;store value temporarily
mov ax,cx             ;set ax = character
mul dx                ;multiply two characters
mov cx,ax             ;set result to cx
pop ax                ;restore ax from stack
mul cx                ;multiply by table value
add bx, ax            ;accumulate values
xor ax,ax             ;clear ax
xor cx,cx             ;clear cx
mov dx,[900]          ;set dx to user length
dec dx                ;dx = dx - 1
cmp si,dx             ;last character?
jl 269                ;if so loop
xor cx,cx             ;clear cx
xor dx,dx             ;clear dx
pop ax                ;restore 1st checksum
int 20                ;quit

g =100 298

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


AX will contain the first half of the string, and BX the second
half, convert them and you will be given something like '1234-
56789'. Just to you you how easy it is to do it in other
languages, here is the first checksum in C:
------------------------------------------------

for (i=4;i<=strlen(username);i++){
    j=(i-4)%26;
    value=value+username[i-1]*table_values[j];
}

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


When you first create a keygen, it can be a real bitch to
understand what the code is doing, and how to implement it.
You however will find that after a few tries, you will get used
to it and be able to create keygens without too much
problems. Just remember to comment the code and you wont
have much problems at all ;)

				
DOCUMENT INFO
Shared By:
Categories:
Stats:
views:10
posted:4/29/2010
language:English
pages:9