Home    Training    Downloads    Tutorials    Arbitary    Get Fate    Proxy Info
 
Training session 35: Keygen Creation #3
Difficulty: Medium
Learn how to create a slightly more difficult keygen
Creator: m101


Target: V 2000 (SR2)
URL: http://www.fileviewer.com

The aim of this tutorial is not to teach you how to read assembly, or for that matter, how to read commented assembly, but how to comment it yourself and follow it to understand what is going on. Through this you will learn much more than you do off just reading. I assume you have an understanding of assembly already if you are reading this aswell. The target is V 2000 (SR2) and is available at http://www.fileviewer.com. You will need Softice, Win32DASM, procdump, hiew and an assembler such as TASM or MASM if you are planning on creating the keygen.

After you have installed the program, start it up and watch what it does. Take notice that there is a register button in the splash screen, and in the help menu. Click on either one, enter some bogus crap, and see what the message says. "The User Name and/or Registration Code are incorrect." How very nice, have a look down further: "If you are just trying to break the registration code, then I wish you luck." Looks like our programmer respects the arts ;)

Open up Win32DASM and disassemble V.exe. Generally we would backup V first, but since we arent actually aiming to patch it, its fine for the time being. Get into the dead listing 'Refs > String Data References' and look around for our little message. To my delight, you wont find anything. Since we cant go straight to the dead listing for the dialog, have a look around and be a bit inventive about a choice. "Registration" How fitting, double click on it and have a look where we have landed ourselves. To me it looks like the happy result of a crack:
:004186CF 6A0B                    push 0000000B
:004186D1 50                      push eax

* Possible StringData Ref from Data Obj ->"Registration"
                                  |
:004186D2 68E0264800              push 004826E0
:004186D7 B9187D4800              mov ecx, 00487D18
:004186DC E89F790100              call 00430080
:004186E1 8B4C2424                mov ecx, dword ptr [esp+24]
:004186E5 6A01                    push 00000001
:004186E7 51                      push ecx
:004186E8 8BCD                    mov ecx, ebp
:004186EA E8E1020000              call 004189D0
:004186EF 5F                      pop edi
:004186F0 5E                      pop esi
:004186F1 5D                      pop ebp
:004186F2 5B                      pop ebx
:004186F3 83C40C                  add esp, 0000000C
:004186F6 C20800                  ret 0008

Trace the call back a little bit:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00418666(C)
|
:00418679 8BFE                    mov edi, esi
:0041867B 83C9FF                  or ecx, FFFFFFFF
:0041867E 33C0                    xor eax, eax
:00418680 F2                      repnz
:00418681 AE                      scasb

Hows this, referenced by a conditional jump then some dialog messages. Looks to me like a good result even more now. Go back through the jump and you will find yourself looking at this:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041864A(C)
|
:00418651 8D4C2424                lea ecx, dword ptr [esp+24]
:00418655 6A01                    push 00000001
:00418657 8D5D23                  lea ebx, dword ptr [ebp+23]
:0041865A 51                      push ecx
:0041865B 50                      push eax
:0041865C 53                      push ebx
:0041865D 8BCD                    mov ecx, ebp
:0041865F E82C010000              call 00418790
:00418664 85C0                    test eax, eax
:00418666 7511                    jne 00418679
:00418668 8BCD                    mov ecx, ebp
:0041866A E841FFFFFF              call 004185B0
:0041866F 5F                      pop edi
:00418670 5E                      pop esi
:00418671 5D                      pop ebp
:00418672 5B                      pop ebx
:00418673 83C40C                  add esp, 0000000C
:00418676 C20800                  ret 0008

