Keygen Tutorial 2
				   Yes123 '99
---[ WARNING ]--------------------------------------------------------------------------------

This Tutorial is for education purpose only. I wrote it to allow you to understand how are
coded some protections schemes in software. I didn't make it to allow you to use the target
program without paying the author. If you plan to use these programs regularly, please remeber
to send your $ to the authors, don't be a outlaw, and over all, don't be a LAMER !!!

---[ INTRO ]----------------------------------------------------------------------------------

target: SubmitWolf(DEMO) v4.01
where : http://www.trellian.com/swolf

Tools : SoftIce for Win9x v3.24
	W32Dasm v8.9
        Programming Language (C, Pascal, asm, anyone you want, I'll use our old C)

This is my second tutorial in english. I hope my bad spelling won't make this text too much 
hard to understand for you. :)

I'll try to teach you how to make a key generator for a program. The way i teach will be 
based more on reverse enginerring (instead of only cracking the program, we try to fully 
understand the whole key-generating algorithm), some newbies maybe having problem of 
reading this.

- basic use of SoftIce
- asm instructions (at least the ones used for cracking)
- knowing which call is used to calculate serial number.

---[ TUTORIAL ]-------------------------------------------------------------------------------

At first, launch SoftICe (assuming you know the basics, and how to setup this Numega's
nice tool). Then launch our target, swolf32.exe! Go to the help menu and select the register.

Well, we'll try to find how this protection is running. Let's enter anything as a name
and random digits as a serial. Don't click the OK button yet, but hit CTRL & D to bring up 
SoftICe. We'll define a breakpoint, using the classical BPX hmemcpy(it works for most program. 
Hit CTRL-D again to go back to the program. Click OK and... SoftICe pop up.

Keep pressing F12 until you get back to the traget program code. Trace a few lines down you will
be in:

:004167D9 8D4C2410                lea ecx, dword ptr [esp+10]		;ecx=our fake serial
:004167DD 8D942490000000          lea edx, dword ptr [esp+00000090]	;edx=our name
:004167E4 51                      push ecx
:004167E5 52                      push edx
:004167E6 E8B5450100              call 0042ADA0				;This is the call which
:004167EB 83C410                  add esp, 00000010			;calculate our serial
:004167EE 85C0                    test eax, eax				;and return eax=1 if true
:004167F0 0F8596000000            jne 0041688C				;jump to registered
:004167F6 8D442408                lea eax, dword ptr [esp+08]
:004167FA 8D8C2488000000          lea ecx, dword ptr [esp+00000088]
:00416801 50                      push eax
:00416802 51                      push ecx
:00416803 E868470100              call 0042AF70				;This is a call to test
:00416808 83C408                  add esp, 00000008			;wheteher our serial is
:0041680B 85C0                    test eax, eax				;for v3.XX one.
:0041680D 7433                    je 00416842

The most important call is at 004167E6, there are a few calls before it, but nothing intersting
inside. The following call at 416803 is to test whether our serial is for version 3.XX. It some
how similar to v4.01, you can try to trace it if you want.
So we trace in the call 0042ADA0.

* Referenced by a CALL at Addresses:
|:004167E6   , :0042ABFC   
|
:0042ADA0 83EC30                  sub esp, 00000030
:0042ADA3 55                      push ebp
:0042ADA4 56                      push esi
:0042ADA5 57                      push edi
:0042ADA6 8B7C2444                mov edi, dword ptr [esp+44]		;edi=our fake serial
:0042ADAA 85FF                    test edi, edi	
:0042ADAC 0F84A7010000            je 0042AF59				;die if empty
:0042ADB2 8B6C2440                mov ebp, dword ptr [esp+40]		;ebp=our name
:0042ADB6 85ED                    test ebp, ebp				
:0042ADB8 0F849B010000            je 0042AF59				;die if empty
:0042ADBE 8A07                    mov al, byte ptr [edi]		;compare first byte of
:0042ADC0 3C50                    cmp al, 50				;serial with 'P', die
:0042ADC2 0F8587010000            jne 0042AF4F				;if not equal
:0042ADC8 807F0134                cmp byte ptr [edi+01], 34		;compare second byte of
:0042ADCC 750C                    jne 0042ADDA				;serial with '4'
:0042ADCE C70500C8430000000000    mov dword ptr [0043C800], 00000000
:0042ADD8 EB1C                    jmp 0042ADF6

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042ADCC(C)
|
:0042ADDA 3C50                    cmp al, 50				;compare first byte of
:0042ADDC 0F856D010000            jne 0042AF4F				;serial with 'P'
:0042ADE2 807F0145                cmp byte ptr [edi+01], 45		;compare second byte of
:0042ADE6 0F8563010000            jne 0042AF4F				;serial with 'E'
:0042ADEC C70500C8430001000000    mov dword ptr [0043C800], 00000001

