------====== Keygening TVTool v4.8 by Thigo // TMG======------

 

 

 

I. Introduction

 

First, I'd like to explain why I choosed this target :)

The main reason is that there's many interesting tricks in it so you'll learn a lot :)

The second reason is the fact that it was coded in ASM and that's a good thing to help the Fravia to understad better.

Now let's start the game ;)

 

NOTE:

This tutorial is only for the version of TVTool included ! The author changed the check each time I released the keygen, so

you could have a wrong version (btw he only changed strange_str :) ).

 

Tools needed:

- Hacker view (v6.55)

- SoftICE

- IDA

- a brain...

 

II. Let's see our baby now !

 

1. Finding the check...

 

Open tvtool.exe in hacker's view... look at the sections:

+-Number  Name   VirtSize   RVA    PhysSize  Offset    Flag---+

¦     1 .text    0000A000 00001000 00003000 00000400 C0000040 ¦

¦     2 .rdata   00001000 0000B000 00000600 00003400 C0000040 ¦

¦     3 .data    00002000 0000C000 00000800 00003A00 C0000040 ¦

¦     4 .rsrc    00026000 0000E000 00008200 00004200 C0000040 ¦

¦     5 .......  00007000 00034000 00006E00 0000C400 C0000040 ¦

¦     6 .data    00001000 0003B000 00000000 00013200 C0000040 ¦

 

The section named "........" seems strange ;)

I won't bother you with packer recognition... Use File Info on it you'll learn that it's packed with aspack v2.1.

Use your favorite unpacker on it.

 

Now you have unpacked it.

Run it... (load icedump to avoid SoftICE detection)

And go on 'Info' tab. the author warn us : "No message !" and i have to add that the program exits after

10 minutes when unregistered.

Open the unpacked exe in IDA... We have a little bit of time to trace it ;)

Enter some dummy data in the edits and put a 'bpx getldgitemtexta' and also 'bpx getwindowtexta' in softice.

Click on the button... Nothing ! The program uses another method which must be a SendMessage shit but there's shitloads of

call for it each second in Windows.

So we put bpx sendmessagea IF (esp.4 == WM_GETTEXT)...

Still nothing :(

Maybe the proggy saves the settings on exit...

So we exit and look into the registry at : Software\FullScreen

We found that by looking for sth like software\... in the proggy...

What do you see there ???

Username - Usermail - Userserial

So we look into IDA for those strings...

We look at the xrefs fot "Username" we have only 2. The first is the one we need:

 

00401618                 push    offset dword_40CDF0

0040161D                 push    offset unk_40D5CC

00401622                 push    0

00401624                 push    0

00401626                 push    offset aUsername ; "Username"

0040162B                 push    dword_40CDF8

00401631                 call    j_RegQueryValueExA

 

So it gets the username and store is into unk_40D5CC... let's rename it 'namestr' and convert it into an array

Do the same for usermail and userserial :)

Now we can look at the xrefs for namestr... we should land easily on the check...

but... Still nothing :(( Where is that fucking check ?? We'll find it ;)

Run the target and put a bpx InitCommonControls to break at the beginning and then we put a bmp 0040D5CC to break

when TVTool will read the name... we land at 4059c4 in softice exit the call by pressing F12 ... the address is 40569b

We go at this address in IDA...

We have only shitty bytes, the function is encrypted !!

If we got above, we find a xref:

00405686 unk_405686      db 0AAh ; ¬             ; DATA XREF: start+8E8o

00405686                                         ; .text:00408C87o

 

let's see what's going there :)

 

004018E8                 mov     eax, offset unk_405686

004018ED                 mov     dword_40D19D, eax

 

HMmm this is only to disturb bad crackers ;) we're going to break that ;)

The second xref... We land in a very interesting place:

 

00408C7C sub_408C7C      proc near               ; DATA XREF: sub_408C7C+A7o

00408C7C                                         ; sub_408D6B+Bo ...

00408C7C

00408C7C var_C           = dword ptr -0Ch

00408C7C var_8           = dword ptr -8

00408C7C var_4           = dword ptr -4

00408C7C

00408C7C                 push    ebp

00408C7D                 mov     ebp, esp

00408C7F                 add     esp, 0FFFFFFF4h

00408C82                 mov     eax, offset aMA ; "ÀмÐA"

00408C87                 mov     ecx, offset unk_405686

00408C8C                 mov     [ebp+var_8], ecx

00408C8F                 sub     eax, ecx

00408C91                 mov     [ebp+var_4], eax

00408C94                 mov     eax, offset j_GetCurrentDirectoryA

00408C99                 add     eax, 6

