home *** CD-ROM | disk | FTP | other *** search
- 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 . 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)
- - 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 is PE4-103061(Enterprise edition)
- P44-114080(PRO edition)
- ---[ C-LANGUAGE CODE ]-------------------------------------------------------------------------
- #include <stdio.h>
- #include<conio.h>
-
- 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 can
- 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.
-
- ---[ THAT'S ALL FOLKS ]-----------------------------------------------------------------------
-
- Yes123 '99
- ==========