We can see from above code, the first byte of our serial need to be 'P' and second byte need to
be either '4'(PRO edition) or 'E'(Enterprise edition), so we can chose to register as either
PRO edition or enterprise edition.


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042ADD8(U)
|
:0042ADF6 83C9FF                  or ecx, FFFFFFFF
:0042ADF9 33C0                    xor eax, eax
:0042ADFB F2                      repnz
:0042ADFC AE                      scasb
:0042ADFD F7D1                    not ecx
:0042ADFF 2BF9                    sub edi, ecx

The above code basically calculate the lenght of edi(our serial), and return the result in ecx.

:0042AE01 8D542414                lea edx, dword ptr [esp+14]
:0042AE05 8BC1                    mov eax, ecx
:0042AE07 8BF7                    mov esi, edi
:0042AE09 8BFA                    mov edi, edx
:0042AE0B 6A2D                    push 0000002D		;push 2D ('-') in stack
:0042AE0D C1E902                  shr ecx, 02
:0042AE10 F3                      repz
:0042AE11 A5                      movsd
:0042AE12 8BC8                    mov ecx, eax
:0042AE14 83E103                  and ecx, 00000003
:0042AE17 F3                      repz
:0042AE18 A4                      movsb
:0042AE19 8D4C2418                lea ecx, dword ptr [esp+18]

The above code move content of edi(our serial) to [ESP+14]

:0042AE1D 51                      push ecx
:0042AE1E E81D2A0000              call 0042D840
:0042AE23 8BF8                    mov edi, eax
:0042AE25 83C408                  add esp, 00000008
:0042AE28 85FF                    test edi, edi
:0042AE2A 0F8429010000            je 0042AF59

The above call actually test whether our fake serial contain '-'(remember it push 2D into stack?
), it return eax=-????? of our code or 00 if no '-' found. Then it test if eax=00 then straight
die.


:0042AE30 C60700                  mov byte ptr [edi], 00	;clear the '-' in our serial
:0042AE33 8B151CB44300            mov edx, dword ptr [0043B41C]	;edx=20202020,20 is space(' ')
:0042AE39 A020B44300              mov al, byte ptr [0043B420]
:0042AE3E 6A04                    push 00000004
:0042AE40 8D4C2410                lea ecx, dword ptr [esp+10]
:0042AE44 55                      push ebp
:0042AE45 51                      push ecx
:0042AE46 89542418                mov dword ptr [esp+18], edx
:0042AE4A 8844241C                mov byte ptr [esp+1C], al
:0042AE4E E8ED370000              call 0042E640

The above call basically test for space(' ')in our serial, but it did nothing except return eax=
first 4 character of our name. So it basically is a rubbish call.

:0042AE53 8A542420                mov dl, byte ptr [esp+20]	;move first byte of our serial
:0042AE57 83C40C                  add esp, 0000000C		;to dl
:0042AE5A 84D2                    test dl, dl
:0042AE5C 7421                    je 0042AE7F
:0042AE5E 8D742414                lea esi, dword ptr [esp+14]	;esi=our serial

Actually the above code move 'P' to dl(because the first byte of real serial is 'P' as stated in
above lines).
The first important loop begins:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AE7D(C)
|
:0042AE62 33C9                    xor ecx, ecx			;restart counter
:0042AE64 80C203                  add dl, 03			;dl=dl+3

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AE75(C)
|
:0042AE67 8A440C0C                mov al, byte ptr [esp+ecx+0C]	;move ecx byte of [esp+0c] to al
:0042AE6B F6EA                    imul dl			;al=al*dl
:0042AE6D 88440C0C                mov byte ptr [esp+ecx+0C], al	;move result back to [esp+ecx+0c]
:0042AE71 41                      inc ecx			;increase counter
:0042AE72 83F904                  cmp ecx, 00000004		;compare counter with 4
:0042AE75 7CF0                    jl 0042AE67			;jump if less than 4
:0042AE77 8A5601                  mov dl, byte ptr [esi+01]	;move next byte of serial to dl
:0042AE7A 46                      inc esi			;increase address of serial
:0042AE7B 84D2                    test dl, dl			;test dl=0?
:0042AE7D 75E3                    jne 0042AE62			;jump if false