00408C9C                 call    eax ; j_GetCurrentDirectoryA

 

hmm:

00408C94                 mov     eax, offset j_GetCurrentDirectoryA

00408C99                 add     eax, 6

 

THIS is strange, we got at j_GetCurrentDirectoryA and look 6 bytes after:

 

0040A94A ; [00000006 BYTES: COLLAPSED FUNCTION j_GetCurrentProcessId. PRESS KEYPAD "+" TO EXPAND]

 

This is very very interesting... We come back to our code and do the same...

00408CB0                 mov     eax, offset j_GetVersionExA

00408CB5                 add     eax, 6

00408CB8                 call    eax ; j_GetVersionExA

 

This is infact a call to OpenProcess... We're on the good way !

 

00408CBF                 push    0

00408CC1                 push    [ebp+var_4]

00408CC4                 push    offset unk_40D1B1 ;interesting, isn't it?

00408CC9                 push    [ebp+var_8] //base address

00408CCC                 push    dword_40D597 ;handle to our process

00408CD2                 mov     eax, offset j_GetVersionExA

00408CD7                 add     eax, 0Ch

00408CDA                 call    eax ; j_GetVersionExA ; = ReadProcessMemory...

 

So it will read our function... and put it in unk_40D1B1

Let's see what's coming next:

 

00408CE3                 mov     esi, offset byte_40D1B1

00408CE8                 mov     edi, offset byte_40D1B1

00408CED

00408CED loc_408CED:                             ; CODE XREF: sub_408C7C+83j

00408CED                 lodsb

00408CEE                 xor     eax, 0FFh

00408CF3                 stosb

00408CF4                 inc     [ebp+var_C]

00408CF7                 mov     eax, [ebp+var_C]

00408CFA                 cmp     eax, [ebp+var_4]

00408CFD                 jnb     short loc_408D01

00408CFF                 jmp     short loc_408CED

 

So the whole function will be decrypted by a xor 0xFF for each byte... We can code a little IDC script that will do that

for us :)

Here's it:

 

==================================================== decrypt.idc ====================================================

 

static main()

{

    auto ea, key, length ;

 

    ea = ScreenEA();                   //Current cursor pos

    length = AskLong(0,"Enter length");         //Function length

    key = AskLong(0,"Enter key");       //Byte to xor with

    length = ea+length;                    //End address

    while(ea <= length)                    //While in the function

    {

        PatchByte (ea, key ^ Byte(ea)) ;       // Decrypt current byte

      ea = ea + 1;                       //Go to next byte

    }  

  

    return 0 ;                             //Return

}

 

=====================================================================================================================

 

We see that the length is computed here:

 

00408C82                 mov     eax, offset unk_405958

00408C87                 mov     ecx, offset sub_405686

00408C8C                 mov     [ebp+var_8], ecx

00408C8F                 sub     eax, ecx

00408C91                 mov     [ebp+var_4], eax

 

So 405958-405686 = 2D2... So we go to our function at 405686 and run our script (F2), we enter 2d2 and FF...

Press on P to create a function...

It's magic :=) We got a nice function with some nice refs to the name:

 

00405686 sub_405686      proc near               ; DATA XREF: start+8E8o

00405686                                         ; sub_408C7C+Bo

00405686

00405686 var_8A          = byte ptr -8Ah

00405686 var_4D          = byte ptr -4Dh

00405686 var_10          = dword ptr -10h

00405686 var_C           = dword ptr -0Ch

00405686 var_8           = dword ptr -8

00405686 var_4           = dword ptr -4

00405686

00405686                 push    ebp

00405687                 mov     ebp, esp

00405689                 add     esp, 0FFFFFF74h

0040568F                 push    offset namestr

00405694                 lea     eax, [ebp+var_8A]

0040569A                 push    eax

0040569B                 call    sub_4059BA

004056A0                 push    offset mailstr

004056A5                 lea     eax, [ebp+var_8A]

004056AB                 push    eax

004056AC                 call    sub_405871

004056B1                 mov     dword_40D59F, 1

004056BB                 mov     byte ptr asc_40D197, 2Ah ; "*"

004056C2                 lea     eax, [ebp+var_8A]

004056C8                 push    eax

004056C9                 call    sub_40585B

 

We got the check now :)

 

2. Reversing the check...

 

0040568F                 push    offset namestr

00405694                 lea     eax, [ebp+var_8A]

0040569A                 push    eax

0040569B                 call    sub_4059BA

 

We have to see what this call does.

 

004059BA sub_4059BA      proc near               ; CODE XREF: sub_405686+15p

004059BA                                         ; sub_405686+77p ...

004059BA

004059BA var             = dword ptr  8

