home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Reverse Code Engineering RCE CD +sandman 2000
/
ReverseCodeEngineeringRceCdsandman2000.iso
/
RCE
/
+Sandman
/
kgtut2.txt
< prev
next >
Wrap
Text File
|
2000-05-25
|
23KB
|
492 lines
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
==========