This is the first important loops in generating serial. Ecx act as a counter in this case,
it count the number of [esp+0c] up to 4, dl is the first few bytes of our serial until 00
(remember we replace 2D with 00).It multiply every byte of the [esp+0c] with every byte of
our serial until dl=00. So it basically contain two small loop inner one multiple every byte of
[esp+0c](initially [esp+0c] is the first four byte of our name) with dl, the outer loop move 
next byte of our serial to dl until dl=00. So that every byte of the [esp+0c] is multiple by 
every byte of our serial before '-'.
If you still don't understand, try to trace it yourself or look at my C-language coding in 
below part.


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AE5C(C)
|
:0042AE7F 8A4D00                  mov cl, byte ptr [ebp+00]
:0042AE82 84C9                    test cl, cl
:0042AE84 7419                    je 0042AE9F

It test wheter first byte of our name is empty or not.
Then we follow by the second important big loop,it contain two loops, a inner and a outer loop.

:0042AE86 8BD5                    mov edx, ebp			;edx=our name
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AE9D(C)
|
:0042AE88 33C0                    xor eax, eax			;eax act as counter
:0042AE8A 80C103                  add cl, 03			;cl=cl+3

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AE95(C)
|
:0042AE8D 304C040C                xor byte ptr [esp+eax+0C], cl	;xor eax byte of[esp+0c] with cl
:0042AE91 40                      inc eax			;increase counter
:0042AE92 83F804                  cmp eax, 00000004		;compare eax with 4
:0042AE95 7CF6                    jl 0042AE8D			;repeat if less than 4
:0042AE97 8A4A01                  mov cl, byte ptr [edx+01]	;move next char of name into cl
:0042AE9A 42                      inc edx			;increase edx
:0042AE9B 84C9                    test cl, cl			;test whether is end of name
:0042AE9D 75E9                    jne 0042AE88			;jump if false

This second impotant loop basically xor (every byte of our name + 3)with every byte of [esp+0c].
the inner loop xor every bytes of [esp+0c] with cl, the outer loop move next char in our name
into cl and add cl with 03.
If you still donn't understand try to trace it again by yourself or look at my c-language coding
in below part.


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AE84(C)
|
:0042AE9F 8B4C240C                mov ecx, dword ptr [esp+0C]	;move the final result to ecx 
:0042AEA3 85C9                    test ecx, ecx			
:0042AEA5 7D04                    jge 0042AEAB			;test if ecx>0
:0042AEA7 F7D9                    neg ecx			;ecx=-ecx 
:0042AEA9 85C9                    test ecx, ecx			;test if ecx=0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AEA5(C)
|
:0042AEAB 7507                    jne 0042AEB4			;jump if flase
:0042AEAD B901000000              mov ecx, 00000001		;ecx=1
:0042AEB2 EB08                    jmp 0042AEBC

The above code actually convert ecx to positive number if it is less than 0, and if ecx=0 then 
let ecx=1.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AEAB(C)
|
:0042AEB4 81F90F270000            cmp ecx, 0000270F	;compare ecx with 9999
:0042AEBA 7D0D                    jge 0042AEC9

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0042AEB2(U), :0042AEC7(C)
|
:0042AEBC 8D0C89                  lea ecx, dword ptr [ecx+4*ecx]
:0042AEBF D1E1                    shl ecx, 1
:0042AEC1 81F90F270000            cmp ecx, 0000270F
:0042AEC7 7CF3                    jl 0042AEBC

If ecx<9999, then ecx=ecx*10. And repeat the operation until ecx>9999


* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AEBA(C)
|
:0042AEC9 81F93F420F00            cmp ecx, 000F423F	;compare ecx with 999999
:0042AECF 7E1B                    jle 0042AEEC

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AEEA(C)
|
:0042AED1 B867666666              mov eax, 66666667
:0042AED6 F7E9                    imul ecx
:0042AED8 8BCA                    mov ecx, edx
:0042AEDA C1F902                  sar ecx, 02
:0042AEDD 8BD1                    mov edx, ecx
:0042AEDF C1EA1F                  shr edx, 1F
:0042AEE2 03CA                    add ecx, edx
:0042AEE4 81F93F420F00            cmp ecx, 000F423F
:0042AEEA 7FE5                    jg 0042AED1

if ecx>999999, then ecx=ecx/10. And repeat until ecx<1000000.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AECF(C)
|
:0042AEEC 51                      push ecx