004059BA name            = dword ptr  0Ch

004059BA

004059BA                 push    ebp

004059BB                 mov     ebp, esp

004059BD                 mov     esi, [ebp+name]

004059C0                 mov     edi, [ebp+var]

004059C3

004059C3 loc_4059C3:                             ; CODE XREF: sub_4059BA+Fj

004059C3                 lodsb

004059C4                 stosb

004059C5                 or      al, al

004059C7                 jz      short locret_4059CB

004059C9                 jmp     short loc_4059C3

004059CB ; ---------------------------------------------------------------------------

004059CB

004059CB locret_4059CB:                          ; CODE XREF: sub_4059BA+Dj

004059CB                 leave

004059CC                 retn    8

004059CC sub_4059BA      endp

 

It's a simple lstrcpy function... We rename it...

 

004056A0                 push    offset mailstr

004056A5                 lea     eax, [ebp+var_8A]

004056AB                 push    eax

004056AC                 call    sub_405871

 

This one is a lstrcat so we have var_8A = name+email

 

004056C9                 call    sub_40585B

 

This one is a lstrlen ;) So:

 

004056CE loc_4056CE:                             ; CODE XREF: sub_405686+6Aj

004056CE                 cmp     eax, 3Ch

004056D1                 jnb     short loc_4056F2

004056D3                 push    offset asc_40D197 ; "*"

004056D8                 lea     eax, [ebp+var_8A]

004056DE                 push    eax

004056DF                 call    lstrcat

004056E4                 lea     eax, [ebp+var_8A]

004056EA                 push    eax

004056EB                 call    lstrlen

004056F0                 jmp     short loc_4056CE

 

adds the char "*" until the var_8a is 60 chars long. We can rename it as user_data_str.

After, we see that this str is copied into var_4D using our lstrcpy function...

Let's rename it as user_data_str2. This is just after:

 

00405702                 mov     [ebp+var_C], 0                 ;Init our vars to zero, this one is a variable to count sth

00405709                 mov     [ebp+var_4], 0                 ;this one is the counter for the next loop

00405710

00405710 loc_405710:                             ; CODE XREF: sub_405686+B5j

00405710                 xor     eax, eax               ;Zero

00405712                 mov     al, [ebp+user_data_str2]      ;Moves the current char of the string in al

00405715                 mul     [ebp+var_4]             ;multiplies it with the current positon in the string

00405718                 mov     edx, 0                       ;Initialize the remain to zero

0040571D                 mov     ecx, 25h              

00405722                 div     ecx

00405724                 mov     eax, edx               ;remain -> eax

00405726                 add     [ebp+var_C], eax         ;eax is added to var_C

00405729                 lea     eax, [ebp+user_data_str2]

0040572C                 push    eax

0040572D                 call    inc_str_pos             ;just look into the call and rename it

00405732                 inc     [ebp+var_4]             ;increase the counter

00405735                 cmp     [ebp+var_4], 60          ;is it the end of the str ?

00405739                 jnb     short loc_40573D        ;yes... jump after

0040573B                 jmp     short loc_405710        ;no... come back into the loop

 

So we can recode this part in C for a better view:

      lstrcat(name,mail);                             //Put the 2 strings together

      if(lstrlen(name)<0x3C)                          //IF length < 60

      {

            for(i=lstrlen(name);i<0x3C;i++)                 //padds the '*' at the end

                  name[i]='*';

      }

      name[i]=0;                                //Puts the terminating 0

      for(i=0;i<60;i++)                         //do the first loop

      {

            eax=name[i];                              //takes the current char

            eax*=i;                                   //multiply it with current pos

            eax=eax%0x25;                             //takes the % 25

            var_C+=eax;                         //add it to our var (DWORD var_c;)

      }

 

I hope you understand what's going there ;) Because the nect part is a bit harder (but not too much, dont worry).

 

0040573D                 mov     dword_40D1A5, 0

00405747                 lea     eax, [ebp+user_data_str]

0040574D                 push    eax

0040574E                 lea     eax, [ebp+user_data_str2]

00405751                 push    eax

00405752                 call    lstrcpy                             ;As the str was fucked in the 1st loop...

00405757                 mov     dword ptr asc_40D179, 0 ; "                "     ;Put a 0 so init the str to ""

00405761                 mov     [ebp+var_8], 0                             ;0

00405768

00405768 loc_405768:                             ; CODE XREF: sub_405686+152j

00405768                 mov     [ebp+var_10], 0                    

0040576F                 mov     [ebp+var_4], 0

00405776

00405776 loc_405776:                             ; CODE XREF: sub_405686+10Aj 

