CAD Viewer v3.2 A.30 - Tutorial

By zoltan - [ECLIPSE]

Hello every one, and welcome to my first public tutorial on how to make keygenerators. With this tutorial i am going to show you how to make a keygen for a program called CAD Viewer v3.2. You can grabb it from the following location:

http://www.guthcad.com.au/viewer.htm

PART ONE: Checking the serial

After we installed the program we can start off by running the program, then Enter a name/serial in the registration field's. Next go into softice and set a bpx on GetDlgItemTextA and press the OK button. Softice pops up, F12 to return to the caller and we should be landing here:

: bpx getdlgitemtexta
: F12 (pret)

:0054cb6b call [USER32!GetDlgItemTextA] :0054cb71 push 3f <- we are here :0054cb73 lea eax,[ebp-0194] :0054cb79 push eax :0054cb7a push 000001ac :0054cb7f mov eax,[ebp+08] :0054cb82 push eax :0054cb83 call [USER32!GetDlgItemTextA] :0054cb89 lea eax,[ebp-0194] <- load effective adress of serial number string into eax :0054cb8f push eax <- push it on the stack :0054cb90 lea eax,[ebp-01d4] <- load effective adress of name string into eax :0054cb96 push eax <- push it on the stack

OK trace pass where the serial is getting pushed (:0054cb90) and set a bpm on esp. Now hit F5 and we should be landing here:

: bpm esp

:0054cd8f  mov       eax,[ebp+0c]              <- mov serial string offset into eax
:0054cd92  push      eax                       <- push it on the stack
:0054cd93  call      0054cfe9                  <- call a function
do same as above, trace pass where the serial string is being pushed and put a bpm on esp. Next we land here:

:bpm esp

:0054cff9  mov       eax,[ebp+08]
:0054cffc  movsx     eax,byte ptr [eax]       <- mov first number of serial into eax
:0054cfff  test      eax,eax                  <- test
:0054d001  jz        0054d178                 <- if it's equal to zero, then just to quit :)
:0054d007  mov       eax,[ebp+08]
:0054d00a  push      eax                      <- push the serial string on the stack again
:0054d00b  call      0059da60                 <- this call is calculating the lenght of the string
:0054d010  add       esp,04
:0054d013  mov       [ebp-08],eax             <- return value from the call is in eax, witch is the string length
:0054d016  mov       dword ptr [ebp-0c],00000000 <- clear this register
:0054d01d  mov       dword ptr [ebp-10],00000000 <- clear this register
:0054d024  jmp       0054d02c                    <- jump

:0054d029 inc dword ptr [ebp-0c] <- counter :0054d02c mov eax,[ebp-08] <- lenght of the serial string :0054d02f cmp [ebp-0c],eax <- compare counter with the lenght :0054d032 jge 0054d078 <- if it's greater of equal then jump out of loop :0054d038 mov eax,[ebp-0c] :0054d03b mov ecx,[ebp+08] :0054d03e movsx eax,byte ptr [ecx+eax] :0054d042 cmp eax,30 :0054d045 jl 0054d073 :0054d04b mov eax,[ebp-0c] :0054d04e mov ecx,[ebp+08] :0054d051 movsx eax,byte ptr [ecx+eax] :0054d055 cmp eax,39 :0054d058 jg 0054d073 :0054d05e mov eax,[ebp-0c] :0054d061 mov ecx,[ebp+08] :0054d064 mov al,[ecx+eax] :0054d067 mov ecx,[ebp-10] :0054d06a mov edx,[ebp+0c] :0054d06d mov [edx+ecx],al :0054d070 inc dword ptr [ebp-10] :0054d073 jmp 0054d029 <- jump

All this algorithm does is check each char of the serial string if they are numbers or not, if it finds that one or more of the chars are not numbers it removes them from the string. So it looks like we can tagg the serial up the arse, Anyway [ebp+0c] points to the new string and [ebp-10] holds the lenght of our new string. Lets move on ...

:0054d078  mov       eax,[ebp-10]             <- lenght
:0054d07b  mov       ecx,[ebp+0c]             <- point to new string
:0054d07e  mov       byte ptr [ecx+eax],00
:0054d082  mov       eax,[ebp+0c]
:0054d085  push      eax                      <- push serial string offset on stack
:0054d086  call      0054e0dd                 <- hmm ...let's trace in here

A few lines down inside the call...

