:::::::::::::::::::::  m E X / c 4 N  T U T O R I A L  D I V I S I O N ::::::::::::::::::::::::::

Tutor      : CoRN2
Editor     : Notepad (fullscreen 800x600 wit' wordwrap)
Audience   : Beginners an' Newbies :)
Greets     : josephCo, for help when I needed it the most.
Target     : DirectNet v1.1
Rev Date   : 28/03/98

Check out our homepage, http://mex98.home.ml.org/

Hey, lets learn about keygens. Many people seem to think that these things are difficult, and I'm sure there are some real bitches out there, but as a rule, if you can get a valid serial, you can keygen the mf. ;)

This target isn't the hardest target in the world... try to learn the method if nothing else :)

Please note that this tutorial does NOT cover getting serials as such, so the following instructions are brief, if you need to know more about grabbing serials, try my winzip tute (bah) or any of the others on the mex homepage.

1) Click Register, and type in your details, I used CoRN2 and 111222333444

2) Before you click OK, ctrl-d into SoftIce, lets try our default API calls, GetWindowTextA, and GetDlgItemTextA. 

3) Done? F5 to get back to the prog. Click OK.

4) Boom, SoftIce at the start of the GetDlgItemTextA call, F11 to get back to our program.

5) Trace down a few lines, at the CALL ESI line, GetDlgItemTextA pops up again, since ESI now points to it, f11 again to get back.

6) Now we see two pushes, PUSH 407020 and PUSH 407120. The second one is the address of our NAME field, then a CALL, hmm, wonder what that call does. Step over it (f10) NOT trace (f8). 

Now we see that our CODE is pushed ( PUSH EAX ) and then PUSH 407020 (again). Hmm these are params for lstrcmp - which compares two strings. This smells very much like a serial compare. Look at 407020. Hmmmmmmmmmmm, "9031-79647-2392-654" wonder what that could be...

Obviously then, the PUSH 407020 before the CALL sets up a bit of memory to store our serial, combine that with the PUSH of our NAME, smells a bit suspect huh? 

F5 to get back to the prog, Click OK again, and get back to the offset 40118C. Lets trace into this call this time f8 and make a keygen :)

					-=-

Ok, now we seem to have found our serial generation routine... what now? Well, quite simply, we mimic its operation.

We've seen that the serial number is constructed from 4 parts. This *could* mean that there are four parts to our serial generation routine. So lets take a look at the deadlist.

* Referenced by a CALL at Addresses:
|:004010F7   , :0040118C   			
|
:00401230 81EC00010000            sub esp, 00000100			
:00401236 A034734000              mov al, byte ptr [00407334]		; Irrelevant Code.
:0040123B 88442400                mov byte ptr [esp], al
:0040123F 53                      push ebx
:00401240 56                      push esi
:00401241 33C0                    xor eax, eax
:00401243 57                      push edi
:00401244 B93F000000              mov ecx, 0000003F
:00401249 8D7C240D                lea edi, dword ptr [esp+0D]
:0040124D 55                      push ebp
:0040124E F3                      repz
:0040124F AB                      stosd
:00401250 66AB                    stosw					
:00401252 BD6B000000              mov ebp, 0000006B			; Note This.
:00401257 6834734000              push 00407334				; Irrelevant Code.
:0040125C AA                      stosb
:0040125D 8BB4241C010000          mov esi, dword ptr [esp+0000011C]	; Computed serial addr.
:00401264 56                      push esi				; push it

* Reference To: USER32.wsprintfA, Ord:0262h
                                  |
:00401265 FF1588934000            Call dword ptr [00409388]		; Call wsprintf
:0040126B 8B9C241C010000          mov ebx, dword ptr [esp+0000011C]	; ebx = addr. of NAME
:00401272 83C408                  add esp, 00000008			; adjust stack
:00401275 8BC3                    mov eax, ebx				; eax = addr. of NAME

* Reference To: USER32.CharNextA, Ord:0021h
                                  |