Lovely, a test to see if our result is correct, then a call to some dialog which may be a good result. Trace it back even further:
* Referenced by a CALL at Addresses:
|:00418CEE   , :0041F41E   
|
:00418610 8B442408                mov eax, dword ptr [esp+08]
:00418614 83EC0C                  sub esp, 0000000C
:00418617 53                      push ebx
:00418618 55                      push ebp
:00418619 56                      push esi
:0041861A 57                      push edi
:0041861B 8BE9                    mov ebp, ecx
:0041861D 50                      push eax
:0041861E E8DD000000              call 00418700
:00418623 85C0                    test eax, eax
:00418625 7511                    jne 00418638
:00418627 8BCD                    mov ecx, ebp
:00418629 E882FFFFFF              call 004185B0
:0041862E 5F                      pop edi
:0041862F 5E                      pop esi
:00418630 5D                      pop ebp
:00418631 5B                      pop ebx
:00418632 83C40C                  add esp, 0000000C
:00418635 C20800                  ret 0008

So it looks like this is the start of the protection scheme. Remember that there are two calls to the registration dialog? Links nicely with the two references by calls we can see. Trace one back:
:00418CE6 E8056F0100              call 0042FBF0
:00418CEB 50                      push eax
:00418CEC 8BCE                    mov ecx, esi
:00418CEE E81DF9FFFF              call 00418610
:00418CF3 85C0                    test eax, eax
:00418CF5 7456                    je 00418D4D
:00418CF7 8BCE                    mov ecx, esi
:00418CF9 E8A2FBFFFF              call 004188A0

Well, this is all nice and good to have found the routine, but how can we be sure that its correct? Notice all those calls to MFC42.Ordinal:0***. Well this means that we arent going to be able to break on GetDlgItemTextA or anything similar, so heres a nice way to use the info we have from Win32DASM in Softice. Open up Softices Symbol Loader and load up V.exe, Click on run and click yes to any dialogs that come up. Softice will break open at the programs entry point.
:00468B3A 55                      push ebp

Heres the thing, when you loaded it in softice, the program was forced to give us standard memory address from the image base. What does this mean to us? It means we can use the addresses we gathered from softice to break on the routine. Lets set a breakpoint before both calls to check which call we are dealing with. Type "u 418cee" into softice and double click to highlight the first line. Then type "u 41f41e" and again double click the first line. You have now breakpointed the instructions. Get out of Softice and click on either registration buttons and enter some whatever you like and click Register. BAM! Straight into softice. For me I clicked on the help menu register button, and I got the second call. You should see something like:
:0041F405 8D4C2400                lea ecx, dword ptr [esp]
:0041F409 E8F2070100              call 0042FC00
:0041F40E 50                      push eax
:0041F40F 8D4C2404                lea ecx, dword ptr [esp+04]
:0041F413 E8D8070100              call 0042FBF0
:0041F418 50                      push eax
:0041F419 B9487E4800              mov ecx, 00487E48
:0041F41E E8ED91FFFF              call 00418610
:0041F423 85C0                    test eax, eax
:0041F425 740A                    je 0041F431
:0041F427 B9487E4800              mov ecx, 00487E48
:0041F42C E86F94FFFF              call 004188A0

Do a few checks on the data and you will find that your username was pushed before the call is made. Press F10 to step over the jump. Youll be chucked straight back to the Bad Cracker dialog. From this we can pretty safely say that this routine is the cause of our troubles. Now we know what we are looking at, get back into Win32DASM and check out that call:
* Referenced by a CALL at Addresses:
|:00418CEE   , :0041F41E   
|
:00418610 8B442408                mov eax, dword ptr [esp+08]
:00418614 83EC0C                  sub esp, 0000000C
:00418617 53                      push ebx
:00418618 55                      push ebp
:00418619 56                      push esi
:0041861A 57                      push edi
:0041861B 8BE9                    mov ebp, ecx
:0041861D 50                      push eax			<push our serial
:0041861E E8DD000000              call 00418700			<do something with it
:00418623 85C0                    test eax, eax			<is result bad?
:00418625 7511                    jne 00418638			<no, then continue
:00418627 8BCD                    mov ecx, ebp
:00418629 E882FFFFFF              call 004185B0
:0041862E 5F                      pop edi
:0041862F 5E                      pop esi
:00418630 5D                      pop ebp
:00418631 5B                      pop ebx
:00418632 83C40C                  add esp, 0000000C
:00418635 C20800                  ret 0008