:0054e0e6 mov eax,[ebp+08] :0054e0e9 push eax :0054e0ea call 0059da60 <- get lenght of serial string :0054e0ef add esp,04 :0054e0f2 cmp eax,0e <- compare it to 14 :0054e0f5 jae 0054e100 <- if it's above or equal, jump to (go away baad cracker) algorithm start. :0054e0fb jmp 0054e181 <- else jump to end of this call

I am not going to go deeper into the (go away baad cracker) algorithm because it's not important here. return from the call and we are back here:
:0054d08e  mov       eax,[ebp+0c]             <- our serial string
:0054d091  push      eax
:0054d092  call      0059da60                 <- get lenght of serial, and put it in eax
:0054d097  add       esp,04
:0054d09a  mov       [ebp-10],eax
:0054d09d  cmp       dword ptr [ebp-10],0d    <- if lenght == 13
:0054d0a1  jz        0054d0ac                 <- then jump good boy
:0054d0a7  jmp       0054d178                 <- else beggar of baad cracker!
:0054d0ac  mov       eax,[ebp+0c]
:0054d0af  movsx     eax,byte ptr [eax+01]    <- mov second number of serial into eax
:0054d0b3  cmp       eax,31                   <- compare it to 1
:0054d0b6  jz        0054d0d1                 <- jump good boy
:0054d0bc  mov       eax,[ebp+0c]
:0054d0bf  movsx     eax,byte ptr [eax+02]    <- mov third number of serial into eax
:0054d0c3  cmp       eax,33                   <- compare it to 3
:0054d0c6  jz        0054d0d1                 <- jump good boy
:0054d0cc  jmp       0054d178                 <- jump to beggar off lame cracker!

Alright then, so now we know that the serial needs to be at least 13 chars long and the second number of the serial needs to be equal to 1 or the third char needs to be equal to 3 ..more tagging, eheh :) let's move on ..

Ok so let's use the third char.

:bc*
:bpx 0054d0c6
:F5

We enter a serial (use this: 1234555667890). Next we press the ok button and we land in sice again:

:0054d0c3  cmp       eax,33
:0054d0c6  jz        0054d0d1                <- ok we take this jump now, since our third char is equal to 3
:0054d0cc  jmp       0054d178
:0054d0d1  mov       eax,[ebp+0c]            <- our serial string
:0054d0d4  movsx     eax,byte ptr [eax+0a]   <- mov the 11'th number of the serial into eax
:0054d0d8  lea       eax,[eax*4+eax-00f0]
:0054d0df  lea       eax,[eax*4+eax]
:0054d0e2  lea       eax,[eax*4+eax]
:0054d0e5  mov       ecx,[ebp+0c]
:0054d0e8  movsx     ecx,byte ptr [ecx+09]   <- mov the 10'th number of the serial into ecx
:0054d0ec  lea       ecx,[ecx*4+ecx-00f0]
:0054d0f3  lea       ecx,[ecx*4+ecx]
:0054d0f6  lea       ecx,[ecx*4+ecx]
:0054d0f9  lea       ecx,[ecx*4+ecx]
:0054d0fc  shl       ecx,04
:0054d0ff  lea       eax,[eax*8+ecx]

eax is now: dec=0000078000, dec stands for decimal.

:0054d102 mov ecx,[ebp+0c] :0054d105 movsx ecx,byte ptr [ecx+08] <- mov the 9'th number of the serial into ecx :0054d109 sub ecx,30 :0054d10c mov edx,ecx :0054d10e shl ecx,05 :0054d111 sub ecx,edx :0054d113 lea ecx,[ecx*4+edx] :0054d116 lea ecx,[ecx*4+ecx] :0054d119 lea ecx,[ecx*4+ecx] :0054d11c shl ecx,05 :0054d11f add eax,ecx

eax is now: dec=0000678000

:0054d121 mov ecx,[ebp+0c] :0054d124 movsx ecx,byte ptr [ecx+0c] <- mov the last number of the serial into ecx :0054d128 lea ecx,[ecx*4+ecx-00f0] :0054d12f lea eax,[ecx*2+eax]

eax is now: dec=0000678000 note the bold one ..it's the last number of our serial

:0054d132 mov ecx,[ebp+0c] :0054d135 movsx ecx,byte ptr [ecx+07] <- mov the 8'th number of the serial into ecx :0054d139 sub ecx,30 :0054d13c mov edx,ecx :0054d13e shl ecx,06 :0054d141 add ecx,edx :0054d143 lea ecx,[ecx*2+ecx] :0054d146 lea ecx,[ecx*4+edx] :0054d149 lea ecx,[ecx*4+edx] :0054d14c lea ecx,[ecx*4+ecx] :0054d14f shl ecx,06 :0054d152 add eax,ecx

