TechAmok Forum Index TechAmok
Independent Technology News
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Learn How to Create a Keygen

 
Post new topic   Reply to topic    TechAmok Forum Index -> Software
View previous topic :: View next topic  
Author Message
admin
Site Admin
Site Admin


Joined: 28 Feb 2006
Posts: 889

PostPosted: Fri May 26, 2006 9:06 am    Post subject: Learn How to Create a Keygen Reply with quote

Code:

Training session 34: Keygen Creation #2
Difficulty: Medium
Learn how to create a keygen for Winzip
Creator: m101


Target: Winzip 7.*
URL: http://www.winzip.com

For this tutorial you will need both Softice and either Win32DASM or IDA. If you plan on compiling the Keygen provided herein, you will need atleast TASM v2.0. You should have a basic knowledge of assembly to attempt this tutorial. Although at the time of writing, Winzip 7 wasnt the latest release, the serial algorithm hasnt changed. The aim of this tutorial is not to teach you how to defeat Winzip, but however how to identify an algorithm in assembly, and then create a key generator for it.

The first thing to do is ofcourse, to find the serial generation algorithm. Open up Winzip and feed it some garbage. Now hit ok and a message box will popup with the message "Incomplete or incorrect information". Crack open Win32DASM and goto 'Refs > String Data References'. Search until you find our message and youll find yourself here:

:004080B0 EB31                    jmp 004080E3

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00408051(C), :0040805A(C), :00408063(C)
|
:004080B2 E8E5010000              call 0040829C

* Possible Reference to String Resource ID=00654: "Incomplete or incorrect information"
                                  |
:004080B7 688E020000              push 0000028E
:004080BC E89B050200              call 0042865C
:004080C1 59                      pop ecx
:004080C2 50                      push eax
:004080C3 57                      push edi


Weve found our error dialog, so lets check out those conditional jumps. Notice they are all close together? This is generally due to input checking, and ofcourse our serial. So lets trace those jumps back a bit:

:00408030 68810C0000              push 00000C81
:00408035 57                      push edi

* Reference To: USER32.GetDlgItemTextA, Ord:00F5h
                                  |
:00408036 FF150C844600            Call dword ptr [0046840C]
:0040803C 56                      push esi
:0040803D E857160200              call 00429699
:00408042 59                      pop ecx
:00408043 56                      push esi
:00408044 E879160200              call 004296C2
:00408049 803D28D9470000          cmp byte ptr [0047D928], 00      <is username longer than 0?
:00408050 59                      pop ecx
:00408051 745F                    je 004080B2            <bad cracker
:00408053 803D58D9470000          cmp byte ptr [0047D958], 00      <is password longer than 0?
:0040805A 7456                    je 004080B2            <bad cracker
:0040805C E8EAFAFFFF              call 00407B4B            <create serials
:00408061 85C0                    test eax, eax            <must return eax = 1
:00408063 744D                    je 004080B2            <bad cracker
:00408065 53                      push ebx


A very nice GetDlgItemTextA right before our checking, this is an excellent opportunity to load up softice and just check what the each register points to before our tests/calls. As you can see, it checks the to make sure we gave it any input, performs a call, and finally returns a value that either tells us if our serial is correct or not. Trace the call down and you will find this:

* Referenced by a CALL at Addresses:
|:0040108C   , :00401228   , :0040805C   , :0042D0EC   
|
:00407B4B 55                      push ebp
:00407B4C 8BEC                    mov ebp, esp
:00407B4E 81EC08020000            sub esp, 00000208
:00407B54 53                      push ebx
:00407B55 56                      push esi
:00407B56 33F6                    xor esi, esi
:00407B58 803D28D9470000          cmp byte ptr [0047D928], 00      <check for username
:00407B5F 57                      push edi
:00407B60 0F84A1000000            je 00407C07            <bad cracker
:00407B66 8D45EC                  lea eax, dword ptr [ebp-14]
:00407B69 50                      push eax

* Possible StringData Ref from Data Obj ->"c"
                                  |
:00407B6A 6860F44600              push 0046F460


