------====== 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