|
|
|
:hwnd explorer
Window Handle
hQueue SZ QOwner Class Name
Window Procedure
0538(1)
0F77 32 EXPLORER #32770 (Dialog)
1777:00004757 ;this is our window
053C(2)
0F77 32 EXPLORER Button
1777:0000102E
0540(2)
0F77 32 EXPLORER Static
1777:000052FA
0544(2)
0F77 32 EXPLORER Edit
1777:00000BF4 ;this is an edit field
Okay, let's trap messages referring to text reading from this window: 'bmsg 544 wm_gettext'. Enter a name and a registration code and press OK. Boom! we land in some kernel/gdi functions, but after a few F12's (p ret) we find the following piece of code:
:1000445A 8B3594F70710
mov esi, dword ptr GetDlgItemTextA
:10004460 8D4C241C
lea ecx, dword ptr [esp+1C] ;push the address for username
:10004464 6A1F
push 0000001F
:10004466 51
push ecx
:10004467 68C8000000
push 000000C8
:1000446C 57
push edi
:1000446D FFD6
call esi ;call GetDlgItemTextA
:1000446F 8D542460
lea edx, dword ptr [esp+60] ;push the address for code
:10004473 68C9000000
push 000000C9
:10004478 52
push edx
:10004479 68C9000000
push 000000C9
:1000447E 57
push edi
:1000447F FFD6
call esi ;call GetDlgItemTextA
This is a classic routine for reading username and code. The username
is at [esp+1c] and the code at [esp+60]. Now we'll se how are manipulated
these two string and get compared.
|
:10004491 0FBE06
movsx eax, byte ptr [esi] ;at [esi] is the username string
:10004494 50
push eax
:10004495 E8F6AA0400
call 1004EF90 ;convert the character in eax to uppercase
:1000449A 83C404
add esp, 00000004
:1000449D 3C41
cmp al, 41 ;'A'
:1000449F 7C04
jl 100044A5
:100044A1 3C5A
cmp al, 5A ;'Z'
:100044A3 7E04
jle 100044A9
:100044A5 3C20
cmp al, 20 ;' '
:100044A7 7505
jne 100044AE
:100044A9 8A0E
mov cl, byte ptr [esi] ;esi points to unfiltered string
char
:100044AB 880B
mov byte ptr [ebx], cl ;ebx points to filtered string
char
:100044AD 43
inc ebx
:100044AE 8A4601
mov al, byte ptr [esi+01]
:100044B1 46
inc esi
:100044B2 84C0
test al, al
:100044B4 75DB
jne 10004491
For example, if the string is '+mISu '98 +mISu', the filtered string
will be 'mISu mISu'.
|
:10003B70 56
push esi
:10003B71 8B742408
mov esi, dword ptr [esp+08] ;username string address
:10003B75 56
push esi
:10003B76 FF158CF50710
Call lstrlenA
:10003B7C 83F805
cmp eax, 00000005 ;verifies if the username string
:10003B7F 7D04
jge 10003B85 ;has at least
5 characters
:10003B81 33C0
xor eax, eax
:10003B83 5E
pop esi
:10003B84 C3
ret
:10003B85 682D224900
push 0049222D ;an encoding key
:10003B8A 56
push esi ;username string
address
:10003B8B E820500000
call 10008BB0 ;call encryption routine
:10003B90 83C408
add esp, 00000008 ;in eax is the key
:10003B93 8BF0
mov esi, eax
:10003B95 8B44240C
mov eax, dword ptr [esp+0C] ;regcode string address
:10003B99 50
push eax
:10003B9A E801B30400
call 1004EEA0 ;code transforming routine
:10003B9F 83C404
add esp, 00000004
:10003BA2 33C9
xor ecx, ecx
:10003BA4 3BF0
cmp esi, eax ;compare the two keys
:10003BA6 0F94C1
sete cl
:10003BA9 8BC1
mov eax, ecx ;set in eax the result
:10003BAB 5E
pop esi
;1 if good buyer
:10003BAC C3
ret
;0 if bad cracker
|
|
int validate(char*
name)
{
int
i,j=0,v=0;
for(i=0;i<strlen(name);i++)
{
name[i]=toupper(name[i]);
name[j]=name[i];
if(name[i]>='A'&&name[i]<='Z'||name[i]==' ')
{
if(name[i]!=' ') v++;
j++;
}
}
name[j]=0;
return
v;
}
The main function will read the name until it's valid and then display the key:
int main(int argc, char*
argv[])
{
long
key;
int
i;
char
name[100];
cout
<< endl;
cout
<< "============================" << endl;
cout
<< "KeyMaker for PicaView32 1.21" << endl;
cout
<< "----------------------------" << endl;
cout
<< "Coded by +mISu, 05-29-1998 " << endl;
cout
<< "============================" << endl;
do
{
cout << "Name: ";
gets(name);
if(strlen(name)>30) name[30]=0;
}
while(validate(name)<5);
key=createkey(name);
cout
<< "Key: " << key << endl;
cout
<< "============================" << endl;
return
0;
}
Okay, now let's get the first block of code from the encryption routine:
:10008BB0 81ECA8000000
sub esp, 000000A8
:10008BB6 53
push ebx
:10008BB7 55
push ebp
:10008BB8 8BAC24B4000000
mov ebp, dword ptr [esp+000000B4] ;username string address
:10008BBF 56
push esi
:10008BC0 57
push edi
:10008BC1 8BFD
mov edi, ebp
:10008BC3 83C9FF
or ecx, FFFFFFFF
:10008BC6 33C0
xor eax, eax
:10008BC8 33F6
xor esi, esi
:10008BCA F2
repnz
:10008BCB AE
scasb
:10008BCC F7D1
not ecx
:10008BCE 49
dec ecx ;in ecx is now the username
string length
:10008BCF 89742410
mov dword ptr [esp+10], esi
:10008BD3 6683F901
cmp cx, 0001
:10008BD7 0F82A0010000
jb 10008D7D
:10008BDD 6683F950
cmp cx, 0050
:10008BE1 0F8796010000
ja 10008D7D ;verifies if it is between 1 and 80, if it isn't,
exit
:10008BE7 39B424C0000000
cmp dword ptr [esp+000000C0], esi ;in esi is 0
:10008BEE 0F85C6000000
jne 10008CBA ;in [esp+c0] is the encryption key
Comparing 0 with 0049222d it will always jump to 10008cba. Let's continue
our reverse engineering to
1008cba:
:10008CBA 8BD9
mov ebx, ecx ;in ebx is strlen(username)
:10008CBC 33FF
xor edi, edi ;edi is the counter
:10008CBE 81E3FFFF0000
and ebx, 0000FFFF
:10008CC4 7E2D
jle 10008CF3
:10008CC6 0FBE4C3500
movsx ecx, byte ptr [ebp+esi] ;gets char
:10008CCB 51
push ecx
:10008CCC E8BF620400
call 1004EF90 ;uppercase eax
:10008CD1 0FAF8424C4000000
imul eax, dword ptr [esp+000000C4] ;multiplies with encryption key
:10008CD9 03C7
add eax, edi ;adds counter to the result
:10008CDB 83C404
add esp, 00000004
:10008CDE 25FFFF0000
and eax, 0000FFFF ;keeps only 16-bit value
:10008CE3 99
cdq
:10008CE4 F7FB
idiv ebx ;gets in edx the rest of the division of eax by strlen
:10008CE6 47
inc edi ;next char
:10008CE7 6689547418
mov word ptr [esp+2*esi+18], dx ;saves the value
:10008CEC 0FBFF7
movsx esi, di
:10008CEF 3BF3
cmp esi, ebx
:10008CF1 7CD3
jl 10008CC6
So, the program generates a table with some values between 0 and strlen-1 at [esp+18]. The table has strlen values. Let's do the same thing in C/C++:
length=strlen(name);
key=0x0049222d;
for(i=0;i<length;i++)
{
var=(name[i]*key+i)&0xffff;
table[i]=(int)(var%length);
}
The table is stored in int table[100]. Let's get the next block of code:
:10008CF3 33FF
xor edi, edi
:10008CF5 3BDF
cmp ebx, edi
:10008CF7 897C2414
mov dword ptr [esp+14], edi
:10008CFB 7E43
jle 10008D40
:10008CFD 33F6
xor esi, esi
:10008CFF 668B747C18
mov si, word ptr [esp+2*edi+18] ;gets number from the table
:10008D04 0FBE543500
movsx edx, byte ptr [ebp+esi] ;gets the sith character
from string
:10008D09 52
push edx
:10008D0A E881620400
call 1004EF90 ;uppercase eax
:10008D0F 8BD0
mov edx, eax
:10008D11 8BCE
mov ecx, esi
:10008D13 D3E2
shl edx, cl ;shifts left edx by table value
:10008D15 83C404
add esp, 00000004
:10008D18 47
inc edi
:10008D19 8B742410
mov esi, dword ptr [esp+10]
:10008D1D 0FAFD7
imul edx, edi ;multiplies edx with (counter+1)
:10008D20 0FAF9424C0000000
imul edx, dword ptr [esp+000000C0] ;multiplies edx with enc. key
:10008D28 0BD0
or edx, eax ;ors edx with char
:10008D2A 8B442414
mov eax, dword ptr [esp+14] ;updates the counter
:10008D2E 03F2
add esi, edx ;ads to the key edx
:10008D30 40
inc eax
:10008D31 0FBFF8
movsx edi, ax
:10008D34 3BFB
cmp edi, ebx
:10008D36 89742410
mov dword ptr [esp+10], esi ;saves the key until now
:10008D3A 89442414
mov dword ptr [esp+14], eax ;saves the counter
:10008D3E 7CBD
jl 10008CFD
Well, I think this is the part that does everything. Let's do the same operations in C/C++:
keynow=0;
for(i=0;i<length;i++)
{
t=table[i];
var=name[t];
var<<=t;
var*=(i+1)*key;
var|=name[t];
keynow+=var;
}
Let's see the last part:
:10008D40 8B442410
mov eax, dword ptr [esp+10]
:10008D44 85C0
test eax, eax
:10008D46 7D06
jge 10008D4E
:10008D48 F7D8
neg eax ;eax=modulo eax
:10008D4A 89442410
mov dword ptr [esp+10], eax
:10008D4E 8B442410
mov eax, dword ptr [esp+10]
:10008D52 85C0
test eax, eax
:10008D54 7508
jne 10008D5E
:10008D56 C7442410DC6F2400
mov [esp+10], 00246FDC ;if the key is zero, gets this value
:10008D5E 8B442410
mov eax, dword ptr [esp+10]
:10008D62 B900CA9A3B
mov ecx, 3B9ACA00
:10008D67 99
cdq
:10008D68 F7F9
idiv ecx ;gets only the last ten digits
:10008D6A 89542410
mov dword ptr [esp+10], edx
:10008D6E 8B442410
mov eax, dword ptr [esp+10]
:10008D72 5F
pop edi
:10008D73 5E
pop esi
:10008D74 5D
pop ebp
:10008D75 5B
pop ebx
:10008D76 81C4A8000000
add esp, 000000A8
:10008D7C C3
ret
Let's finish this:
if(keynow<0) keynow=-keynow;
if(keynow==0)
keynow=0x00246fdc;
keynow%=1000000000;
|
I'm tired, I'm working here for five hours long. Here is the whole C/C++ program:
#include <iostream.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
long createkey(char*);
int validate(char*);
int main(int argc, char*
argv[])
{
long
key;
int
i;
char
name[100];
cout
<< endl;
cout
<< "============================" << endl;
cout
<< "KeyMaker for PicaView32 1.21" << endl;
cout
<< "----------------------------" << endl;
cout
<< "Coded by +mISu, 05-30-1998 " << endl;
cout
<< "============================" << endl;
do
{
cout << "Name: ";
gets(name);
if(strlen(name)>30) name[30]=0;
}
while(validate(name)<5);
key=createkey(name);
cout
<< "Key: " << key << endl;
cout
<< "============================" << endl;
return
0;
}
int validate(char*
name)
{
int
i,j=0,v=0;
for(i=0;i<strlen(name);i++)
{
name[i]=toupper(name[i]);
name[j]=name[i];
if(name[i]>='A'&&name[i]<='Z'||name[i]==' ')
{
if(name[i]!=' ') v++;
j++;
}
}
name[j]=0;
return
v;
}
long createkey(char*
name)
{
int
table[100], length, i, t;
long
var, key, keynow;
length=strlen(name);
key=0x0049222d;
for(i=0;i<length;i++)
{
var=(name[i]*key+i)&0xffff;
table[i]=(int)(var%length);
}
keynow=0;
for(i=0;i<length;i++)
{
t=table[i];
var=name[t];
var<<=t;
var*=(i+1)*key;
var|=name[t];
keynow+=var;
}
if(keynow<0)
keynow=-keynow;
if(keynow==0)
keynow=0x00246fdc;
keynow%=1000000000;
return
keynow;
}
|
Hope you are now convinced that the key generators are the future of
cracking, writing a small C/C++ program is better than giving lamer patches
that don't find the program's dir, and patch in other part that it should,
etc, etc.
For feedback questions, you can contact me at plusmisu@hotmail.com
|