:00401277 8B3D7C934000            mov edi, dword ptr [0040937C]		; edi=addr. of function
:0040127D 803B00                  cmp byte ptr [ebx], 00		; check is name char is 0
:00401280 7416                    je 00401298				; if so quit

Ok, up until now, we've seen very little of interest. 00401230-401250 clears out a section of memory, and gets ready for the serial generation. We can ignore it all. Then we see the address where the function will store the valid serial it computes ([esp+0000011C])

Now ebx is made to refer to the NAME field, in this case, it points to the first letter of the name which we entered. Then EDI is made to point to the API Call, USER32.CharNextA. So, whenever we see a reference to CALL EDI, we know that CharNextA is the real call. (Note that this is a typical Compiler optimization)

Finally the first character of our NAME is checked for NULL (00), if this is empty then the function exits. Ok, lets continue:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:     ; PART ONE OF SERIAL GEN
|:00401296(C)
|
:00401282 0FBE08                  movsx ecx, byte ptr [eax]	  ; ECX=character of our name
:00401285 2BE9                    sub ebp, ecx			  ; subtract letter from EBP
:00401287 8BD1                    mov edx, ecx			  ; make a copy of the character
:00401289 8D0C49                  lea ecx, dword ptr [ecx+2*ecx]  ; ECX=char*2+char
:0040128C 50                      push eax			  ; push NAME string
:0040128D 8D6CCD00                lea ebp, dword ptr [ebp+8*ecx]  ; ebp=ebp+ecx
:00401291 FFD7                    call edi			  ; CALL CharNextA
:00401293 803800                  cmp byte ptr [eax], 00	  ; have we processed all chars?
:00401296 75EA                    jne 00401282			  ; if not go back to 401282

Ok, here were have part one of our serial generation routine. Lets go through it. 
Firstly, notice that EAX is a pointer to our NAME, and the function CharNextA increments EAX so that it points to the next character of the name. Now we know this we can look at the function.
Basically the formula is:

	ecx = character
	ebp = ebp-character
	ecx = ecx * 2 + ecx
	ebp = ebp + ecx * 8