* Possible StringData Ref from Data Obj ->"%li"
                                  |
:0042AEED 6818B44300              push 0043B418
:0042AEF2 8D442430                lea eax, dword ptr [esp+30]
:0042AEF6 6A14                    push 00000014
:0042AEF8 50                      push eax
:0042AEF9 E8FE290000              call 0042D8FC			;covert ecx into string
:0042AEFE 83C410                  add esp, 00000010
:0042AF01 8D7701                  lea esi, dword ptr [edi+01]
:0042AF04 8D442428                lea eax, dword ptr [esp+28]

Until here we have already knew the code by typing:
d esi (which return our fake serial after '-')
d eax (which return the real serial after '-')
I cann't remember which one is the our fake serial and which one is real serial, 
it is either one. You can check and verify it.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AF26(C)
|
:0042AF08 8A10                    mov dl, byte ptr [eax]
:0042AF0A 8ACA                    mov cl, dl
:0042AF0C 3A16                    cmp dl, byte ptr [esi]
:0042AF0E 752A                    jne 0042AF3A
:0042AF10 84C9                    test cl, cl
:0042AF12 7414                    je 0042AF28
:0042AF14 8A5001                  mov dl, byte ptr [eax+01]
:0042AF17 8ACA                    mov cl, dl
:0042AF19 3A5601                  cmp dl, byte ptr [esi+01]
:0042AF1C 751C                    jne 0042AF3A
:0042AF1E 83C002                  add eax, 00000002
:0042AF21 83C602                  add esi, 00000002
:0042AF24 84C9                    test cl, cl
:0042AF26 75E0                    jne 0042AF08

The above code is just to test every byte of our serial with the real serial.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042AF12(C)
|
:0042AF28 33C0                    xor eax, eax
:0042AF2A 33C9                    xor ecx, ecx
:0042AF2C 85C0                    test eax, eax
:0042AF2E 0F94C1                  sete cl
:0042AF31 5F                      pop edi
:0042AF32 5E                      pop esi
:0042AF33 8BC1                    mov eax, ecx
:0042AF35 5D                      pop ebp
:0042AF36 83C430                  add esp, 00000030
:0042AF39 C3                      ret

