Nov 3rd 1999 
SheeP140 [PGC] Keygen Algorithm (Tutorial 3)
Win '95 PROGRAM 
Win Code Reversing 
 
 
by SHeeP140 [PGC]
 
 
Code Reversing For Beginners 
 

 
 

Program Details
Program Name: Hard Copy V1.51
Program Type: Screen Capture Utility 
Program Location:  http://www.desksoft.com
Program Size: 106,496 bytes (tiny)
 
  
Tools Used:
W32Dasm and Softice

 

Rating
Easy ( )  Medium ( X )  Hard (    )  Pro (    ) 
Potatoes have a very interesting life.

 


"TIME is MORE than money, its life, and every moment is precious."



 
Introduction

HardCopy Pro is the professional, easy to use screen capture utility for Windows 95 / 98 and NT 4.0 or higher. It can capture rectangular screen areas and whole windows. The captured images can be cropped very easily and the color depth can be changed to any desired value from monochrome to true color. Images can be printed, saved, copied to the clipboard or edited with any image editing program. Many options allow the customization of all these actions to individual user needs.
 
About this protection system

30 day trial to use this piece of hmmmmmm (EXCELLENT) software.
 
The Essay
 
This is my 2nd TUTORial in as many days. I really was'nt intending on doing another one quite so soon, but it was pointed out to me that the algorithm in my last TUTORial was a little simple, so this time I have chosen something a little more complex, as I said before in my last TUTORial I would'nt write this if I didnt feel someone will benefit from it.

The main thing is that you get some kind of understanding from this essay, even if you dont understand all of it I hope you can come away with something new to apply to your future cracks.

Okay, here goes, again.
 

If you read my last TUTORial then you will be familiar with my methods of cracking, well I say my methods but they are simply the generic steps taken by most crackers I will run through them quickly.

STAGE 1.... Collect all relevant information on the target as possible, this will no doubt help you with your crack.
STAGE 2.... This is where we enter (oooh) the target, goggles at the ready.:o)

SORRY! once again I will explain ONLY how to get to the algorithm, the rest is for other TUTORials.

