home *** CD-ROM | disk | FTP | other *** search
- Keygen Tutorial 1
- 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: A Day in the Life v1.0
- where : http://www.cartoonlogic.com
-
- 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 first 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 . Our target will be an
- easy one, as the protection scheme is really easy to understand and follow.
- I'll assume you know the following:
-
- - basic use of SoftIce
- - asm instructions (at least the ones used for cracking)
- ---[ TUTORIAL ]-------------------------------------------------------------------------------
-
- At first, launch SoftIce (assuming you know the basics, and how to setup this Numega's
- nice tool). Then launch our target, ADITL1.0.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:
-
-
- :004848A7 E8D4B1F9FF call 0041FA80 ;start here
- :004848AC 8B45F8 mov eax, dword ptr [ebp-08] ;eax=pointer to name
- :004848AF E8C4F3F7FF call 00403C78 ;return eax=lenght of name
- :004848B4 8BF0 mov esi, eax ;esi=lenght of name
- :004848B6 85F6 test esi, esi ;test lenght
- :004848B8 7E28 jle 004848E2 ;if no name enterthen jump
- :004848BA B901000000 mov ecx, 00000001 ;counter=1;ebx=1 firstly
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:004848E0(C)
-
- *first important loop*
- ======================
- :004848BF 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax
- :004848C2 0FB67C08FF movzx edi, byte ptr [eax+ecx-01];edi=name[ecx]
- :004848C7 0FAFFB imul edi, ebx ;edi=edi*ebx
- :004848CA 83FF12 cmp edi, 00000012 ;compare edi with 0x12
- :004848CD 7C0F jl 004848DE ;jump if less than
- :004848CF 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax
- :004848D2 8BC7 mov eax, edi ;eax=edi
- :004848D4 BB11000000 mov ebx, 00000011 ;ebx=11
- :004848D9 99 cdq ;expand eax
- :004848DA F7FB idiv ebx ;eax=eax/11
- :004848DC 8BD8 mov ebx, eax ;ebx=eax
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:004848CD(C)
- |
- :004848DE 41 inc ecx
- :004848DF 4E dec esi
- :004848E0 75DD jne 004848BF ;if unfinish loop back
-
- The above code is the first important loop of the key-genereating loop.It is basicaaly a very
- simple coding and can be summurized as follow:
- code=name[ecx]*code/17
- Ecx act as a counter, it count the current byte of name in increasing order, and the ebx is
- the code we want.
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:004848B8(C)
- |
- :004848E2 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax
- :004848E5 E88EF3F7FF call 00403C78 ;calculate lenght of eax
- :004848EA 8BC8 mov ecx, eax
- :004848EC 83F901 cmp ecx, 00000001
- :004848EF 7C30 jl 00484921 ;jump if no name enter
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:0048491F(C)
-
- *second important loop*
- =======================
- :004848F1 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax
- :004848F4 0FB67408FF movzx esi, byte ptr [eax+ecx-01];move name[ecx] to esi
- :004848F9 8BC1 mov eax, ecx ;ecx is in decresing order
- :004848FB 99 cdq ;clear edx
- :004848FC F7FE idiv esi ;eax = count / name[ecx]
- :004848FE 85D2 test edx, edx ;test if any number remain
- :00484900 740D je 0048490F ;if no then jump
- :00484902 8B45F8 mov eax, dword ptr [ebp-08] ;eax=name
- :00484905 83EE0B sub esi, 0000000B ;name[ecx]=name[ecx]-0B
- :00484908 0FAFF3 imul esi, ebx ;code=esi*code
- :0048490B 8BDE mov ebx, esi ;ebx=code
- :0048490D EB0D jmp 0048491C ;unconditional jump
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:00484900(C)
- |
- :0048490F 8B45F8 mov eax, dword ptr [ebp-08] ;eax=name
- :00484912 83C611 add esi, 00000011 ;name[0]=name[0]+0x11
- :00484915 8BC3 mov eax, ebx ;eax=code
- :00484917 99 cdq ;clear edx
- :00484918 F7FE idiv esi ;eax=eax/esi
- :0048491A 8BD8 mov ebx, eax ;ebx=code
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:0048490D(U)
- |
- :0048491C 49 dec ecx
- :0048491D 85C9 test ecx, ecx
- :0048491F 75D0 jne 004848F1 ;loop if unfinish
-
- The second important loop is also quite simple, we can neglect line 48490F to 48491A because
- it will never reach when is came to last byte, it will pass throught 48491F and never have
- change to loop back. So the testing algorithem from 4848FB to 484900 actually is rubbish code.
- This might be a bug or something that make to confuse us.
- The above coding can be summerized as:
- code=code*(name[ecx]-0x0b) ;code in this case is our serial calculated
- Ecx is a counter in decreasing order, it start it the last byte of our name. Ebx is the code we
- want.
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:004848EF(C)
- |
- :00484921 8B45F8 mov eax, dword ptr [ebp-08]
- :00484924 E84FF3F7FF call 00403C78
- :00484929 50 push eax
- :0048492A 8BC3 mov eax, ebx
- :0048492C 5A pop edx ;edx=lenght of name
- :0048492D 2BC2 sub eax, edx ;code=code-lenght
- :0048492F B907000000 mov ecx, 00000007 ;ecx=0x07
- :00484934 99 cdq
- :00484935 F7F9 idiv ecx ;code=code/7
- :00484937 05920F0000 add eax, 00000F92 ;code=code+0x0f92
- :0048493C 8BD8 mov ebx, eax ;ebx=code
-
- Until here our code=((code-lenght of our name)/7)+0F92
-
- :0048493E 8D55F4 lea edx, dword ptr [ebp-0C] ;edx=location [final reg key]
- :00484941 8BC3 mov eax, ebx ;eax=code
- :00484943 E8042BF8FF call 0040744C ;This call convert the int format
- :00484948 8D45F0 lea eax, dword ptr [ebp-10] ;of the code into character,
- :0048494B 8B55F8 mov edx, dword ptr [ebp-08] ;including it's sign also
- :0048494E 8A12 mov dl, byte ptr [edx]
-
- The call at 484943 actually convert our code into string form including sign also.
-
- :00484950 E84BF2F7FF call 00403BA0
-
- This call search for the char need to be insert in first byte of our serial, it return the
- in [ebp-10]. In this case is search for the first byte of our name as result.
-
- :00484955 8B45F0 mov eax, dword ptr [ebp-10] ;eax=target of insertion
- :00484958 8D55F4 lea edx, dword ptr [ebp-0C] ;edx=target for insertion
- :0048495B B901000000 mov ecx, 00000001 ;ecx=location
- :00484960 E89FF5F7FF call 00403F04 ;This call insert first char
- :00484965 8B55F8 mov edx, dword ptr [ebp-08] ;of name into first location
- :00484968 B8744A4800 mov eax, 00484A74 ;of code
-
- The call 00403F04 actually is a call to insert certain character into the string. It need
- EAX for what we want to insert, in above case it need to insert the first char of name.
- EDX is the address for the target to insert, in above case edx=address of our serial.
- ECX is the location for insertion, in above case it is insert at first byte.
-
- :0048496D E8EEF5F7FF call 00403F60 ;This call for any space in our
- :00484972 8B55F8 mov edx, dword ptr [ebp-08] ;name
- :00484975 8A1402 mov dl, byte ptr [edx+eax]
- :00484978 8D45F0 lea eax, dword ptr [ebp-10]
- :0048497B E820F2F7FF call 00403BA0 ;This call return eax=the char
- :00484980 8B45F0 mov eax, dword ptr [ebp-10] ;should placed in last byte of
- :00484983 50 push eax ;code
- :00484984 8B45F4 mov eax, dword ptr [ebp-0C]
- :00484987 E8ECF2F7FF call 00403C78 ;Calculate last byte location
- :0048498C 8BC8 mov ecx, eax
- :0048498E 41 inc ecx
- :0048498F 8D55F4 lea edx, dword ptr [ebp-0C]
- :00484992 58 pop eax
- :00484993 E86CF5F7FF call 00403F04 ;This call insert eax from call
- :00484998 8B45F4 mov eax, dword ptr [ebp-0C] ;00403BA0 into last byte of code
-
- The above code search for any space(20) in our name, if it does then the take the next byte
- after the FIRST space and put it into last byte of our code. If it doesn't found any space, then
- it will put first byte of our name into last byte of our code.
- Eg: Name=abc
- code=a15335672a
- Name=abc def
- code=a87672812d
- Name=abc 123 456
- code=a-15942455d
-
- :0048499B E8D8F2F7FF call 00403C78
- :004849A0 8BC8 mov ecx, eax
- :004849A2 D1F9 sar ecx, 1 ;ecx=lenght(name)>>1
- :004849A4 7903 jns 004849A9 ;jump if ecx>0
- :004849A6 83D100 adc ecx, 00000000
-
- The above code calculate the lenght of our name, ecx = our lenght of name shift right by one.
-
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:004849A4(C)
- |
- :004849A9 8D55F4 lea edx, dword ptr [ebp-0C]
-
- * Possible StringData Ref from Code Obj ->"ClaDiTL"
- |
- :004849AC B8804A4800 mov eax, 00484A80
- :004849B1 E84EF5F7FF call 00403F04 ;This call insert "ClaDiTL" into
- :004849B6 8D55EC lea edx, dword ptr [ebp-14] ;ecx location of code
- :004849B9 8B45FC mov eax, dword ptr [ebp-04]
- :004849BC 8B80EC010000 mov eax, dword ptr [eax+000001EC]
- :004849C2 E8B9B0F9FF call 0041FA80
-
- The above code inserts words'ClaDiTL" into our code, it insert in the location ecx, and ecx
- is our name lenght right shift by one.
- Eg:With name=yes123
- before insertion, code=y-85638181y
- after insertion, code=y-85ClaDiTL638181y
-
- :004849C7 8B55EC mov edx, dword ptr [ebp-14] ;edx=our false key
- :004849CA 8B45F4 mov eax, dword ptr [ebp-0C] ;eax=real key
- :004849CD E8B6F3F7FF call 00403D88 ;test for both key
- :004849D2 7530 jne 00484A04 ;jump if not equal
-
- The this part, it compare our fake key with the real key generated.
- So we can type 'd eax' after line 4849CA to sea for the real key.
-
-
- :004849D4 8D55EC lea edx, dword ptr [ebp-14]
- :004849D7 8B45FC mov eax, dword ptr [ebp-04]
- :004849DA 8B80EC010000 mov eax, dword ptr [eax+000001EC]
- :004849E0 E89BB0F9FF call 0041FA80
- :004849E5 8B55EC mov edx, dword ptr [ebp-14]
-
- * Possible StringData Ref from Code Obj ->"ClaDiTL"
- |
- :004849E8 B8804A4800 mov eax, 00484A80
- :004849ED E86EF5F7FF call 00403F60
- :004849F2 85C0 test eax, eax
- :004849F4 7E0E jle 00484A04
- :004849F6 A114F74800 mov eax, dword ptr [0048F714]
- :004849FB 8B00 mov eax, dword ptr [eax]
- :004849FD E8BA7B0000 call 0048C5BC
- :00484A02 EB37 jmp 00484A3B
-
- The above code store our registered data into registry.
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
- |:004849D2(C), :004849F4(C)
- |
- :00484A04 6A00 push 00000000
- :00484A06 668B0D884A4800 mov cx, word ptr [00484A88]
- :00484A0D B201 mov dl, 01
-
- * Possible StringData Ref from Code Obj ->"Invalid registration code!"
- |
- :00484A0F B8944A4800 mov eax, 00484A94 ;Change this code to 8B45F49090
-
- When we trace to here, there is also a fun place to make change, we can change the :
- 00484A0F B8944A4800 mov eax, 00484A94
- into:
- 00484A0F 8B45F4 mov eax, dword ptr [ebp-0C]
- 00484A12 90 nop
- 00484A13 90 nop
- So that instead of display out the "Invalid registration code!", it will display out our real
- key if we enter wrong key. This is beacuse from line 4849CA, we know the real key is store at
- [ebp-0C]. You can try and make a patch for for it. This technique is so call 'Magic Window'.
-
- :00484A14 E8DB7CFBFF call 0043C6F4
- :00484A19 8B45FC mov eax, dword ptr [ebp-04]
- :00484A1C 8B80E0010000 mov eax, dword ptr [eax+000001E0]
- :00484A22 8B10 mov edx, dword ptr [eax]
- :00484A24 FF9294000000 call dword ptr [edx+00000094]
- :00484A2A 8B45FC mov eax, dword ptr [ebp-04]
- :00484A2D 8B80EC010000 mov eax, dword ptr [eax+000001EC]
- :00484A33 8B10 mov edx, dword ptr [eax]
- :00484A35 FF9294000000 call dword ptr [edx+00000094]
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:00484A02(U)
- |
- :00484A3B 33C0 xor eax, eax
- :00484A3D 5A pop edx
- :00484A3E 59 pop ecx
- :00484A3F 59 pop ecx
- :00484A40 648910 mov dword ptr fs:[eax], edx
-
- * Possible StringData Ref from Code Obj ->"_^["
- |
- :00484A43 68654A4800 push 00484A65
-
- * Referenced by a (U)nconditional or (C)onditional Jump at Address:
- |:00484A63(U)
- |
- :00484A48 8D45EC lea eax, dword ptr [ebp-14]
- :00484A4B E8ACEFF7FF call 004039FC
- :00484A50 8D45F0 lea eax, dword ptr [ebp-10]
- :00484A53 BA03000000 mov edx, 00000003
- :00484A58 E8C3EFF7FF call 00403A20
- :00484A5D C3 ret
-
- ---[ OVERALL CONCLUSION OF KEY GENERATING PROCESS ]--------------------------------------------
- 1.code=(code*name[count1])/0x11; //count1=counter from 0 to lenght of name
- 2.code=code*(name[count2]-0x0b); //count2=counter from lenght of name to 0
- 3.code=((code-count1)/7)+0xf92; //count1=lenght of name
- 4.convert code into string form.
- 5.insert suitable character into the head and tail of the code.
- 6.insert 'ClaDiTL' into suitable place of the code
-
- ---[ C-LANGUAGE CODE ]-------------------------------------------------------------------------
- #include <stdio.h>
- #include<conio.h>
- #include <stdlib.h>
-
- int main(void)
- {
- long code=1;
- int count1,count2,count3;
- char name[25],reg[15];
- 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 A Day in the Life v1.0");
- 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(" - February 1999");
- printf("\n\nEnter register name = ");
- scanf("%[^\n]",name);
- for(count1=0;name[count1]>0;count1++) //first loop
- code=(code*name[count1])/0x11;
- for(count2=count1-1;count2>=0;count2--) //second loop
- code=code*(name[count2]-0x0b);
- code=((code-count1)/7)+0xf92;
- ltoa(code,reg,10); //convert long to string
- printf("Your registration key = %c",name[0]);
- for(count2=0;reg[count2]>0;count2++);
- count2=(count2+2)>>1;
- for(count1=0;count1<(count2-2);count1++)
- printf("%c",reg[count1]);
- printf("ClaDiTL");
- for(;reg[count1]>0;count1++)
- printf("%c",reg[count1]);
- for(count1=0;name[count1]>0;count1++){
- if ((name[count1]==0x20)&&(name[count1+1]>0)){
- printf("%c",name[count1+1]);
- return;
- }
- }
- printf("%c",name[0]);
- return ;
- }
-
- ---[ LAST ]------------------------------------------------------------------------------------
-
- I know my english expressing ability is weak, so in many place i know what's happening but i can
- express well and explain to you clearly. I hope you can really try it out then you can
- understand more out of it. This is my first keygen tutorial and i hope you'll like it.
-
- You can also try to trace into the call of how they insert words into string, call of how
- they calculate the strings number, call of how they compare two strings, call of how they search
- for specific char in a string... Try to understand their coding, it is quite interesting and
- useful also if we met similar call.
-
- Thanks for reading my first keygen tutorial.
-
- ---[ THAT'S ALL FOLKS ]-----------------------------------------------------------------------
-
- Yes123 '99
- ==========