Yet again it checks for our input, and depending on the result, returns a bad vulue in eax from the call. Trace the code further down and you will find this:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407BF9(C)
|
:00407C0E 8D85C0FEFFFF            lea eax, dword ptr [ebp+FFFFFEC0]
:00407C14 50                      push eax
:00407C15 57                      push edi            <push username
:00407C16 E8AB000000              call 00407CC6            <calculate serial
:00407C1B 59                      pop ecx            <pop username
:00407C1C BE58D94700              mov esi, 0047D958
:00407C21 59                      pop ecx            <pop serial
:00407C22 8D85C0FEFFFF            lea eax, dword ptr [ebp+FFFFFEC0]
:00407C28 56                      push esi
:00407C29 50                      push eax
:00407C2A E8D1FC0400              call 00457900
:00407C2F F7D8                    neg eax
:00407C31 1BC0                    sbb eax, eax
:00407C33 59                      pop ecx
:00407C34 40                      inc eax
:00407C35 59                      pop ecx
:00407C36 A37CB04700              mov dword ptr [0047B07C], eax


From a little debuggin we find that the call at 00407C16 returns a correct serial according to our username. Follow the call through and you will find this:

:00407CD3 8A11                    mov dl, byte ptr [ecx]      <set dl to first character of username
:00407CD5 57                      push edi
:00407CD6 33C0                    xor eax, eax            <clear eax
:00407CD8 8BF1                    mov esi, ecx            <set esi to point to username
:00407CDA 33FF                    xor edi, edi            <clear edi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407CF1(U)
|
:00407CDC 84D2                    test dl, dl            <is byte = 0?
:00407CDE 7413                    je 00407CF3            <yes, then jump
:00407CE0 660FB6D2                movzx dx, dl            <clear dh and leave dl alone
:00407CE4 8BDF                    mov ebx, edi            <set ebx = edi
:00407CE6 0FAFDA                  imul ebx, edx            <multiply char by position in username
:00407CE9 015DFC                  add dword ptr [ebp-04], ebx      <accumulate ebx
:00407CEC 8A5601                  mov dl, byte ptr [esi+01]      <move next char into dl
:00407CEF 47                      inc edi            <edi = edi + 1
:00407CF0 46                      inc esi            <next character
:00407CF1 EBE9                    jmp 00407CDC            <loop


This little bit of code does a nice little thing to our serial. It returns the last four characters of our serial. Each hex value of the username is multiplied by its position relative to the first, then adds the results up leaving us with our value. This is illustrated below:

m 6D * 0 = 0
1 31 * 1 = 31
0 30 * 2 = 60
1 31 * 3 = 93

0 + 31 + 60 + 93 = 0124                     <second half of serial


After calculating the second half of our serial, it obviously has to calculate the first half. This is done below:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407CDE(C)
|
:00407CF3 C705ECD3470001000000    mov dword ptr [0047D3EC], 00000001
:00407CFD 8BF1                    mov esi, ecx            <reset esi to start of username
:00407CFF 8A09                    mov cl, byte ptr [ecx]      <set cl = first character of username

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D1C(U)
|
:00407D01 84C9                    test cl, cl            <is byte = 0?
:00407D03 7419                    je 00407D1E            <yes, then jump
:00407D05 660FB6C9                movzx cx, cl            <clear ch and leave cl alone
:00407D09 6821100000              push 00001021
:00407D0E 51                      push ecx
:00407D0F 50                      push eax
:00407D10 E82A000000              call 00407D3F
:00407D15 8A4E01                  mov cl, byte ptr [esi+01]      <set cl to next byte
:00407D18 83C40C                  add esp, 0000000C
:00407D1B 46                      inc esi            <next character
:00407D1C EBE3                    jmp 00407D01            <loop

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D03(C)
|
:00407D1E 0FB74DFC                movzx ecx, word ptr [ebp-04]
:00407D22 83C063                  add eax, 00000063         <add 63 to the result
:00407D25 51                      push ecx
:00407D26 0FB7C0                  movzx eax, ax            <leave first part of serial in ax


The mass of the routine relies on that call, so lets follow that through and see what it does:

* Referenced by a CALL at Addresses:
|:00407D10   , :00407DFB   
|
:00407D3F 55                      push ebp
:00407D40 8BEC                    mov ebp, esp
:00407D42 8B4508                  mov eax, dword ptr [ebp+08]
:00407D45 56                      push esi
:00407D46 33C9                    xor ecx, ecx               <reset ecx

* Possible Reference to String Resource ID=00008: "Delete files from %s"
                                  |