And thats it, lets look at it with the name 'CoRN2' (note that I'll convert hex2dec )

	ecx = 67	   ( 67 is the ascii code for 'C' )
	ebp = 107 - 67     ( remember at 401252, ebp is set to 6B hex which is 107 decimal )
	ecx = (67 * 2) + 67  
	ebp = 40 + (67 * 8)

Then the pointer is incremented by ONE by CharNextA, so the next letter would be 'o', but this time ebp is NOT 107, since its been changed.

At the end of this process, the value of EBP is 9031 decimal, then we see this:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401280(C)
|
:00401298 8D442410                lea eax, dword ptr [esp+10]	; where to put the string
:0040129C 55                      push ebp			; push our number

* Possible StringData Ref from Data Obj ->"%d-"			; This is a C directive, %d is 
				  |				; the number that has been 					  |				; pushed (ebp) 
                                  |
:0040129D 6844734000              push 00407344			
:004012A2 50                      push eax			; push where to put the string 
7
* Reference To: USER32.wsprintfA, Ord:0262h
                                  |
:004012A3 FF1588934000            Call dword ptr [00409388]	; call wsprintfA
:004012A9 8D44241C                lea eax, dword ptr [esp+1C]	; eax = pointer to string
:004012AD 83C40C                  add esp, 0000000C		; fix stack
:004012B0 50                      push eax			; push our string (9031-)
:004012B1 56                      push esi			; push dest for final string

* Reference To: KERNEL32.lstrcatA, Ord:028Dh
                                  |
:004012B2 FF15B4924000            Call dword ptr [004092B4]	; string concatenate (join)
:004012B8 8BC3                    mov eax, ebx			; eax = our NAME string
:004012BA 803B00                  cmp byte ptr [ebx], 00	; check for NULL character
:004012BD 741A                    je 004012D9			; if so, exit function

Ok, lets explain this. Basically what the above stuff does is to convert the number 9031 into a STRING in memory so we can build the valid serial number. The wsprintfA function does this, and adds a '-' to it. So eax now points to the string "9031-", this is then copied to the destination for the final generated serial to go. We've generated the first part of our serial number. 

Onward:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:	; PART TWO 
|:004012D7(C)
|
:004012BF 0FBE08                  movsx ecx, byte ptr [eax]	  ; ecx = character
:004012C2 8BD1                    mov edx, ecx			  ; edx = ecx
:004012C4 50                      push eax			  ; push name string
:004012C5 8D0CC9                  lea ecx, dword ptr [ecx+8*ecx]  ; ecx = ecx * 8 + ecx
:004012C8 8D0C89                  lea ecx, dword ptr [ecx+4*ecx]  ; ecx = ecx * 4 + ecx
:004012CB 8D144A                  lea edx, dword ptr [edx+2*ecx]  ; edx = ecx * 2 + edx
:004012CE 8D6C5500                lea ebp, dword ptr [ebp+2*edx]  ; ebp = edx * 2 + ebp
:004012D2 FFD7                    call edi			  ; CALL NextCharA
:004012D4 803800                  cmp byte ptr [eax], 00 	  ; by now we should know
:004012D7 75E6                    jne 004012BF		          ; what this is...

Ok, same again. Formula:

	ecx = character
 	edx = character
	ecx = ecx * 8 + ecx
	ecx = ecx * 4 + ecx
	edx = ecx * 2 + edx
	ebp = edx * 2 + ebp

For "CoRN2" :

	ecx = 67
	edx = 67
	ecx = 67 * 8 + 67
	ecx = 603 * 4 + 603
	edx = 3015 * 2 + 67
	ebp = 6097 * 2 + 9031		( note this is the previous number we calculated )

Repeat this for all characters of the name. At this point we should have 21225 in EBP, repeat for all characters.
The code that follows this does exactly the same as that above. When we get to this point, we should have in the serial destination: "9031-79647-"

Once again:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401310(C)
|
:00401300 0FBE08                  movsx ecx, byte ptr [eax]
:00401303 50                      push eax
:00401304 8DAC895E080000          lea ebp, dword ptr [ecx+4*ecx+0000085E]
:0040130B FFD7                    call edi
:0040130D 803800                  cmp byte ptr [eax], 00
:00401310 75EE                    jne 00401300

Oh, a nice one, they must be getting bored. Formula:

	ecx = character
	ebp = ecx * 4 + ecx + 0000085E
	
WoW. By now you should get the process. At the end of this, we have 2392 in ebp, so after the string mucking around we have, "9031-79647-2392-"

Now for the last bit:
	
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040134C(C)
|
:00401339 0FBE08                  movsx ecx, byte ptr [eax]
:0040133C 50                      push eax
:0040133D 8D1489                  lea edx, dword ptr [ecx+4*ecx]
:00401340 8D2C559A000000          lea ebp, dword ptr [2*edx+0000009A]
:00401347 FFD7                    call edi
:00401349 803800                  cmp byte ptr [eax], 00
:0040134C 75EB                    jne 00401339

Once again, another NIGHTMARE of code ;) 

	ecx = character
	edx = ecx * 4 + ecx
	ebp = edx * 2 + 0000009A

In EBP, 654, so the final code turns out to be "9031-79647-2392-654" for the username "CoRN2", check it if you like. :)

So now that we know the formula's we need, the next step is to write a program that will enable us to generate our own keys at a whim. To give the most support, I'll supply C, and Pascal 	sources for this keyen. Lucky you.

We'll start with pascal, and get it out of the way ;)

* Notice that in parts four and five, although the DirectNet loops through every character, the only one which affects our code is the last character.

-------------8<-----------------------------8<-----------------------8<--------------------------

PROGRAM DirectNet_Keygen;