1. Load the target (Hard Copy V1.51) :o)
2. Goto the 'DEMO' tab and click 'REGISTER NOW'
3. You will be presented with a standard REGISTRATION NAME/CODE box
4. Type in your name and code, i used - Name: sheep140 CODE:1212121212
5. Enter Softice type BPX HMEMCPY
6. press F5 to exit Softice, press OK on the registration box
7. Softice will pop and you will be inside HMEMCPY, press F11, you can now disable HMEMCPY. 8. Search for the name you entered by typing s 0 lffffffff "sheep140" <----(you will use your name in here.
9. We need to place a BPM on the location of your name, mine was ---> BPM 015F:418858
10. Press F5 twice and you should land at the code point below.

--:00409BC0 xor edi, edi------------------;Zero EDI
--:00409BC2 cmp edx, 00000005-------------;Compare 5 with Username Length
--:00409BC5 mov dword ptr [ebp-08], edi---;Set [ebp-08] to Zero
S :00409BC8 jge 00409BD1------------------;Jump if username 5 characters or more (GOODGUY)
E :00409BCA xor al, al--------------------;Zero AL
C :00409BCC jmp 00409C6F -----------------;Jump out of routine (BADGUY)
T :00409BD1 xor eax, eax -----------------;Zero EAX
I :00409BD3 cmp edx, edi -----------------;Compare EDX with EDI
O :00409BD5 jle 00409BE3 -----------------;Jump if Error to next section
N :00409BD7 movsx ecx, byte ptr [eax+esi]-;Move letter into ECX (move letter of username into ECX)<----- ALGORITHM STARTS
1 :00409BDB add dword ptr [ebp-08], ecx --;Add Name[counter] to [ebp-08] MAGIC_NUMBER_1<----- YOU LAND HERE
--:00409BDE inc eax ----------------------;counter = counter +1
--:00409BDF cmp eax, edx -----------------;Have we finished MAGIC_NUMBER_1 ?
--:00409BE1 jl 00409BD7 ------------------;IF NO, JUMP.
***************** END OF SECTION 1 ****************

SUMMARY OF SECTION 1
Its always easier if you can break your algorithm down into sections.
Section 1 creates (MAGIC_NUMBER_1) in [EBP-08] by adding all characters of your username together, even though it only uses the second digits to XOR with later.

example....

USERNAME: sheep140
MAGIC_NUMBER_1: 02,aa :The algorithm doesnt use the 02, only the aa.

I`m afraid your going to need a little C knowledge.

The CODE snippet for MAGIC_NUMBER_1 in C.....

int name_length,i;
int MAGIC_NUMBER_1=0;
char name[20];

printf("Please Enter Username:");
gets(name);
name_length=strlen(name);

for (i=0;i<name_length;i++)
    MAGIC_NUMBER_1+=username[i]

MAGIC_NUMBER_1&=0xFF;  //gets rid of the extra bit 02,aa becomes 00,aa

--:00409BE3 push ebx ---------------------;Save EBX
S :00409BE4 mov ebx, dword ptr [ebp+0C] --;Move 64f938 (offset) into ECX
E :00409BE7 mov [ebp-04], 00414478 -------;Move Look Up Table 1 (offset) into [EBP-04}
C :00409BEE sub dword ptr [ebp-04], ebx --;SUBTRACT 64f938 from 414478
T :00409BF1 mov eax, edi -----------------;Move counter into EAX
I :00409BF3 mov ecx, dword ptr [ebp-04] --;Move result of above SUBTRACTION into ECX
O :00409BF6 cdq --------------------------;set up for idiv
N :00409BF7 idiv [ebp-10] ----------------;Divide EAX with username length - result is in EAX remainder in EDX
2 :00409BFA mov eax, dword ptr [ebp+08] --;Move USERNAME(offset) into EAX
--:00409BFD lea esi, dword ptr [edi+ebx] -;Setup storage for MAGIC_NUMBER_2
--:00409C00 mov al, byte ptr [eax+edx] ---;Move USERNAME[counter] into AL (the program uses the remainder as a counter) :o)
--:00409C03 xor al, byte ptr [ecx+esi] ---;XOR LOOK_UP_TABLE_1[counter] with USERNAME[counter]
--:00409C06 xor al, byte ptr [ebp-08] ----;XOR above result with MAGIC_NUMBER_1
--:00409C09 inc edi ----------------------;Increase counter
--:00409C0A cmp edi, 0000000A ------------;Have we finished generating MAGIC_NUMBER_2 ?
--:00409C0D mov byte ptr [esi], al -------;Save MAGIC_NUMBER_2[counter]
--:00409C0F jl 00409BF1 ------------------;Carry on generating? Jump if yes.
***************** END OF SECTION 2 ****************

SUMMARY OF SECTION 2
Well it seems to get a little bit complex here but dont worry this is what the summary is for.:o)
Before I show you the sum I will explain why they use the USERNAME[remainder] instead of USERNAME[counter].
the reason they do it is so that usernames less than 10 digits can produce a 10 digit code. example....
1. USERNAME[remainder=0]=s
2. USERNAME[remainder=1]=h
3. USERNAME[remainder=2]=e
4. USERNAME[remainder=3]=e
5. USERNAME[remainder=4]=p
6. .and so on...........=1
7. .....................=4
8. .....................=0
9. USERNAME[remainder=0]=s
10.USERNAME[remainder=1]=h
its the same as modulus in C.

The CODE snippet for MAGIC_NUMBER_2 in C.....

int name_length,i,temp1,temp2,temp3,t=0,flag;
int MAGIC_NUMBER_1=0;
int MAGIC_NUMBER_2[10];
int MAGIC_NUMBER_3[10];

char LOOK_UP_TABLE_1[]="3He78tKo4L";

for (i=0;i<10;i++)
  {
    t%=name_length;
    temp1=name[t];
    temp2=temp1^LOOK_UP_TABLE[i];
    temp3=temp2^MAGIC_NUMBER_1;
    MAGIC_NUMBER_2[i]=temp3;
    t++;
  }
DONT WORRY IF YOU DONT UNDERSTAND, THE WHOLE PROGRAM IS EXPLAINED AT THE END.

USERNAME:sheep140
MAGIC_NUMBER_1: 02,aa
MAGIC_NUMBER_2: EA 8A AA F8 E2 EF D5 F5 ED 8E

--:00409C11 and dword ptr [ebp+08], 00000-;Zero [EBP+08]
--:00409C15 cmp dword ptr [ebp-0C], 00000-;Does [ebp-oc] = 0 ?
--:00409C19 jle 00409C3D -----------------;Jump if error
--:00409C1B mov eax, dword ptr [ebp+08] --;Move counter into EAX
--:00409C1E push 0000000A ----------------;Push dec(10)
--:00409C20 cdq --------------------------;Setup idiv
S :00409C21 pop ecx ----------------------;Move dec(10) into ECX
E :00409C22 idiv ecx ---------------------;Divide EAX(counter) with ECX dec(10)
C :00409C24 mov al, byte ptr [ebp+08] ----;Move counter into AL
T :00409C27 imul al ----------------------;Multiply AL with AL
I :00409C29 add al, byte ptr [ebp-10] ----;Add USERNAME length to AL
O :00409C2C xor byte ptr [edx+ebx], al ---;Xor AL with MAGIC_NUMBER_2[counter]
N :00409C2F inc [ebp+08] -----------------;Increase counter
3 :00409C32 mov eax, dword ptr [ebp+08] --;Move counter into EAX
--:00409C35 cmp eax, dword ptr [ebp-0C] --;Are we finished?
--:00409C38 lea ecx, dword ptr [edx+ebx] -;Move offset of MAGIC_NUMBER_2 into ECX - no idea why , maybe someone can point this out?
--:00409C3B jl 00409C1B ------------------;if not finished jump back to start of section 3
***************** END OF SECTION 3 ****************

SUMMARY OF SECTION 3
Well nothing new here, section 3 uses the remainder counter again the SUM has become much easier
a simple Xor of MAGIC_NUMBER_2 to produce MAGIC_NUMBER_3

The CODE snippet for MAGIC_NUMBER_3 in C.....

for (i=0;i<flag;;i++)
   {

    t%=10;
    temp1=i;
    temp1*=temp1;
    temp1+=name_length;

  if (temp1>0xff)
      temp1&=0xff;

MAGIC_NUMBER_3[t]=MAGIC_NUMBER_2[t]^temp1;
MAGIC_NUMBER_2[t]=MAGIC_NUMBER_3[t];
t++;
   }

THIS SNIPPET DOESNT MAKE MUCH SENSE IT JUST SHOWS THE SUM TO GENERATE MAGIC_NUMBER_3
WHOLE KEYGEN CODE COMING NEXT.....
 

USERNAME:sheep140
MAGIC_NUMBER_1: aa02
MAGIC_NUMBER_2: EA 8A AA F8 E2 EF D5 F5 ED 8E
MAGIC_NUMBER_3: E2 83 A6 E9 FA CE F9 CC AD D7

:00409C3D xor esi, esi -----------------;Zero esi (counter)
:00409C3F mov al, byte ptr [esi+ebx] ---;Move MAGIC_NUMBER_3[counter] into AL
:00409C42 push eax ---------------------;Push eax for CALL 409B7A
:00409C43 call 00409B7A ----------------;This routine checks to see if your MAGIC_NUMBER_3[counter] is an alphanumeric digit, if it isnt then its manipulated.
 

************ START OF CALL 409B7A ***************
:00409B7A mov al, byte ptr [esp+04] ----;Move MANIPULATED number into AL
:00409B7E cmp al, 31 -------------------;Compare MANIP number with 31h "1"
:00409B80 jl 00409B86 ------------------;is the MANIP number less than the 31h? if yes JUMP.
:00409B82 cmp al, 39 -------------------;Compare MANIP number with 39h "9"
:00409B84 jle 00409B96 -----------------;Is MANIP number a REAL number from 0-9? if yes JUMP
:00409B86 cmp al, 61 -------------------;Compare MANIP number with 61h "a"
:00409B88 jl 00409B8E ------------------;Is MANIP number less than 61h "a"?If yes JUMP
:00409B8A cmp al, 7A -------------------;Compare MANIP number with 7ah "z"
:00409B8C jle 00409B96 -----------------;Is MANIP number a REAL lowercase digit from a-z? if yes JUMP
:00409B8E cmp al, 41 -------------------;Compare MANIP number with 41h "A"
:00409B90 jl 00409B99 ------------------;Is MANIP number less than 41h "A"? if yes JUMP
:00409B92 cmp al, 5A -------------------;Compare MANIP with 5ah "Z"
:00409B94 jg 00409B99 ------------------;Is MANIP number a REAL uppercase digit between A-Z? if no JUMP
:00409B96 mov al, 01 -------------------;FLAG to indicate no more manipulating on the MANIP number
:00409B98 ret --------------------------;Return from call
:00409B99 xor al, al -------------------;FLAG to indicate carry on manipulating MANIP number.
:00409B9B ret --------------------------;Return from call
************ END OF CALL 409B7A *****************
 

:00409C48 test al, al ------------------;Is the MAGIC_NUMBER_3[counter] a real CODE_DIGIT yet?
:00409C4A pop ecx ----------------------;Save MANIPULATED number
:00409C4B jne 00409C62 -----------------;Jump if MANIPULATED number is now a real CODE_DIGIT
:00409C4D movsx eax, byte ptr [esi+ebx]-;carry on MANIPULATING number, move FFFFFF?? into EAX
:00409C51 add eax, 00000046 ------------;Add 46 to the MANIPULATED number
:00409C54 mov ecx, 000000FF ------------;Move FF into ECX
:00409C59 cdq --------------------------;setup idiv
:00409C5A idiv ecx ---------------------;divide MANIPULATED number with FF (this places the MANIPULATED number into EDX)
:00409C5C mov byte ptr [esi+ebx], dl ---;Save MANIPULATED number again
:00409C5F push edx ---------------------;Push MANIPULATED number for CALL again
:00409C60 jmp 00409C43 -----------------;Jump to call
:00409C62 inc esi ----------------------;Increase counter
:00409C63 cmp esi, 0000000A ------------;Are we finished the real CODE_DIGIT?
:00409C66 jl 00409C3F ------------------;If not, JUMP BACK.
***************** END OF LAST SECTION*****
 

SUMMARY AND CONCLUSION
We are finaly here, THE END, well it has taken me about 7 hours to write this TUTORial, that includes the 1 hour working out the algorithm.
I truely hope you get something out of it, I know I`m very bad at explaining things, i tried my hardest, I tried to keep it brief as possible.
The last part of the algorithm converts your MAGIC_NUMBER_3 into a 10 digit displayable code, this is how its done.

1. Look at first element of (array) MAGIC_NUMBER_3
2. Is it a displayable digit between 0-9, a-z or A-Z?
3. NO! okay, add 0x46 to it. Make sure element doesnt exceed 0xff, if it does make it a negative number.
4. Is it a displayable digit between 0-9, a-z or A-Z?
5. YES! okay, SAVE it into REAL_CODE array.
6. Move to next element.
SORRY! its the easiest way to explain this stuff. :o)
 