00405776                 xor     eax, eax                           ;Another loop, quite easy to understand

00405778                 mov     al, [ebp+user_data_str2]                ;Takes the char

0040577B                 add     [ebp+var_10], eax                   ;add it to var_10

0040577E                 lea     eax, [ebp+user_data_str2]                ;next char

00405781                 push    eax

00405782                 call    inc_str_pos

00405787                 inc     [ebp+var_4]

0040578A                 cmp     [ebp+var_4], 5                             ;is it the 5th char ?

0040578E                 jnb     short loc_405792                    ;yeah so jump out

00405790                 jmp     short loc_405776                    ;No... to loop

00405792 ; ---------------------------------------------------------------------------

00405792

00405792 loc_405792:                             ; CODE XREF: sub_405686+108j

00405792                 mov     dword_40D5AF, 0

0040579C                 mov     eax, [ebp+var_10]                        ;our var_10...

0040579F                 add     eax, [ebp+var_C]                   ;Adds the result of the 1st loop

004057A2                 mov     edx, 0

004057A7                 mov     ecx, 24h

004057AC                 div     ecx

004057AE                 mov     eax, edx                           ;takes var_10%0x24

004057B0                 add     eax, 30h                           ;Adds 0x30

004057B3                 cmp     eax, 39h                           ;compares to 0x39

004057B6                 jbe     short loc_4057BB                    ;If eax<=0x39

004057B8                 add     eax, 7                                   ;else add 7

004057BB

004057BB loc_4057BB:                             ; CODE XREF: sub_405686+130j

004057BB                 mov     byte ptr asc_40D197, al ; "*"                ;Moves the resulting char in this var

                                                            ;If you don't see why it's char... Go and

                                                            ;look your ASCII table :)

004057C0                 push    offset asc_40D197 ; "*"              

004057C5                 push    offset asc_40D179 ; "                "

004057CA                 call    lstrcat                             ;Add it to our serial...

004057CF                 inc     [ebp+var_8]

004057D2                 cmp     [ebp+var_8], 0Ch                     ;is it the 12th time ?

004057D6                 jnb     short locret_4057DA                       ;yeah...

004057D8                 jmp     short loc_405768                    ;no... go on in the loop

 

I know this is big... But you have to see the whole code commented if you want to learn, no ?

I think it is a bit hard if you never experienced double loops... So here's the C code:

      for(j=0;j<12;j++)

      {

            var_10=0;

            for(i=(j*5);i<((j*5)+5);i++)

                  var_10+=name[i];

            eax=var_C+var_10;

            eax=eax%0x24;

            eax+=0x30;

            if(eax>0x39)

                  eax+=7;

            serial[j]=eax&0xFF;

      }

 

Easy, no ? Now we are at the end of this call... But you think... there's absolutely no check here ! That's only a

serial gen routine ! You're right ;) So go at the top of the function and look at the refs...:

sub_408C7C > This one will already saw it when decrypting the code...

tart+8E8 > This one we NEVER saw it... So there !

 

004018E8                 mov     eax, offset sub_405686

004018ED                 mov     dword_40D19D, eax

 

Nice code isn't it ? We should have a look at the refs for dword_40D19D, especialy CODE Xrefs (call dword_40D19D) ;)

 

004057DC sub_4057DC      proc near               ; DATA XREF: start+ABDo

004057DC                 call    dword_40D19D

004057E2                 call    dword_40D1A1

004057E8                 push    offset asc_40D179 ; "                "

004057ED                 push    offset serialstr

004057F2                 call    sub_405931

004057F7                 or      eax, eax

004057F9                 jnz     short loc_40582D

 

Wow... I think we got the rigth place... our function was the first call... There might be interesting things in the second.

And if you look at sub_405931 you'll see it's a lstrcmp ;) Rename it and go in the second call to finish our serial

generation. To go in the call look at the xrefs of the dword.. I guess you can do that by yourself ;)

 

00405890                 push    ebp

00405891                 mov     ebp, esp

00405893                 add     esp, 0FFFFFF74h

00405899                 mov     [ebp+counter], 0

004058A0                 push    offset aLk6q2jel46ez ; "LK6q2jeL46Ez"

004058A5                 lea     eax, [ebp+strange_str]

004058AB                 push    eax

004058AC                 call    lstrcpy

004058B1                 push    offset asc_40D179 ; "                "

004058B6                 lea     eax, [ebp+temp_serial]

004058B9                 push    eax

004058BA                 call    lstrcpy

004058BF                 mov     dword ptr asc_40D179, 0 ; "                "           ;deletes our temp serial

004058C9

004058C9 loc_4058C9:                             ; CODE XREF: sub_405890+9Dj