It will go to the above code if correct serial enter.
The following code is the wrong serial coding, it will produce eax=0, and we neglect them here.
Now we can now enter the serial PE-?????? to register the program. It sucess but after we
restart the program, it pops up a message "You are using a corrupt or pirate copy of SubmitWolf.
If you .....", it can detect our pirate serial so there should be a hidden routine that has
further test on our serial. So we dissambly the program or just using softice bpx messageboxa to
search for the hidden routine, it should be performed at the starting of the program. I used
dissambly method (by using W32dasm), and checck for the string "You are using a corrupt...." and
look for a few lines above the starings:
And we write down the address 00411BC6, then we lunch the target and get into target coding and
get into softice to put breakpoint bpx 411BC2(you must enter into the target area first to put
breakpoint, you can do this by simplily put a breakpoint bpx showwindow and press F12 several
times to get back into the swolf32!. This is a very basic thing, i assume you all know it,
 and i won't repeat next time, repeating simply thing is meaningless)
Then we break at:

:00411BC2 0FBE4802                movsx ecx, byte ptr [eax+02]	;ecx=3rd character of our serial	
* Possible StringData Ref from Data Obj ->"         (((((                "
                                        ->"  H"
                                  |
:00411BC6 8B15F4B74300            mov edx, dword ptr [0043B7F4]
:00411BCC 8A044A                  mov al, byte ptr [edx+2*ecx]
:00411BCF 83E004                  and eax, 00000004

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00411BC0(U)
|
:00411BD2 85C0                    test eax, eax
:00411BD4 7542                    jne 00411C18
:00411BD6 A12C6B4400              mov eax, dword ptr [00446B2C]
:00411BDB 68D00F0000              push 00000FD0
:00411BE0 6840F04300              push 0043F040

* Possible Reference to String Resource ID=01457: "You are using a corrupt or pirate copy of SubmitWolf.  If yo"
                                  |
:00411BE5 68B1050000              push 000005B1
:00411BEA 50                      push eax
:00411BEB FFD7                    call edi
:00411BED 8B0D2C6B4400            mov ecx, dword ptr [00446B2C]
:00411BF3 68D00F0000              push 00000FD0
:00411BF8 68E0DD4300              push 0043DDE0

In order to jump over the warning message, we must have none zero eax. So we must have something
that and with 4(0100) to produce non-zero, so eax must be either ?4(0100),?5(0101),?6(0110)
 or ?7(0111), the four less significant bit of eax must be from 4-7.
We then check for edx since edx is a reference table of all the byte .By typing d eax we saw many
20,00,48,28,10,... and we also found some 84 at address(satisfy what we want):
004BB83E,004BB85E-004BB870
Since ecx is the 3rd character of our serial, in order to reach the above address(reverse the
above coding), ecx=((address of 84)-edx)/2	;edx=43B7F4
So for the address:
4BB83E ECX=20(' ')
4BB85E ECX=30('0')
4BB85F ECX=31('1')
4BB860 ECX=32('2')
.....
4BB870 ECX=39('9')

So we know our serial must contain the 3rd byte with ' 'or '0'-'9'.
An example of our serial is PE4-654321.

---[ OVERALL CONCLUSION OF KEY GENERATING PROCESS ]--------------------------------------------
1.first two character of serial must be either 'PE' or 'P4'.
2.multiple every first four character or our name with every byte of our serial number before '-'.
3.xor every four byte with every byte of our name.
4.convert to positive number if <0.
5.convert to number between 10000 and 1000000.

As an example, i use name:yes123 ,the final registration number = PE4-103061(Enterprise edition)
								  P44-114080(PRO edition)
(i use '4' at 3rd characters of key is just for an example, you can use others digits you like)

--[ C-LANGUAGE CODE ]-------------------------------------------------------------------------
#include 
#include

int main(void)
  {
   long code=555583,count1,count2;
   char name[25],cod[5],type='0';
   clrscr();
   textcolor(14);
   cprintf("    __,__\r\n");
   cprintf("   /     \\\r\n" );
   cprintf("   vvvvvvv  /|__/|\r\n");
   cprintf("      I   /O,O   |\r\n");
   cprintf("      I /_____   |      /|/|\r\n");
   cprintf("     J|/^ ^ ^ \\  |    /00  |    _//|\r\n");
   cprintf("      |^ ^ ^ ^ |W|   |/^^\\ |   /oo |\r\n");
   cprintf("       \\m___m__|_|    \\m_m_|   \\mm_|\r\n");
   textcolor(10);
   cprintf("=======================================");
   textcolor(11);
   cprintf("\r\nKeyGenerator for SubmitWolf(DEMO) v4.01");
   textcolor(10);
   cprintf("\r\n=======================================");
   printf("\nCracked by ");
   textcolor(14);
   cprintf("%c%c%c",0x10,0x10,0x10);
   textcolor(12);
   cprintf("Yes123");
   textcolor(14);
   cprintf("%c%c%c",0x11,0x11,0x11);
   printf(" - March 1999");
   printf("\n\nSelect edition PRO(0) or Enterprise(1) (0/1)= ");
   scanf("%c",&type);
   if (type=='1') code=557283;
   getchar();
   printf("Enter registration name = ");
   scanf("%[^\n]",name);
   for(count1=0;count1<=3;count1++)
     cod[count1]=name[count1];
   for(count1=1;count1<=3;count1++){
     for(count2=0;count2<=3;count2++)
       cod[count2]=cod[count2]*(code%100);
     code=code/100;
     }
   for(count1=0;name[count1]>0;count1++)
     for(count2=0;count2<=3;count2++)
       cod[count2]=cod[count2]^(name[count1]+3);
   for(count1=3;count1>=0;count1--){
     code=code+(cod[count1]&0xFF);
     if (count1>0)
       code=code*0x100;
     }
   if(code<0) code=-code;
   for(;code<10000;) code=code*10;
   for(;code>999999;) code=code/10;
   printf("Your serial number = P%c4-%ld",(type=='1')? 'E':'4',code);
   return ;
}

---[ LAST ]--------------------------------------------------------------------------------------

I know my english expressing ability is weak, so in many place i know what's happening but i 
cann't express well and explain to you clearly. I hope you can really try it out then you can
understand more out of it. There are many interesting coding like how they do /10 or *10 in
asembly, it is quite different from what we had learned.

After you register the program it will need to upgrade to full functional version by download
certain data from internet.

You can also try to make a keygen, using the same scheme, for the version 3.XX of the same 
program, remember in the begining i said there is also another call which test for serial key
of version 3,XX, you can have a try on it, it used similar method with little differences.

Thanks for reading my keygen tutorial.

---[ THAT'S ALL FOLKS ]-----------------------------------------------------------------------

Yes123 '99
==========e="Courier New">