To me this looks like the program is testing the serial for characteristics, so lets check out that call:
* Referenced by a CALL at Address:
|:0041861E   
|
:00418700 8B542404                mov edx, dword ptr [esp+04]
:00418704 53                      push ebx
:00418705 55                      push ebp
:00418706 56                      push esi
:00418707 8BE9                    mov ebp, ecx
:00418709 57                      push edi
:0041870A 8BFA                    mov edi, edx
:0041870C 83C9FF                  or ecx, FFFFFFFF
:0041870F 33C0                    xor eax, eax
:00418711 F2                      repnz
:00418712 AE                      scasb
:00418713 F7D1                    not ecx
:00418715 49                      dec ecx
:00418716 83F912                  cmp ecx, 00000012
:00418719 7409                    je 00418724

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00418759(C)
|
:0041871B 5F                      pop edi
:0041871C 5E                      pop esi
:0041871D 5D                      pop ebp
:0041871E 33C0                    xor eax, eax
:00418720 5B                      pop ebx
:00418721 C20400                  ret 0004

Aint this just great. It counts the characters in our serial we entered. I would recommend you check in softice to see just how it does this if you cant see it already. The most interesting line here is:
:00418716 83F912                  cmp ecx, 00000012

Whats this mean? Well it checks if our serial is 12 characters long doesnt it? This is a thing alot of newbies get trapped in, they see the 12 and they presume its in decimal. Crack open a calculator if you neeed to and find out what 12 in hexidecimal is when converted to decimal. 18, Our serial has to be 18 characters long. You might like to check this for yourself though. Set your serial to '012345678912345678' and see what the call returns. As you can see, the call now jumps like we want it to.
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00418625(C)
|
:00418638 8B742420                mov esi, dword ptr [esp+20]
:0041863C 83C9FF                  or ecx, FFFFFFFF
:0041863F 8BFE                    mov edi, esi
:00418641 33C0                    xor eax, eax
:00418643 F2                      repnz
:00418644 AE                      scasb
:00418645 F7D1                    not ecx
:00418647 49                      dec ecx
:00418648 8BC6                    mov eax, esi
:0041864A 7505                    jne 00418651

* Possible StringData Ref from Data Obj ->"EVALUATION COPY"
                                  |
:0041864C B86C304800              mov eax, 0048306C

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041864A(C)
|
:00418651 8D4C2424                lea ecx, dword ptr [esp+24]
:00418655 6A01                    push 00000001
:00418657 8D5D23                  lea ebx, dword ptr [ebp+23]
:0041865A 51                      push ecx
:0041865B 50                      push eax
:0041865C 53                      push ebx
:0041865D 8BCD                    mov ecx, ebp
:0041865F E82C010000              call 00418790
:00418664 85C0                    test eax, eax
:00418666 7511                    jne 00418679
:00418668 8BCD                    mov ecx, ebp
:0041866A E841FFFFFF              call 004185B0
:0041866F 5F                      pop edi
:00418670 5E                      pop esi
:00418671 5D                      pop ebp
:00418672 5B                      pop ebx
:00418673 83C40C                  add esp, 0000000C
:00418676 C20800                  ret 0008

