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