The WHOLE KEYGEN CODE in C.....
 
 
 

#include <stdio.h>
#include <string.h>


// ************************************* //
// *                                   * //
// * HARDCOPY PRO v1.51 KEYGEN         * //
// *                                   * //
// * Coded by: SHeeP140 [PGC]          * //
// *                                   * //
// ************************************* //



int checkcode(int eq);                                  // this declares the checkcode routine
void main()                                             // main program

{

                                                        // Variable declaration
        long eq;                                        // Var for checkcode routine
        int name_length,i,temp1,temp2,temp3,t=0,flag;   // Var for general purpose
        int MAG1=0;                                     // Var for MAGIC_NUMBER_1
        int MAG2[10];                                   // Var for MAGIC_NUMBER_2
        int MAG3[10];                                   // Var for MAGIC_NUMBER_3
        int REAL_CODE[10];                              // Var for REAL_CODE display
        char name[20];                                  // Var for USERNAME

        char LUT1[]="3He78tKo4L";                       // Look Up Table

        printf("Hardcopy V1.51 Keygen coded by SHeeP140 [PGC]\n\n")
        printf("Enter Username:");                      // Display enter username message
        gets(name);                                     // Store username in var NAME
        name_length=strlen(name);                       // Get NAME length
        if (name_length<5)
                {
                        printf("Username needs to be 5 or more characters");
                        exit(0);
                }
        
        flag=name_length;                               // Flag value needed later


        //MAGIC NUMBER 1 GENERATION START **** -----------------------------//


        for (i=0;i<name_length;i++)                    //For loop to calculate MAG1
                        MAG1+=name[i];                   //Calculation for MAG1
               

        MAG1&=0xFF;                                      //AND's MAG1 to produce MAG1-1
                                                         //i.e 23aa becomes 00aa
                                                         //the algorithm only xor's
                                                         //the last digits.  

        //MAGIC NUMBER 1 GENERATION END ****** -----------------------------//


        //MAGIC NUMBER 2 GENERATION START **** -----------------------------//


        for (i=0;i<10;i++)                             //for loop to calculate MAG2
                {

                        t%=name_length;                  //t=modulus x, so that username = 10
                        temp1=name[t];                   //temp1 = letter of username
                        temp2=temp1^LUT1[i];             //xor look up table with letter of username
                        temp3=temp2^MAG1;                //result of above is xored with MAG1-1
                        MAG2[i]=temp3;                   //store MAG2 using an array of 10 numbers
                        t++;                             //increase counter
                                                                               
                }

                
        //MAGIC NUMBER 2 GENERATION END ****** -----------------------------//


        //MAGIC NUMBER 3 GENERATION START **** -----------------------------//
                                                          //zero t
              t=0;  

        if (flag<10)                                    //Another BUG fixed by this
                flag=10;                                  //instruction, names <> than 10
                                                          //digits are now both worked out
                                                          //correctly :o) 

        for (i=0;i<flag;;i++)                           //for loop to create MAG3
                {

                        t%=10;                            //t=modulus 10 so that xoring cycles through MAG2
                        temp1=i;                          //temp1 = counter
                        temp1*=temp1;                     //temp1=temp1*temp1
                        temp1+=name_length;               //temp1=temp1+namelength


                        if (temp1>0xff)                   //if condition for xor correction
                            {
                              temp1&=0xff;                //when username is longer than 10 digits 
                            }                             //the algorithm starts to re-xor 
                                                          //MAG3 with MAG3 values it just generated
                                                          //this instruction stops the xoring digit
                                                          //exceed 0xff i.e 0134 becomes 0034
                            



                       MAG3[t]=MAG2[t]^temp1;             //MAG3=MAG2 xored with temp1
                       MAG2[t]=MAG3[t];                   //This instruction stops fuckups if
                                                          //username is over 10 digits
                                                          //THIS FUCKER NEARLLY HAD NE STUMPED.
                       t++;                               //increase t by 1

                }


        //MAGIC NUMBER 3 GENERATION END ****** -----------------------------//


        //FINAL CODE GENERATION START ******** -----------------------------//
                                             
 for(i=0;i<10;i++)                                       //for loop to create REAL_CODE
        {
                eq=MAG3[i];                                //eq= MAGIC NUMBER 3 offset+counter
                flag=checkcode(eq);                        //This routine checks if the MAGIC NUMBER
                                                           //in eq is a displayable character
                                                           //if it is then flag is set to 1 

                while(flag==0)                             //while loop (keep going until eq is displayable
                    {

                       temp2=0xff-eq;                      //Check if adding 0x46 will make temp2 exceed 0xff 

                       if (temp2&lt0x46)                   //If temp2 is going to exceed 0xff
                           {                               //
                              eq=0-temp2-1;                //make the number negative so that 
                           }                               //when 0x46 is added it wont exceed 0xff

                eq+=0x46;                                  //Add 0x46 to eq
                flag=checkcode(eq);                        //check to see if eq is displayable
                                                           //
                                                           //If eq is displayable then
                        }                                  //
                REAL_CODE[i]=eq;                           //eq is saved in the REAL_CODE
                                                           //array.
                printf("%c ",REAL_CODE[i]);                //display REG CODE
        }


        
 }

 int checkcode(int eq)                                     // check routine
        {

                                             
        if (eq>0x30 && eq<0x3a)                          //Is eq a digit from 0-9?
                {
                        return(1);                         //if yes return 1. (stop adding 0x46)
                }                               

         else if (eq>0x60 && eq<0x7b)                      //Is eq a letter between a-z?
                {
                        return(1);                         //if yes return 1. (stop adding 0x46)
                }

         else if (eq>0x40 && eq <0x5b)                      //Is eq a letter between A-Z?
                        {
                        return(1);                         //if yes return 1. (stop adding 0x46)
                        }
         else
                {
                        return(0);                         //If none of the above, carry on adding 0x46
                }

      
         }