Since im not doing my normal commenting of the this code anyway, its your turn to work out what is going on, or atleast try to work it out. Ill give you a hint, we need it to jump again this time ;) Just kidding ya, I couldnt let you tackle the main routine all by yourself:
* Referenced by a CALL at Addresses:
|:0041865F   , :00418923   , :00418F3B   
|
:00418790 8B542408                mov edx, dword ptr [esp+08]
:00418794 83EC08                  sub esp, 00000008
:00418797 33C0                    xor eax, eax
:00418799 53                      push ebx
:0041879A 56                      push esi
:0041879B 8BF1                    mov esi, ecx
:0041879D 57                      push edi
:0041879E 8BFA                    mov edi, edx
:004187A0 83C9FF                  or ecx, FFFFFFFF
:004187A3 F2                      repnz
:004187A4 AE                      scasb
:004187A5 F7D1                    not ecx
:004187A7 49                      dec ecx
:004187A8 51                      push ecx			<push length
:004187A9 52                      push edx			<push username
:004187AA E811F80100              call 00437FC0			<calculate part of serial
:004187AF 8B4C2420                mov ecx, dword ptr [esp+20]
:004187B3 8BD8                    mov ebx, eax

Lets take a look into that call:
* Referenced by a CALL at Addresses:
|:004187AA   , :004187F1   
|
:00437FC0 56                      push esi
:00437FC1 8B74240C                mov esi, dword ptr [esp+0C]
:00437FC5 83C8FF                  or eax, FFFFFFFF
:00437FC8 33C9                    xor ecx, ecx
:00437FCA 85F6                    test esi, esi
:00437FCC 762C                    jbe 00437FFA
:00437FCE 53                      push ebx
:00437FCF 57                      push edi
:00437FD0 8B7C2410                mov edi, dword ptr [esp+10]
This is all pretty boring crap above, I wouldnt bother too much with it. Here comes the juicy bits:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00437FF6(C)
|
:00437FD4 8A1439                  mov dl, byte ptr [ecx+edi]
:00437FD7 8BD8                    mov ebx, eax
:00437FD9 81E2FF000000            and edx, 000000FF
:00437FDF 81E3FF000000            and ebx, 000000FF
:00437FE5 33D3                    xor edx, ebx
:00437FE7 C1E808                  shr eax, 08
:00437FEA 8B1495E0514800          mov edx, dword ptr [4*edx+004851E0]
:00437FF1 33C2                    xor eax, edx
:00437FF3 41                      inc ecx
:00437FF4 3BCE                    cmp ecx, esi
:00437FF6 72DC                    jb 00437FD4
This aint exactly the nicest routine for a cracker to imitate because of that damn table. Youll see why in a minute. Have a go at commenting this code, done worry too much about the table just yet. It should endup looking something like this:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00437FF6(C)
|
:00437FD4 8A1439                  mov dl, byte ptr [ecx+edi]		<take next byte of name
:00437FD7 8BD8                    mov ebx, eax				<set ebx = total
:00437FD9 81E2FF000000            and edx, 000000FF			<clear edh
:00437FDF 81E3FF000000            and ebx, 000000FF			<clear ebh
:00437FE5 33D3                    xor edx, ebx				<edx = edx xor ebx
:00437FE7 C1E808                  shr eax, 08				<move total right 1 byte
:00437FEA 8B1495E0514800          mov edx, dword ptr [4*edx+004851E0]	<edx = 4*edx+004851e0
:00437FF1 33C2                    xor eax, edx				<total = total xor edx
:00437FF3 41                      inc ecx				<next character
:00437FF4 3BCE                    cmp ecx, esi				<end of username?
:00437FF6 72DC                    jb 00437FD4				<no, then loop
Examine that, if you dont get it the first time, follow it through in softice and examine it again till you can understand it. Now to deal with that table, a table is pretty much just an array of values. To make a keygen we will need a copy of this table, and well, I dont know about you, but recording every single possible value on paper and then placing it into our keygen doesnt quite fit my defenition of interesting. Just to discourage you a little more from wasting your time, look at 4*EDX+004851E0. EDX only contains one byte, so EDX can be anything between 00 and FF. This tells us that we would have to copy down 4*FF+4 bytes, which is 400h bytes, or in decimal 1024. So we are at a little bit of a dilemna, we need someway to dump our data, this is where procdump comes to the rescue. Open up procdump and go down the list till we find V.exe. Right click and select partial dump, we want to dump from 004851E0 for a length of 400. Ive included the table data in table.dmp with this file if you dont have procdump handy. Make sure you use only capitals, and then click dump. Select where you want to dump it to and you now have your table. Now return from the call and you will see this:
:004187AA E811F80100              call 00437FC0
:004187AF 8B4C2420                mov ecx, dword ptr [esp+20]
:004187B3 8BD8                    mov ebx, eax
:004187B5 8B44242C                mov eax, dword ptr [esp+2C]
:004187B9 83C408                  add esp, 00000008
:004187BC 85C0                    test eax, eax
:004187BE 7410                    je 004187D0
:004187C0 33C0                    xor eax, eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004187CE(C)
|
:004187C2 8A1408                  mov dl, byte ptr [eax+ecx]
:004187C5 32D3                    xor dl, bl
:004187C7 881408                  mov byte ptr [eax+ecx], dl
:004187CA 40                      inc eax
:004187CB 83F809                  cmp eax, 00000009
:004187CE 7CF2                    jl 004187C2

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004187BE(C)
|
:004187D0 8B7905                  mov edi, dword ptr [ecx+05]
:004187D3 8B01                    mov eax, dword ptr [ecx]
:004187D5 8A4904                  mov cl, byte ptr [ecx+04]
:004187D8 8D54240C                lea edx, dword ptr [esp+0C]