:00407D48 6A08                    push 00000008               <place 08 on stack
:00407D4A 8A6D0C                  mov ch, byte ptr [ebp+0C]         <move character to ch
:00407D4D 5A                      pop edx               <edx = 08

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D65(C)
|
:00407D4E 8BF1                    mov esi, ecx               <esi = ecx
:00407D50 33F0                    xor esi, eax               <xor esi with current sum
:00407D52 66F7C60080              test si, 8000               <is si greater than 8000?
:00407D57 7407                    je 00407D60               <yes, then jump
:00407D59 03C0                    add eax, eax               <eax = eax * 2
:00407D5B 334510                  xor eax, dword ptr [ebp+10]         <xor eax with 1021
:00407D5E EB02                    jmp 00407D62

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D57(C)
|
:00407D60 D1E0                    shl eax, 1               <eax = eax * 2

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D5E(U)
|
:00407D62 D1E1                    shl ecx, 1               <ecx = ecx * 2
:00407D64 4A                      dec edx               <loop count = loop count - 1
:00407D65 75E7                    jne 00407D4E               <jump after 8th loop
:00407D67 5E                      pop esi
:00407D68 5D                      pop ebp
:00407D69 C3                      ret


As the code is above, it is not in an easy to read form, even with commenting. This is because the code has been pepered either by the creators, or by the compiler. To make this a little less confusing, I have removed the junk code as shown below:

-----------------------------------------------------------------------------------------------------------------
Second half of serial
-----------------------------------------------------------------------------------------------------------------

:00407CD3 8A11                    mov dl, byte ptr [ecx]      <set dl to first character of username
:00407CD6 33C0                    xor eax, eax            <clear eax
:00407CD8 8BF1                    mov esi, ecx            <set esi to point to username
:00407CDA 33FF                    xor edi, edi            <clear edi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407CF1(U)
|
:00407CDC 84D2                    test dl, dl            <is byte = 0?
:00407CDE 7413                    je 00407CF3            <yes, then jump
:00407CE0 660FB6D2                movzx dx, dl            <clear dh and leave dl alone
:00407CE4 8BDF                    mov ebx, edi            <set ebx to loop number
:00407CE6 0FAFDA                  imul ebx, edx            <multiply character by loop number
:00407CE9 015DFC                  add dword ptr [ebp-04], ebx      <accumulate ebx
:00407CEC 8A5601                  mov dl, byte ptr [esi+01]      <move next char into dl
:00407CEF 47                      inc edi            <edi = edi + 1
:00407CF0 46                      inc esi            <next character
:00407CF1 EBE9                    jmp 00407CDC            <loop

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

-----------------------------------------------------------------------------------------------------------------
First half of serial
-----------------------------------------------------------------------------------------------------------------

:00407CFD 8BF1                    mov esi, ecx               <reset esi to start of username
:00407CFF 8A09                    mov cl, byte ptr [ecx]         <set cl = 1st char of username

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D1C(U)
|
:00407D01 84C9                    test cl, cl               <is byte = 0?
:00407D03 7419                    jz 00407D1E               <yes, then jump
:00407D05 660FB6C9                movzx cx, cl               <clear ch and leave cl alone
:00407D46 33C9                    xor ecx, ecx               <reset ecx
:00407D4A 8A6D0C                  mov ch, byte ptr [ebp+0C]         <move character to ch
:00407D4D 5A                      mov edx, 08               <edx = 08

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D65(C)
|
:00407D4E 8BF1                    mov esi, ecx               <esi = ecx
:00407D50 33F0                    xor esi, eax               <xor esi with current sum
:00407D52 66F7C60080              test si, 8000               <is si greater than 8000?
:00407D57 7407                    jz 00407D60               <yes, then jump
:00407D59 03C0                    add eax, eax               <eax = eax * 2
:00407D5B 334510                  xor eax, 1021               <xor eax with 1021
:00407D5E EB02                    jmp 00407D62

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D57(C)
|
:00407D60 D1E0                    shl eax, 1               <eax = eax * 2

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D5E(U)
|
:00407D62 D1E1                    shl ecx, 1               <ecx = ecx * 2
:00407D64 4A                      dec edx               <loop count = loop count - 1
:00407D65 75E7                    jnz 00407D4E               <jump until 8th loop
:00407D15 8A4E01                  mov cl, byte ptr [esi+01]         <set cl to next byte
:00407D1B 46                      inc esi               <next character
:00407D1C EBE3                    jmp 00407D01               <loop

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407D03(C)
|
:00407D22 83C063                  add eax, 00000063            <add 63 to the result
:00407D26 0FB7C0                  movzx eax, ax               <leave 1st part of serial in ax

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


Follow this code through in Softice if you have to, and you will get a good idea of what is happening. Now try coding up a keygen for yourself. For an input of 'm101' you should arrive with something like AX = B189 BX = 0124, a total result of 'B1890124' as your serial.

Here is the keygen i coded:

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

MAIN SEGMENT
     ASSUME DS:MAIN,ES:MAIN,CS:MAIN,SS:MAIN