004058C9                 xor     eax, eax

004058CB                 xor     ecx, ecx

004058CD                 mov     al, [ebp+temp_serial]             ;moves current char of temp_serial

004058D0                 mov     cl, [ebp+strange_str]             ;moves current char of strange_str

004058D6                 add     eax, ecx                     ;add them

004058D8                 mov     edx, 0

004058DD                 mov     ecx, 24h

004058E2                 div     ecx

004058E4                 mov     eax, edx                     ;Hehe... You remember that I hope ;)

004058E6                 add     eax, 30h                     ;That's exactly the same as in the 1st call

004058E9                 cmp     eax, 39h

004058EC                 jbe     short loc_4058F1

004058EE                 add     eax, 7

004058F1

004058F1 loc_4058F1:                             ; CODE XREF: sub_405890+5Cj

004058F1                 mov     byte ptr asc_40D197, al ; "*"

004058F6                 push    offset asc_40D197 ; "*"

004058FB                 push    offset asc_40D179 ; "                "

00405900                 call    lstrcat

00405905                 mov     dword_40D1A5, 1

0040590F                 lea     eax, [ebp+strange_str]

00405915                 push    eax

00405916                 call    inc_str_pos

0040591B                 lea     eax, [ebp+temp_serial]

0040591E                 push    eax

0040591F                 call    inc_str_pos

00405924                 inc     [ebp+counter]

00405927                 cmp     [ebp+counter], 0Ch

0040592B                 jnb     short locret_40592F

0040592D                 jmp     short loc_4058C9

0040592F ; ---------------------------------------------------------------------------

0040592F

0040592F locret_40592F:                          ; CODE XREF: sub_405890+9Bj

0040592F                 leave

00405930                 retn

 

As you can see I already renamed the vars... I don't have to explain why, It's the same as in the 1st call...

Look at the code... That's very easy ;) We recode that in C and we have our keygen ;) Because :

 

004057E8                 push    offset asc_40D179 ; "                "

004057ED                 push    offset serialstr

004057F2                 call    sub_405931

 

Just compares the resulting serial with the entered serial ;)

 

Here's the C code:

      for(i=0;i<12;i++)

      {

            eax=serial[i]+strange_str[i];

            eax=eax%0x24;

            eax+=0x30;

            if(eax>0x39)

                  eax+=7;

            serial[i]=eax&0xFF;

      }

 

Happy ? Here's the whole keygen source:

====================================================== keygen.c =====================================================

#include <windows.h>

#include <stdio.h>

 

int main(void)

{

      char name[256]={0},email[256]={0},serial[18]={0},strange_str[]="LK6q2jeL46Ez";

      DWORD eax,var_C=0,var_10=0,i,j;

     

      printf("Enter your name : ");

      scanf("%s",name);

      printf("\nEnter your email : ");

      scanf("%s",email);

     

      CharLower(email);                         //You can only enter small chars in the edit...

 

      lstrcat(name,email);                            //Put the 2 strings together

      if(lstrlen(name)<0x3C)                          //IF length < 60

      {

            for(i=lstrlen(name);i<0x3C;i++)                 //padds the '*' at the end

                  name[i]='*';

      }

      name[i]=0;                                //Puts the terminating 0

      for(i=0;i<60;i++)                         //do the first loop

      {

            eax=name[i];                              //takes the current char

            eax*=i;                                   //multiply it with current pos

            eax=eax%0x25;                             //takes the % 25

            var_C+=eax;                         //add it to our var (DWORD var_c;)

      }

 

      for(j=0;j<12;j++)

      {

            var_10=0;

            for(i=(j*5);i<((j*5)+5);i++)

                  var_10+=name[i];

            eax=var_C+var_10;

            eax=eax%0x24;

            eax+=0x30;

            if(eax>0x39)

                  eax+=7;

            serial[j]=eax&0xFF;

      }    

 

      for(i=0;i<12;i++)

      {

            eax=serial[i]+strange_str[i];

            eax=eax%0x24;

            eax+=0x30;

            if(eax>0x39)

                  eax+=7;

            serial[i]=eax&0xFF;

      }

     

      printf("\nYour serial is : %s\n",serial);

      return 0;

}

 

=====================================================================================================================

 

Now you have done all ! I hope you learned something with this tutorial...

 

III. Greets and all that stuff that always go at the end if an essay ;)

 

Email: thigo@caramail.com

Greetz: All TMG members, MackT, Snacker, TiTi, TaM, Duelist, tHE ANALYST, vrom, artif, roy, seifer, all the guys in

#cracking4newbies and everyone I forgot

 

05/2001