* Possible Reference to Dialog: DialogID_00A9, CONTROL_ID:0007, "&No"
                                  |
:004187DC 6A07                    push 00000007
:004187DE 52                      push edx
:004187DF 89442414                mov dword ptr [esp+14], eax
:004187E3 884C2418                mov byte ptr [esp+18], cl
:004187E7 C644241913              mov [esp+19], 13
:004187EC C644241A3B              mov [esp+1A], 3B
:004187F1 E8CAF70100              call 00437FC0
:004187F6 83C408                  add esp, 00000008
:004187F9 3BC7                    cmp eax, edi
:004187FB 740B                    je 00418808

Now this is where it becomes a little hard to explain, so ill show you in english rather than assembly. First thing it does is takes the AL portion of the checksum and multiplies it by every character in the serial we entered. Then it takes the first 5 bytes of the serial, appends 13h and 3Bh, before sending it through the checksum algorithm again. The result is left in EAX, so from this we can say that it returns the last 4 bytes of the serial in reverse order as the last four bytes of our serial. If this matches the values from the entered serial, the serial is correct and the algorithm continues to the last check. To check if im right, lets try an example. For 'm101' the first checksum returns 'AC', so lets set an entire serial to 'FFFFFFFFFFFFFFFFFF' and then xor it by AC, the result is '535353535353535353'. Take this value and place it into the algorithm. After the call EAX = 918363A4, so lets make our serial return the same value. Reverse the string to 'A4638391' and then xor it by 'AC' to arrive with '08CF2F3D' and our serial becomes '535353535308CF2F3D'. Trace the program past the call and see what we get. As you can see, the comparison returns that our serial is valid for that call. From this we can safely say we can calculate the last 8 characters if we have the first 10. The next thing to do is check to see what the last check is:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004187FB(C)
|
:00418808 8B54240E                mov edx, dword ptr [esp+0E]
:0041880C 668B44240C              mov ax, word ptr [esp+0C]
:00418811 8B4C2420                mov ecx, dword ptr [esp+20]
:00418815 33DA                    xor ebx, edx
:00418817 663BC3                  cmp ax, bx
:0041881A 668901                  mov word ptr [ecx], ax
:0041881D 740B                    je 0041882A
Once again you should have a go at commenting the code yourself. Basically what this does is xor the fourth and third bytes of the password with the last two bytes of the first checksum. If the four byte result equals the first two bytes of the serial, the check passes. If you reverse this you will find that if we xor the first two bytes with the checksum, we are left with the third and fourth bytes. Dont forget to xor them with the checksum to be given plain text. Here is basically how a valid serial is generated:
AX_value = checksum(username)