VAR Offset : Byte;
    ECX    : LongInt;
    EBP    : LongInt;
    Name   : STRING[20];

PROCEDURE PartOne;
BEGIN
     EBP := $6B;
     FOR Offset := 1 TO Length( Name ) DO
       BEGIN
            ECX := Byte(Name[Offset]);
            EBP := EBP - ECX;
            ECX := (ECX * 2)+ECX;
            EBP := ECX*8+EBP;
       END;
     Write( EBP );
END;

PROCEDURE PartTwo;
VAR EDX : LongInt;
BEGIN
     FOR OffSet := 1 TO Length( Name ) DO
       BEGIN
            ECX := Byte( Name[OffSet]);
            EDX := ECX;
            ECX := ECX*8+ECX;
            ECX := ECX*4+ECX;
            EDX := ECX*2+EDX;
            EBP := EDX*2+EBP;
       END;
     Write( '-',EBP );
END;

PROCEDURE PartThree;
BEGIN
     ECX := Byte( Name[Length(Name)]);
     EBP := ECX*4+ECX+$85E;
     Write( '-',EBP );
END;

PROCEDURE PartFour;
VAR EDX : LongInt;
BEGIN
     ECX := Byte( Name[Length(Name)]);
     EDX := ECX*4+ECX;
     EBP := EDX*2+$9A;
     Writeln( '-', EBP );
END;

BEGIN
     Writeln( #13#10'DirectNet v1.1 -- KeyGen' );
     Writeln( 'By CoRN2 [mE''98/C4N' );
     Writeln( 'http://mex98.home.ml.org' );
     Write( #13#10'Name: ' );
     Readln( Name );
     Write( 'S/N : ' );
     PartOne;
     PartTwo;
     PartThree;
     PartFour;
END.

-------------8<-----------------------------8<-----------------------8<--------------------------

Now for C :

-------------8<-----------------------------8<-----------------------8<--------------------------

#include <stdio.h>
#include <string.h>

long int EBP,ECX;
unsigned char Offset;
char Name[20];

void PartOne( void )
{
	EBP = 0x6B;
	for( Offset = 0 ; Offset <= strlen( Name ) ; Offset ++ )
	{
		ECX = (int)Name[Offset];
		EBP -= ECX;
		ECX = ECX*2+ECX;
		EBP = ECX*8+EBP;
	}
	printf( "%ld",EBP );
}

void PartTwo( void )
{
	long int EDX;

	for( Offset = 0 ; Offset <= strlen( Name ) ; Offset ++ )
	{
		ECX = (int)Name[Offset];
		EDX = ECX;
		ECX = ECX*8+ECX;
		ECX = ECX*4+ECX;
		EDX = ECX*2+EDX;
		EBP = EDX*2+EBP;
	}
	printf( "-%ld",EBP );
}

void PartThree( void )
{
	ECX = (int)Name[strlen(Name)-1];
	EBP = ECX*4+ECX+0x85E;
	printf( "-%ld",EBP );
}

void PartFour( void )
{
	long int EDX;
	ECX = (int)Name[strlen(Name)-1];
	EDX = ECX*4+ECX;
	EBP = EDX*2+0x9A;
	printf( "-%ld\n",EBP );
}


void main( void )
{
	printf( "\nDirectNet v1.1 -- KeyGen\nBy CoRN2 [mE'98/C4N]\nhttp://mex98.home.ml.org\n" );
	printf( "\nName: " );
	gets( Name );
	printf( "S/N : " );
	PartOne();
	PartTwo();
	PartThree();
	PartFour();
}

-------------8<-----------------------------8<-----------------------8<--------------------------

Well, I hope that this tut has been of some use, as you should be able to see, key generators aren't so much hard, as time consuming, but then you learn more about protection routines into the bargain. Practice on your own targets and lemme know how you get on.

Good Luck.
											--CoRN2

:::::::::::::::::::::  m E X / c 4 N  T U T O R I A L  D I V I S I O N ::::::::::::::::::::::::::