|  | ||
|  |  | |
|  |  | |
| fra_00xx 980605 +mISu 0100 NA PC | This said, here you have an important essay whose purpose is "simply" to teach KNOWLEDGE, written by +mISu (a good reverser who has been contacted by +ORC himself) for all intermediate C++ buffs... Enjoy! | |
|  | ||
|  |  | 
|  | 
|  | 
|  | 
: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
 
|  | 
"Borrowed from Fravia's Dome"