Byte	Value
------------------------------------------------------
1	[FF]
2	[AL_value xor FF]
3	[AL_value xor FF]
4	[AL_value xor (AH of ([1-2] xor AX_Value))]
5	[AL_value xor FF]
6-9	[checksum(foreach xor by AL([1-5][13 3B])]


Here is my source code to the keygen, I decided to do it in MASM this time. Before looking at it, i recommend you try make your own keygen in whatever language you are most comfortable with. Even if you dont want to code your own keygen, try commenting mine.

--------------------------------------------------------------------------------------------------------------
v2000.asm
--------------------------------------------------------------------------------------------------------------

;V2000 Keygen by m101 for Phrozen Crew

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.data
ClassName db "SimpleWinClass",0
AppName  db "V 2000 Keygen cracked by m101 for Phrozen Crew",0
ButtonText db "Generate Key",0
StartupString db "Please enter a name...",0
EditClassName db "edit",0
EditID equ 2
EditID2 equ 3
ButtonID equ 1
ButtonClassName db "button",0
IDM_GETTEXT equ 3
Serial db "FFFFFFFFFFFFFFFFFF"
table_buffer db 1024 dup(?)
Serial2 db "FFFFFFFFF"


.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndButton HWND ?
hwndEdit HWND ?
hwndEdit2 HWND ?
buffer db 512 dup(?)

.code
start:
	invoke GetModuleHandle, NULL
	mov    hInstance,eax
	invoke GetCommandLine
	mov    CommandLine,eax
	invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
	invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
	LOCAL wc:WNDCLASSEX
	LOCAL msg:MSG
	LOCAL hwnd:HWND
	mov   wc.cbSize,SIZEOF WNDCLASSEX
	mov   wc.style, CS_HREDRAW or CS_VREDRAW
	mov   wc.lpfnWndProc, OFFSET WndProc
	mov   wc.cbClsExtra,NULL
	mov   wc.cbWndExtra,NULL
	push  hInstance
	pop   wc.hInstance
	mov   wc.hbrBackground,COLOR_BTNFACE+1
	mov   wc.lpszMenuName,NULL
	mov   wc.lpszClassName,OFFSET ClassName
	invoke LoadIcon,NULL,IDI_APPLICATION
	mov   wc.hIcon,eax
	mov   wc.hIconSm,eax
	invoke LoadCursor,NULL,IDC_ARROW
	mov   wc.hCursor,eax
	invoke RegisterClassEx, addr wc
	INVOKE CreateWindowEx,WS_EX_CONTROLPARENT	 or DS_CENTER,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,305,150,NULL,NULL,\
           hInst,NULL
	mov   hwnd,eax
	invoke ShowWindow, hwnd,SW_SHOWNORMAL
	invoke UpdateWindow, hwnd
	.WHILE TRUE
		invoke GetMessage, ADDR msg,NULL,0,0
		.BREAK .IF (!eax)
		invoke TranslateMessage, ADDR msg
		invoke DispatchMessage, ADDR msg
	.ENDW
	mov     eax,msg.wParam
	ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
	.IF uMsg==WM_DESTROY
		invoke PostQuitMessage,NULL
	.ELSEIF uMsg==WM_CREATE
		invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
                        WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
                        ES_AUTOHSCROLL,\
                        50,20,200,25,hWnd,EditID,hInstance,NULL
		mov  hwndEdit,eax
		invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
                        WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
                        ES_AUTOHSCROLL or ES_READONLY,\
                        50,45,200,25,hWnd,EditID2,hInstance,NULL
		mov  hwndEdit2,eax
            invoke SetWindowText,hwndEdit2,ADDR StartupString 
		invoke SetFocus, hwndEdit
		invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonText,\
                        WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
                        80,75,140,25,hWnd,ButtonID,hInstance,NULL
		mov  hwndButton,eax
	.ELSEIF uMsg==WM_COMMAND
		mov eax,wParam
		.IF lParam==0
			.IF ax==IDM_GETTEXT
                        push edi
                        push esi
				invoke GetWindowText,hwndEdit,ADDR buffer,512
                        invoke lstrlen, ADDR buffer
                        mov esi, eax
                        xor ecx,ecx
                        xor edx,edx
                        or eax, -01h
looper:
                        mov dl, byte ptr [ecx + buffer]
                        mov ebx, eax
                        and edx, 000000FFh
                        and ebx, 000000FFh
                        xor edx, ebx
                        shr eax, 08
                        mov edx, dword ptr [4*edx+table_buffer]
                        xor eax, edx
                        inc ecx
                        cmp ecx, esi
                        jb looper
                        push ax
                        mov [Serial], -01h
                        mov [Serial2], al
                        mov [Serial+1], al
                        mov [Serial2+1], -01h
                        mov [Serial+2], al
                        mov [Serial2+2], -01h
                        mov [Serial+4], al
                        mov [Serial2+4], -01h
                        mov dh,-01h
                        xor dx,ax
                        xor bl,dh
                        mov [Serial2+3],ah
                        xor al, dh
                        mov [Serial+3],al
                        mov [Serial2+5],13h
                        mov [Serial2+6],3Bh
                        mov esi,07h
                        xor ecx,ecx
                        xor edx,edx
                        or eax, -01h
looper2:
                        mov dl, byte ptr [ecx + Serial2]
                        mov ebx, eax
                        and edx, 000000FFh
                        and ebx, 000000FFh
                        xor edx, ebx
                        shr eax, 08
                        mov edx, dword ptr [4*edx+table_buffer]
                        xor eax, edx
                        inc ecx
                        cmp ecx, esi
                        jb looper2
                        mov dword ptr [Serial+5], eax
                        lea esi, Serial
                        xor ecx,ecx
                        pop dx
converter:
                        xor ax,ax
                        lodsb
                        cmp ecx, 10
                        jl conv2
                        xor al, dl
conv2:
                        shl ax, 4h
                        shr al, 4h
                        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 [buffer+ecx], ah
                        inc ecx
                        mov [buffer+ecx], al
                        inc ecx
                        cmp ecx, 18
                        jl converter
                        mov [buffer+ecx], 0h
                        invoke SetWindowText,hwndEdit2,ADDR buffer 

                        pop esi
                        pop edi
			.ELSE
				invoke DestroyWindow,hWnd
			.ENDIF
		.ELSE
			.IF ax==ButtonID
				shr eax,16
				.IF ax==BN_CLICKED
					invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
				.ENDIF
			.ENDIF
		.ENDIF
	.ELSE
		invoke DefWindowProc,hWnd,uMsg,wParam,lParam		
		ret
	.ENDIF
	xor eax,eax
	ret
WndProc endp
end start

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

As you should realise, the keygen WILL NOT work in its current state. To make it work, you have to include the Table of values you dumped earlier. To do this, first compile the keygen, then load up hiew. Open 'v2000.exe' and hit F4 then F2. Do a search for 'FFFF'. You can safely dump the table after the last 'F'. The reason for this was in the creation of the keygen:
Serial db "FFFFFFFFFFFFFFFFFF"
table_buffer db 1024 dup(?)
Serial2 db "FFFFFFFFF"

If you notice the offset 'table_buffer' begins directly after offset 'Serial'. This means the program will reference our table directly from this location like we want it to do. Place the cursor directly after the last 'FF' and then press '*'. Now scroll down and put the cursor directly before the next 'FF' and press '*' again. This has just marked the dump area, but not the dump beginning, so you will have to scroll up and put the cursor back on the first position. Now press 'CTRL + F2', put in the location of 'table.dmp' and leave the offset blank and select 'AS IS'. After this you will have a fully working keygen!
Name

URL or Email

Message