eax is now: dec=0006678000

:0054d154 mov ecx,[ebp+0c] :0054d157 movsx ecx,byte ptr [ecx+0b] <- mov the 12'th number of the serial into ecx :0054d15b lea ecx,[ecx*4-00c0] :0054d162 lea ecx,[ecx*4+ecx] :0054d165 lea ecx,[ecx*4+ecx] :0054d168 add eax,ecx

eax is now: dec=0006678900

:0054d16a mov ecx,[ebp+0c] :0054d16d movsx ecx,byte ptr [ecx] <- mov the first number of the serial into ecx :0054d170 sub ecx,30 :0054d173 add eax,ecx

eax is now: dec=0006678901

:0054d175 mov [ebp-04],eax <- mov checksum into ebp-04 :0054d178 mov eax,[ebp-04] <- err ... :0054d17b jmp 0054d180 <- errrr ... this programer is drunk :/ :0054d180 pop edi :0054d181 pop esi :0054d182 pop ebx :0054d183 leave :0054d184 ret

Alright then, our checksum is finished and stored away in ebp-04. Did you understand what it did ? our checksum became:

dec=0006678901
       |||||||
       ||||||\_ the first number in our serial
       |||||\__ 13'th
       ||||\___ 12'th
       |||\____ 11'th
       ||\_____ 10'th
       |\______ 9'th
       \_______ 8'th
Ok let's moove on ...F12 out of the call and we and up here:

:0054cd93  call      0054cfe9               <- where we returned from
:0054cd98  add       esp,08
:0054cd9b  mov       [ebp-04],eax           <- mov our checksum into ebp-04
:0054cd9e  cmp       dword ptr [ebp-04],00  <- compare it to 00
This is the beggar of i was talking about before, eax was set to zero inside the call either if the serial string was greater or equal to 14 or if the second or third number in the serial was not equal to 1 or 3.

:0054cda2  jnz       0054cdbc               <- jump to good boy
:0054cda8  call      0054d185
:0054cdad  mov       dword ptr [ebp-010c],ffffffff
:0054cdb7  jmp       0054ce20               <- else beggar off
:0054cdbc  mov       eax,[ebp+08]           <- AH ...finaly our namestring
:0054cdbf  push      eax                    <- push it on the stack
:0054cdc0  mov       eax,[006ccc20]         <- hmm ..a checksum value witch is: dec=2041 write it down..
:0054cdc5  push      eax                    <- push it on the stack
:0054cdc6  call      0054cc7e               <- call namestring manipulation routine
PART TWO: Where the real algorithm begins ...

Let's trace that call:

:....
:0054cc87  mov       dword ptr [ebp-10],00000001
:0054cc8e  mov       dword ptr [ebp-14],00000001
:0054cc95  mov       eax,[ebp+08]           <- our magic value
:0054cc98  mov       [ebp-0c],eax           <- move it into ebp-0c, let's call it checksum
:0054cc9b  mov       eax,[ebp+0c]           <- the namestring
:0054cc9e  push      eax
:0054cc9f  call      005a6f60               <- strcopy (copy a string from one location to another)
:0054cca4  add       esp,04
:0054cca7  push      eax                    <- the new location of the namestring
:0054cca8  call      005db460               <- strlwr (convert all the char's in the name to lower case)
:0054ccad  add       esp,04
:0054ccb0  mov       [ebp-08],eax           <- our new namestring witch has haad all it's char's converted
:0054ccb3  mov       eax,[ebp-08]           <\ to lowercase
:0054ccb6  push      eax
:0054ccb7  call      0059da60               <- get lenght of the name string
:0054ccbc  add       esp,04
:0054ccbf  mov       [ebp-04],eax           <- eax holds the lenght of the name string
:0054ccc2  mov       dword ptr [ebp-10],00000000
:0054ccc9  jmp       0054ccd1               <- jump to loop start

Here is the main loop:

:0054ccce inc dword ptr [ebp-10] <- a counter witch was set to zero above :0054ccd1 mov eax,[ebp-10] :0054ccd4 cmp [ebp-04],eax <- compare the counter value to the lenght of the name :0054ccd7 jle 0054cd40 <- jump if lower or equal :0054ccdd mov eax,[ebp-10] :0054cce0 mov ecx,[ebp-08] <- our namestring :0054cce3 movsx eax,byte ptr [ecx+eax] <- move char from namestring into eax :0054cce7 mov [ebp-18],eax <- put it in ebp-18 :0054ccea cmp dword ptr [ebp-18],61 <- comapare it to 'a' :0054ccee jl 0054ccfe <- jump if lower :0054ccf4 cmp dword ptr [ebp-18],7a <- else compare it to 'z' :0054ccf8 jle 0054cd12 <- jump if lower or equal :0054ccfe cmp dword ptr [ebp-18],30 <- or compare it to '0' :0054cd02 jl 0054cd3b <- jump if lower :0054cd08 cmp dword ptr [ebp-18],39 <- or else compare it to '9' :0054cd0c jg 0054cd3b <- jump if greater :0054cd12 mov eax,[ebp-18] <- move current char into eax :0054cd15 imul eax,[ebp-14] <- multiply it with ebp-14 (witch was set to 1 before the loop) :0054cd19 add [ebp-0c],eax <- add it to ebp-0c, checksum :0054cd1c mov eax,[ebp-14] <- mov value in ebp-14 into eax :0054cd1f lea eax,[eax*4+eax] <- multiply it with 5 :0054cd22 add eax,eax <- multiply it with 2 :0054cd24 mov [ebp-14],eax <- mov eax into ebp-14 :0054cd27 cmp dword ptr [ebp-14],000186a0 <- compare ebp-14 to dec=100000 :0054cd2e jle 0054cd3b <- jump if lower or equal :0054cd34 mov dword ptr [ebp-14],00000001 <- else ebp-14 = 1 :0054cd3b jmp 0054ccce <- jump start of loop to get next letter of name

end of loop ..

:0054cd40 cmp dword ptr [ebp-08],00 <- compare offset witch ebp-08 (our namestring) points to with zero. :0054cd44 jz 0054cd56 <- jump if zero, beggar off :0054cd4a mov eax,[ebp-08] <- else mov it to eax :0054cd4d push eax <- push it on the stack :0054cd4e call 0059bd70 <- call some unimportant call :0054cd53 add esp,04 :0054cd56 cmp dword ptr [ebp-0c],0098967f <- compare our checksum to dec=9999999 :0054cd5d jle 0054cd6f <- jump if lower or equal end of function :0054cd63 sub dword ptr [ebp-0c],00989680 <- else sub checksum with dec=10000000 :0054cd6a jmp 0054cd56 <- jump to check again :0054cd6f mov eax,[ebp-0c] <- mov checksum into eax :0054cd72 jmp 0054cd77 <- err ..o well :) :0054cd77 pop edi :0054cd78 pop esi :0054cd79 pop ebx :0054cd7a leave :0054cd7b ret <- return ...

Now this should be so hard to uderstand would it ? :) Return from the call and we land here:

:0054cdc6  call      0054cc7e                    <- where we returned from
:0054cdcb  add       esp,08
:0054cdce  mov       [ebp-0108],eax              <- mov eax (checksum) into ebp-108
:0054cdd4  mov       eax,[ebp-0108]              <- mov it back to eax. err .. :p
:0054cdda  cmp       [ebp-04],eax                <- compare it with the checksum generated from our serial string.
:0054cddd  jz        0054cded                    <- jump if same
:0054cde3  jmp       0054cda8                    <- else beggar off

There we are finished, now ill show you my keygen source witch i have coded in C.

   int a,i,len1;
   unsigned char name[100],code_string[100];
   unsigned char table1[30]="x433434xxxxxx";
   unsigned long checksum1=0;
   unsigned long checksum2=1;
   unsigned long checksum3=2041;

   len1=strlen(name);
   strlwr(name);
   if(len1>=5){
      for(i=0;i=0x30&&name[i]<=0x39)||(name[i]>=0x41&&name[i]<=0x7A)){
            checksum1=name[i];
            checksum3+=checksum1*checksum2;
            checksum2*=5;
            checksum2+=checksum2;
            if(checksum2>=100001){
               checksum2=1;
            }
         }
      }
      if(checksum3>=9999999){
         checksum3-=10000000;
      }
      sprintf(code_string,"%06lu",checksum3);
      table1[0]=(a=code_string[6]);
      table1[7]=(a=code_string[0]);
      table1[8]=(a=code_string[1]);
      table1[9]=(a=code_string[2]);
      table1[10]=(a=code_string[3]);
      table1[11]=(a=code_string[4]);
      table1[12]=(a=code_string[5]);
      SetWindowText(hwndCode,table1);
   }
   else{
      SetWindowText(hwndCode,"Name must be at least least 5 char(s)");
   }

That's all folks, hope you enjoyed this tutorial as much as i did writing it :)


© Author: zoltan 31st December 1999.