Keygen Tutorial 4
				   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: CLR Script v1.23
where : http://www.prairienet.org/~clroth/clrscript.html

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 4th 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 engineering (instead of only cracking the program, we try to fully 
understand the whole key-generating algorithm), some newbies maybe having problem of 
following this.

The protection scheme actually encode your name into certain format, and compare with our serial
part by part, there are three part of the serial: first part is constant value, second part 
encoded from our name, and third part encoded from first and second part.if they all match it 
will then check again whether we enter a name and serial length is 18 chars.

By reading the above paragraph, i think you should know there are many ways to crack this
program(byte patching, look at real key directly(a bit tricky), key-generator...), but what 
i'm teaching here is the key-generator process so we are only interested in building a 
key-generator.(but i'll still touch a bit on others method :))

If you want to unregister the program, look in the registry:
[HKEY_USERS\.Default\Software\CLR\CLR Script\Settings]
[HKEY_CURRENT_USER\Software\CLR\CLR Script\Settings]
You will find the key "RegistrationName" and "RegistrationNumber" ,just delete them will do.
If you look careful at the registry, you will find the key "FirstRunTime"&"ShowExpiredTime",
clever cracker will know what they do, yes!they control the expired time, so just set the
"ShowExpiredTime" value to a large value will extend the expiration date.


I'll assume you know the following:

- basic use of SoftIce
- asm instructions (at least the ones used for cracking)
- knowing which call is important call

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

At first, launch SoftICe (assuming you know the basics, and how to setup this Numega's
nice tool). Then launch our target, CLRScrpt.exe! Go to the help menu and select registration 
number. Enter a name and serial number.

Simplily put a breakpoint before you press ok(eg.bpx hmemcpy or others...).You should have
break into softice after pressing OK button. Keep pressing F12 until you get back to the 
traget program code. You will be somewhere in:

* Reference To: USER32.GetWindowTextA, Ord:013Fh
                                  |
:00425375 FF1510854400            Call dword ptr [00448510]
:0042537B 6AFF                    push FFFFFFFF
:0042537D 8BCF                    mov ecx, edi
:0042537F E8273B0000              call 00428EAB
....

You are inside a call 425333, this call will be call 2 times to get your name and serial.
After a return, you will be in:

:00406F39 E8F5E30100              call 00425333		;call to get our name
:00406F3E 8D4C2408                lea ecx, dword ptr [esp+08]
:00406F42 E8DA190200              call 00428921
:00406F47 8D4C2408                lea ecx, dword ptr [esp+08]
:00406F4B C644241801              mov [esp+18], 01
:00406F50 51                      push ecx

* Possible Reference to Dialog: DialogID_0082, CONTROL_ID:03EC, ""
                                  |
:00406F51 68EC030000              push 000003EC
:00406F56 8BCE                    mov ecx, esi
:00406F58 E8D6E30100              call 00425333		;call to get our serial
:00406F5D E83F5D0300              call 0043CCA1
:00406F62 8B54240C                mov edx, dword ptr [esp+0C]
:00406F66 8B7804                  mov edi, dword ptr [eax+04]
:00406F69 52                      push edx

* Possible StringData Ref from Data Obj ->"RegistrationName"
                                  |
:00406F6A 6854B64500              push 0045B654

* Possible StringData Ref from Data Obj ->"Settings"
                                  |
:00406F6F 6804B64500              push 0045B604
:00406F74 8BCF                    mov ecx, edi
:00406F76 E85DA60200              call 004315D8
:00406F7B 8B442408                mov eax, dword ptr [esp+08]
:00406F7F 8BCF                    mov ecx, edi
:00406F81 50                      push eax

* Possible StringData Ref from Data Obj ->"RegistrationNumber"
                                  |
:00406F82 6840B64500              push 0045B640

* Possible StringData Ref from Data Obj ->"Settings"
                                  |
:00406F87 6804B64500              push 0045B604
:00406F8C E847A60200              call 004315D8
:00406F91 B958094600              mov ecx, 00460958

After the 2 call of retriving our name and serial, the following calls doesn't interesed us, how
do i know?because i trace into it before. So the above are all rubbish call that use to retrive
data from registry but we are not interest in it. Trace until we get here:

:00406F96 E845F7FFFF              call 004066E0			;this is the *important* call
:00406F9B 833D5809460017          cmp dword ptr [00460958], 00000017	;compare result with 17
:00406FA2 7524                    jne 00406FC8			;if not equal die
:00406FA4 8B0D5C094600            mov ecx, dword ptr [0046095C]	;ecx=our name
:00406FAA 8B41F8                  mov eax, dword ptr [ecx-08]	;eax=length of our name
:00406FAD 85C0                    test eax, eax			
:00406FAF 7417                    je 00406FC8			;die if no name enter
:00406FB1 8B1560094600            mov edx, dword ptr [00460960]	;edx=our serial
:00406FB7 837AF812                cmp dword ptr [edx-08], 00000012;compare serial length with 12
:00406FBB 750B                    jne 00406FC8			;die if length not equal to 12
:00406FBD 6A01                    push 00000001
:00406FBF 8BCE                    mov ecx, esi
:00406FC1 E8CE160200              call 00428694			;close window
:00406FC6 EB21                    jmp 00406FE9

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:00406FA2(C), :00406FAF(C), :00406FBB(C)
|
:00406FC8 6A00                    push 00000000
:00406FCA 6A30                    push 00000030

* Possible StringData Ref from Data Obj ->"Invalid registration number."
                                  |
:00406FCC 68F0B74500              push 0045B7F0
:00406FD1 E862A30200              call 00431338			;bad cracker die here!

So from the above code we are only interest in call 4066E0, we will try to make [460958]=17.
From above code we know that if we want to do a patch, patch it at 406faf & 406fbb, nop this two
conditional jumps. But it is not done yet, we still need to patch inside the call 4066E0 so that
it always return with [460958]=17.

Now we trace into call 4066E0:

* Referenced by a CALL at Addresses:
|:00406522   , :00406F96   
|
:004066E0 6AFF                    push FFFFFFFF
:004066E2 6888574400              push 00445788
.......

The above code follows by a lot of rubbish coding(i'll not gonna list them here), basically what 
it does is just retrieving data from registry, open certain free key in registry, convert all 
characters in name into capital letter and so on. But all those rubbish doesn't interest us, 
what we are interested is the following call :


:004067A1 51                      push ecx
:004067A2 52                      push edx
:004067A3 57                      push edi			;edi=our name
:004067A4 50                      push eax
:004067A5 E8E6090000              call 00407190			;This is a call that generate
:004067AA 8B06                    mov eax, dword ptr [esi]	;our real serial, the result is
:004067AC 83C414                  add esp, 00000014		;stored in [esp+20]
:004067AF 8378F812                cmp dword ptr [eax-08], 00000012
:004067B3 0F85FB000000            jne 004068B4			;die if length of our serial !=12
:004067B9 8D4C2430                lea ecx, dword ptr [esp+30]
:004067BD 6A04                    push 00000004
:004067BF 51                      push ecx
:004067C0 8BCE                    mov ecx, esi
:004067C2 E841BF0100              call 00422708			;this call get first 4 key from 
:004067C7 8B10                    mov edx, dword ptr [eax]	;our serial and return in [eax]
:004067C9 C744244002000000        mov [esp+40], 00000002
:004067D1 52                      push edx
:004067D2 E899B80000              call 00412070	;call that convert pushed string to hex no
:004067D7 8B4C2424                mov ecx, dword ptr [esp+24]	;ecx=first part of real serial
:004067DB 83C404                  add esp, 00000004
:004067DE 3BC1                    cmp eax, ecx			;compare first 4 char of serial
:004067E0 0F8597000000            jne 0040687D			;die if not equal
:004067E6 6A08                    push 00000008
:004067E8 8D442420                lea eax, dword ptr [esp+20]	;
:004067EC 6A05                    push 00000005
:004067EE 50                      push eax
:004067EF 8BCE                    mov ecx, esi
:004067F1 E80ABE0100              call 00422600			;call that get 2nd part of our
:004067F6 8B08                    mov ecx, dword ptr [eax]	;serial, return as [eax]
:004067F8 51                      push ecx
:004067F9 E872B80000              call 00412070			;convert 2nd part of our serial
:004067FE 8B542428                mov edx, dword ptr [esp+28]	;from string to hex from.
:00406802 83C404                  add esp, 00000004		;edx=second part of real serial
:00406805 33C9                    xor ecx, ecx
:00406807 3BC2                    cmp eax, edx			;compare second part of serial
:00406809 0F94C1                  sete cl			;set cl if equal
:0040680C 884C2413                mov byte ptr [esp+13], cl	;set marker for later use
:00406810 8D4C241C                lea ecx, dword ptr [esp+1C]
:00406814 E853220200              call 00428A6C
:00406819 8A442413                mov al, byte ptr [esp+13]
:0040681D 84C0                    test al, al
:0040681F 745C                    je 0040687D			;die if marker not set
:00406821 8D54242C                lea edx, dword ptr [esp+2C]	;edx=third part of real serial
:00406825 6A04                    push 00000004
:00406827 52                      push edx
:00406828 8BCE                    mov ecx, esi
:0040682A E85CBE0100              call 0042268B			;get last 4 key of our serial
:0040682F 8B00                    mov eax, dword ptr [eax]	;eax=third part of our serial
:00406831 50                      push eax
:00406832 E839B80000              call 00412070			;convert from string to hex
:00406837 8B54242C                mov edx, dword ptr [esp+2C]	;edx=third part of real serial
:0040683B 83C404                  add esp, 00000004
:0040683E 33C9                    xor ecx, ecx
:00406840 3BC2                    cmp eax, edx			;compare third part serial
:00406842 0F94C1                  sete cl
:00406845 884C2413                mov byte ptr [esp+13], cl	;set a marker for later used
:00406849 8D4C242C                lea ecx, dword ptr [esp+2C]
:0040684D E81A220200              call 00428A6C
:00406852 8A442413                mov al, byte ptr [esp+13]
:00406856 84C0                    test al, al
:00406858 7423                    je 0040687D			;die if marker not set
:0040685A 8B36                    mov esi, dword ptr [esi]
:0040685C B12D                    mov cl, 2D			;cl='-'
:0040685E 33C0                    xor eax, eax
:00406860 384E04                  cmp byte ptr [esi+04], cl	;compare 5th char with '-'
:00406863 0F94C0                  sete al
:00406866 84C0                    test al, al
:00406868 7413                    je 0040687D			;die if 5th char is not '-'
:0040686A 8A560D                  mov dl, byte ptr [esi+0D]	;dl=14th char of our serial
:0040686D 33C0                    xor eax, eax
:0040686F 3AD1                    cmp dl, cl	
:00406871 C644241301              mov [esp+13], 01
:00406876 0F94C0                  sete al
:00406879 84C0                    test al, al
:0040687B 7505                    jne 00406882			;die if 14th char is not '-'

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004067E0(C), :0040681F(C), :00406858(C), :00406868(C)
|
:0040687D C644241300              mov [esp+13], 00

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040687B(C)
|
:00406882 8D4C2430                lea ecx, dword ptr [esp+30]
:00406886 C7442440FFFFFFFF        mov [esp+40], FFFFFFFF
:0040688E E8D9210200              call 00428A6C
:00406893 8A442413                mov al, byte ptr [esp+13]
:00406897 84C0                    test al, al
:00406899 7419                    je 004068B4
:0040689B C70317000000            mov dword ptr	[ebx], 00000017	;here will make [460958]=17
:004068A1 8B4C2438                mov ecx, dword ptr [esp+38]
:004068A5 64890D00000000          mov dword ptr fs:[00000000], ecx
:004068AC 5F                      pop edi
:004068AD 5E                      pop esi
:004068AE 5D                      pop ebp
:004068AF 5B                      pop ebx
:004068B0 83C434                  add esp, 00000034
:004068B3 C3                      ret

*From the above code we know that the serial must be in the form of ????-????????-????. We also 
observe that it always set [esp+13] to 1 if test correct, this is because it will use [esp+13] 
in offset 406893 to test whether to make [460958]=17 or not, so we gotta make sure it is not in 
value '00'. So that we can pass thought offset 40689B sucessfully.
*Also, as i mentioned the serial is divide and tested in 3 section, so we can type'd esp+20' to 
see the real serial.
For exmaple by using name=yes123
[esp+20]=48 05 00 00 53 F3 AB 04 F8 14
05 48 =first part of real serial= 1352 in decimal
04 AB F3 53 =second part of real serial= 78377811 in decimal
14 F8 =third part of real serial= 5368 in decimal
Remember the data in memory is reverse, so we have to reverse them to get the exact value. And 
remember also 2nd part must be 8 char and 3rd part must be 4 char. If it is less than the 
required number add a '0' infront of it.
So our serial in above example = 1352-78377811-5368 (with name=yes123)

Now we go into the real key-generating routine, *call 00407190*

* Referenced by a CALL at Address:
|:004067A5   
|
:00407190 64A100000000            mov eax, dword ptr fs:[00000000]
:00407196 6AFF                    push FFFFFFFF
:00407198 68A8584400              push 004458A8
:0040719D 50                      push eax
:0040719E 64892500000000          mov dword ptr fs:[00000000], esp
:004071A5 53                      push ebx
:004071A6 8B5C2414                mov ebx, dword ptr [esp+14]
:004071AA 55                      push ebp

* Reference To: KERNEL32.lstrlenA, Ord:02A1h
                                  |
:004071AB 8B2D48824400            mov ebp, dword ptr [00448248]
:004071B1 56                      push esi
:004071B2 8B742424                mov esi, dword ptr [esp+24]	;esi=place of real serial store
:004071B6 57                      push edi
:004071B7 53                      push ebx		;ebx='CLR Script'
:004071B8 33FF                    xor edi, edi		;edi as a counter
:004071BA C70600000000            mov dword ptr [esi], 00000000
:004071C0 FFD5                    call ebp
:004071C2 85C0                    test eax, eax
:004071C4 7E1C                    jle 004071E2

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004071E0(C)
|routine that encode 'CLR Script'
:004071C6 8B06                    mov eax, dword ptr [esi]	;esi=place of real code stored
:004071C8 53                      push ebx
:004071C9 8D0480                  lea eax, dword ptr [eax+4*eax]
:004071CC 8D04C0                  lea eax, dword ptr [eax+8*eax]
:004071CF D1E0                    shl eax, 1
:004071D1 8906                    mov dword ptr [esi], eax
:004071D3 0FBE0C1F                movsx ecx, byte ptr [edi+ebx]
:004071D7 03C8                    add ecx, eax
:004071D9 47                      inc edi
:004071DA 890E                    mov dword ptr [esi], ecx
:004071DC FFD5                    call ebp
:004071DE 3BF8                    cmp edi, eax
:004071E0 7CE4                    jl 004071C6

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004071C4(C)
|
:004071E2 8B06                    mov eax, dword ptr [esi]
:004071E4 33D2                    xor edx, edx
:004071E6 B910270000              mov ecx, 00002710
:004071EB F7F1                    div ecx
:004071ED 8D442428                lea eax, dword ptr [esp+28]
:004071F1 8916                    mov dword ptr [esi], edx

*Until here we know that the above code does some encode on 'CLR Script', the outcome is 
always the same (independent of name entered). in this case [esi]=48 05, which is first part of
our real serial, so we now know why it is always constant. But why he want to use constant string
'CLR Script'? because all products of this company uses the same key-generating routine, the only
thing different is the first part, which he can use the program name to differ it, so that same
routine will produce different key on different programs(products).


:004071F3 8B542424                mov edx, dword ptr [esp+24]
:004071F7 52                      push edx
:004071F8 50                      push eax
:004071F9 E8C2FEFFFF              call 004070C0
:004071FE 83C408                  add esp, 00000008
:00407201 8D4C2428                lea ecx, dword ptr [esp+28]
:00407205 C744241800000000        mov [esp+18], 00000000
:0040720D E8211D0200              call 00428F33
:00407212 8B4C242C                mov ecx, dword ptr [esp+2C]	;ecx=place to store result
:00407216 8B7C2428                mov edi, dword ptr [esp+28]	;edi=our name
:0040721A 33D2                    xor edx, edx			;edx as counter
:0040721C C70100000000            mov dword ptr [ecx], 00000000	;clear ecx
:00407222 8B47F8                  mov eax, dword ptr [edi-08]	;eax=length of name
:00407225 85C0                    test eax, eax
:00407227 7E20                    jle 00407249			;die if no name entered

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407247(C)							;here start the keygen routine
|
:00407229 8B01                    mov eax, dword ptr [ecx]	;eax=code
:0040722B 8BD8                    mov ebx, eax			;ebx=eax
:0040722D C1E303                  shl ebx, 03			;left shift ebx by 3
:00407230 2BD8                    sub ebx, eax			;ebx=ebx-eax
:00407232 8D0498                  lea eax, dword ptr [eax+4*ebx];eax=eax+4*ebx
:00407235 D1E0                    shl eax, 1			;left shift eax by 1
:00407237 8901                    mov dword ptr [ecx], eax	;store eax
:00407239 0FBE1C17                movsx ebx, byte ptr [edi+edx]	;ebx=next char in our name
:0040723D 03D8                    add ebx, eax			;ebx=ebx+eax
:0040723F 42                      inc edx			;increase counter
:00407240 8919                    mov dword ptr [ecx], ebx	;store ebx
:00407242 8B47F8                  mov eax, dword ptr [edi-08]	;eax=length of name
:00407245 3BD0                    cmp edx, eax			;compare counter with name length
:00407247 7CE0                    jl 00407229			;jump if less than

I think the routine is quite straight forward and easy to understand, since i already put remark 
on it so i wonn't explain them here, but i'll give overall summary bottom part of easy.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407227(C)
|
:00407249 8B01                    mov eax, dword ptr [ecx]	;eax=code
:0040724B 33D2                    xor edx, edx			;clear counter
:0040724D BF00E1F505              mov edi, 05F5E100		;edi=100000000
:00407252 C7442418FFFFFFFF        mov [esp+18], FFFFFFFF
:0040725A F7F7                    div edi			;eax=eax/100000000
:0040725C 8911                    mov dword ptr [ecx], edx	;ecx=modulus value
:0040725E 8B06                    mov eax, dword ptr [esi]	;eax=first part serial
:00407260 0FAFC2                  imul eax, edx			;multiple 1st and 2nd part serial
:00407263 33D2                    xor edx, edx			;clear edx
:00407265 B910270000              mov ecx, 00002710		;ecx=10000
:0040726A F7F1                    div ecx			;eax=eax/10000
:0040726C 8B442430                mov eax, dword ptr [esp+30]	
:00407270 8D4C2428                lea ecx, dword ptr [esp+28]
:00407274 8910                    mov dword ptr [eax], edx	;store the modulus value
:00407276 E8F1170200              call 00428A6C
:0040727B 8B4C2410                mov ecx, dword ptr [esp+10]
:0040727F 5F                      pop edi
:00407280 5E                      pop esi
:00407281 5D                      pop ebp
:00407282 64890D00000000          mov dword ptr fs:[00000000], ecx
:00407289 5B                      pop ebx
:0040728A 83C40C                  add esp, 0000000C
:0040728D C3                      ret

---[ OVERALL CONCLUSION OF KEY GENERATING PROCESS ]--------------------------------------------
The above routine does 4 things:
1.1st part key=1352
2.encode our name and take modulus with 100000000 to get 2nd part key
3.(multiple 1st and 2nd part key)modulus 10000 to get 3rd part key
4.final key = (1st part key)-(2nd part key)-(3rd part key)

Eg:name = yes123
   serial = 1352-78377811-5368
   
---[ C-LANGUAGE CODE ]-------------------------------------------------------------------------
#include <stdio.h>
#include<conio.h>
#include <stdlib.h>

int main(void)
  {
   unsigned long code=0,temp=0;
   int count1,count2;
   char name[25];
   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 CLR Script v1.23");
   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(" - April 1999");
   printf("\n\nEnter register name = ");
   scanf("%[^\n]",name);
   for(count1=0;name[count1]>0;count1++){
     if ((name[count1]>=97) && (name[count1]<=122))
     name[count1]=name[count1]-32;
     }
   code=name[0];
   for(count1=0;name[count1+1]>0;count1++){
     temp=code;
     code=(code<<3)-temp;
     code=code*4+temp;
     code=code<<1;
     code=code+name[count1+1];
     }
   code=code % 100000000;
   printf("Your registration key = 1352-");
   for (temp=10000000;!(code/temp);temp/=10)
     printf("0");
   printf("%ld-%d",code,(code*1352)%10000);
   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 or explain to you clearly. This key-generating algoritherm basically is 
easy, but the most tricky part is that it has many unknown call that confusing you, so we have
to be wise and able to determine which call is important, which call is rubbish call. This has 
to be train by practising more, this is what so call 'cracker sence'!

After cracking this program, the other program from the same company also use the same keygen 
routine, we only need to change the first part encode string 'CLR Script' will do.

Thanks for reading my keygen tutorial.

---[ THAT'S ALL FOLKS ]-----------------------------------------------------------------------
   __,__
  /     \       
  vvvvvvv  /|__/|
     I   /O,O   |       
     I /_____   |
    J|/^ ^ ^ \  |
     |^ ^ ^ ^ |W|Yes123 '99
      \m___m__|_|[ghoste@rocketmail.com]vetica"><FONT COLOR="#000000"><B>REMEMBER,</B>I'm