//END OF KEYGEN :o)//



I KNOW I KNOW I KNOW, I SUCK BIG TIME AT C. :o) but hey, this is my first REAL keygen,
I promise to improve my C for the next tutorial.:o)
 
 

USERNAME:sheep140
MAGIC_NUMBER_1: 02,aa
MAGIC_NUMBER_2: EA 8A AA F8 E2 EF D5 F5 ED 8E
MAGIC_NUMBER_3: E2 83 A6 E9 FA CE F9 CC AD D7
REAL_CODE-----: n U 2 u X Z W X 1 c
 

WELL, thats it, I do appreciate that TIME is a very precious commodity but if you could spare a few moments to send me your comments I would appreciate it. thanks.

EMAIL - SHEEP140@OPERAMAIL.COM

I USED THE HTML TEMPLATE TO SET THIS OUT, I DID NOT RIP THE ESSAY AND PUT MY NAME ON IT.
 
 
Ob Duh

Do I really have to remind you all that by buying and NOT stealing the software you use will ensure that these software houses will continue to  produce even *better* software for us to use and more importantly, to continue offering even more challenges to breaking their often weak protection systems.

If your looking for cracks or serial numbers from these pages then your wasting your time, try searching elsewhere on the Web under Warze, Cracks etc.
 





Essay by: SheeP140
Page Created:  5 Nov 1999