ORG 100h

START:
 
  MOV AH,09h
  MOV DX,OFFSET TEXTB
  INT 21h
  MOV AH,02h
  MOV DL,0Ah
  INT 21h
  MOV AH,09h
  MOV DX,OFFSET TEXTC
  INT 21h
  MOV AH,02h
  MOV DL,0Ah
  INT 21h
  MOV AH,09h
  MOV DX,OFFSET TEXT
  INT 21h
  MOV DI,OFFSET INPUTTER - 1

TAKENEXTINPUT:

  STOSB
  XOR AX,AX
  INT 16h
  MOV AH,02h
  MOV DL,AL
  INT 21h
  CMP AL,0dh
  JNZ TAKENEXTINPUT
  MOV AL,0h
  STOSB
  MOV AH,02h
  MOV DL,0Ah
  INT 21h
  XOR DX,DX
  XOR CX,CX
  XOR AX,AX
  MOV SI,OFFSET INPUTTER

NEXTCHAR:

  LODSB
  TEST AL,AL
  JZ LASTCHAR
  XOR AH,AH
  PUSH DX
  MUL DX
  ADD CX,AX
  XOR AX,AX
  POP DX
  INC DX
  JMP NEXTCHAR

LASTCHAR:
 
  PUSH CX
  XOR AX,AX
  XOR BX,BX
  XOR CX,CX
  XOR DX,DX
  MOV SI,OFFSET INPUTTER

SUMNATION:

  LODSB
  TEST AL,AL
  JZ FINISHED
  MOV AH,AL
  XOR AL,AL
  MOV DI,08h

ITTERATE:

  MOV CX,AX
  XOR CX,BX
  TEST CX,8000h
  JZ JUMPER
  ADD BX,BX
  XOR BX,1021h
  JMP JUMPY

JUMPER:

  SHL BX,1h

JUMPY:

  SHL AX,1h
  DEC DI
  JNZ ITTERATE
  JMP SUMNATION

FINISHED:

  ADD BX,63h
  MOV AX,BX
  POP BX
  XOR CX,CX
  MOV BYTE PTR [OFFSET HEXA],AH
  MOV BYTE PTR [OFFSET HEXA+1],AL
  MOV BYTE PTR [OFFSET HEXA+2],BH
  MOV BYTE PTR [OFFSET HEXA+3],BL
  XOR AX,AX
  XOR BX,BX
  XOR DX,DX
  MOV SI,OFFSET HEXA
  MOV AH,09h
  MOV DX,OFFSET TEXTD
  INT 21h
  XOR AX,AX

ALPHACHARLOOP:

  LODSB
  SHL AX,1h
  SHL AX,1h
  SHL AX,1h
  SHL AX,1h
  SHR AL,1h
  SHR AL,1h
  SHR AL,1h
  SHR AL,1h
  CMP AL,0Ah
  JL ALPHA
  ADD AL,37h
  JMP ALPHANUMERIC

ALPHA:

  ADD AL,30h

ALPHANUMERIC:

  CMP AH,0Ah
  JL ALPHAB
  ADD AH,37h
  JMP ALPHANUMERICB

ALPHAB:

  ADD AH,30h

ALPHANUMERICB:

  MOV BX,AX
  MOV AH,02h
  MOV DL,BH
  INT 21h
  MOV AH,02h
  MOV DL,BL
  INT 21h
  XOR AX,AX
  CMP SI, OFFSET HEXA+4
  JNZ ALPHACHARLOOP
  MOV AH,02h
  MOV DL,0Ah
  INT 21h
  MOV AH,09h
  MOV DX,OFFSET TEXTE
  INT 21h
  XOR AX,AX
  INT 16h
  INT 20h


TEXT  DB  'Username: $'
TEXTB DB  'Winzip 7.* Keygen$'
TEXTC DB  'Cracked by m101 for Phrozen Crew$'
TEXTD DB  'Your Serial is: $'
TEXTE DB  'Press any key to quit...$'
INPUTTER  DB  'aaa$'
HEXA  DB  'aaaa$'
MAIN ENDS
END START

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


Although the keygen isnt as small as it could be, for a tiny 376 bytes after being compiled i wouldnt complain. I encourage you to just continuously keep going through the code until it makes more sense to you. After reading this, you hopefully will have a better understanding of how programs generate and check serials.


Source:
http://www.arson-network.com/tutorials/create2.html
http://woodmann.com/krobar/
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    TechAmok Forum Index -> Software All times are GMT - 5 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2016 phpBB Group