Creazione di un KeyGen per mIRC v6.02
DISCLAIMER
Le informazioni contenute in questo tutorial sono solo a
scopo informativo. Non incoraggiamo nessuno a non
registrare e/o pagare i
programmi che usa.
Tools Utilizzati :
- Softice 4.05
- Ida
4.21 (ma anche la 4.17 va benissimo)
- Api reference
Allora....
lanciamo il programma, vediamo comparire ai nostri occhi un bellissimo
nag-screen un una scritta provocatoria "Unlicensed Copy" che ci svolazza
davanti.....bene vediamo di nasconderla una volta per tutte :)
Per prima
cosa facciamo una copia dell'.exe che dobbiamo analizzare ( chiamiamola per
esempio mirc.orig.exe)questo in modo da poter operare con ida sulla copia del
file e lasciare la possibilità di patchare eventualmente con un hex editor il
file ( ma in questo esempio non verrà toccato un singolo byte, prometto :)
).
Apriamo la nostra copia con ida e aspettiamo che finisca l'analisi,
dopodichè diamo un occhiata alle funzioni che il programma utilizza, per fare
questo premiamo shift + F3, cerchiamo quindi qualche chiamata interessante......
ci troviamo tante chiamate del tipo sub_XXXXX che sono le chiamate a funzioni
interne del programma e verso la fine della lista cominciamo a vedere le
API.
Tra quelle più *utili* ai nostri fini, c'è la SendDlgItemMessage
:
The SendDlgItemMessageA function sends a message to the specified
control in a dialog box.
Syntax
LRESULT SendDlgItemMessage( HWND hDlg,
int
nIDDlgItem,
UINT Msg,
WPARAM
wParam,
LPARAM
lParam
);
Parameters
hDlg
[in] la
finestra
nIDDlgItem
[in] l'oggetto della finestra che deve ricevere il
messaggio
Msg
[in] il messaggio che vogliamo inviare
wParam
[in]
Specifies additional message-specific information.
lParam
[in] Specifies
additional message-specific information.
vediamo se questa funzione
viene utilizzata per la registrazione... andiamo su ? -> register e riempiamo
i campi con un nome (io ho utilizzato -=Rev20=-) e un numero seriale ( che deve
essere dotato di un -, tipo 12345-67890)
lanciamo SoftIce e settiamo un
breakpoint su questa funzione, per far questo facciamo
bpx
SendDlgItemMessageA <invio>
clicchiamo su Register! e tada! softice
brekka, perfetto, ora dobbiamo vedere cosa ha preso la funzione, per fare questo
premiamo 2 volte F11 e ci ritroveremo per magia in MIRC!.text, subito sotto la
chiamata SendDlgItemMessageA se ci spostiamo un po più in su nel codice (
tenendo premuto ctrl + freccia su), all'indirizzo 004C688D
troviamo
:004C688D push
0057A8A8 ; contiene il nostro username
:004C6892 push
3E7h ; numero massimo di
caratteri da leggere
:004C6897 push
0Dh ; Messaggio
inviato
:004C6899 push
83h ; oggetto della
finesta
:004C689E push [ebp+hWnd] ; handle della
finestra
:004C68A1 call SendDlgItemMessageA
:004C68A6 push
0057Ac8F ; contiene il seriale
:004C68AB push 3E7h
:004C68B0 push 0Dh
:004C68B2 push 82h
:004C68B7 push [ebp+hWnd]
:004C68BA call SendDlgItemMessageA
:004C68BF push
0057Ac8F ; mette il seriale sullo stack
:004C68C4
push 0057A8A8 ; e anche l'username
:004C68C9 call
Funzione_Controllo
noi atterriamo 004C68C4 dopo il break, quindi
siamo stati fortunati e andiamo avanti con l' analisi del codice, steppiamo con
f8 a arrivati alla call premiamo nuovamente f8; arriviamo in 004C645D dove si
vede chiaramente da ida che vengono fatte due memset per allocare lo spazio per
le nostre due stringhe (l' username e il serial), inoltre viene
calcolata la
lunghezza del nome e del seriale inseriti
:004C6494 mov esi, edi ; edi=0 , quindi azzera
esi
:004C6496 mov edi, ebx ; in edi il
nome
:004C6498 xor eax, eax ; azzera eax
:004C649A
or ecx, 0FFFFFFFFh ; ecx = ffffffff
:004C649D repne
scasb ; toglie un byte da edi e decrementa
ecx
:004C649F not ecx ; fa il complemento
bit a bit e ottiene
; la lunghezza della stringa comprensiva del \n
:004C64A1 sub edi,
ecx ; riporta edi a puntare all' inizio del
nome
:004C64A3 xchg esi, edi ; in esi ora c'è il
nome
.... viene tolto il carattere di terminazione ....
.... viene
ripetuta la stessa cosa x il serial....
:004C64E5 call
Seriale ; routine che genera il seriale
:004C64EA
test eax, eax ; controlla se il seriale immesso è
valido
:004C64EC jz Invalid ; questo jump
non fa altro che azzerare il nome e
; seriale e poi stampare il nag
:004C64EE mov eax, 1
; Credo serva ad indicare che il serial è valido
:004C64F3 jmp Valid
[Piccola nota .... sono abituato a rinominare le funzioni in ida
questo perchè permette di semplificare le cose e accorgersi se una certa
chiamata o variabile è gia stata visitata in passato, per fare questo basta
premere N dopo aver posizionato il cursore sulla label da
rinominare]
Bene a 004c64E5 abbiamo quello che ci interessa veramente in
questo tutoria, ovvero l'algoritmo di generazione del seriale :)
....
procediamo senza paura ...
il codice da 004C636A a 004C6384 è banale e
calcola semplicemente la lunghezza della stringa e se è minore di 5 jumpa a msg
errore
più interessante è questo pezzetto di codice che inizialmente mi
aveva fatto introdurre un seriale errato
:004C638B push 2Dh ; equivale al carattere
-
:004C638D push esi ; <- chi si vede, il
nostro seriale
:004C638E call _strchr ; cerca - all'interno del
seriale
:004C6393 add esp, 8 ; esp punta al
nome
:004C6396 mov ebx, eax ; copia in ebx la seconda parte del seriale
compreso il -
:004C6398 test ebx, ebx ; se ebx=0 allora non c'è il - e quindi
il
seriale
; è in una forma scorretta risultato...msg di errore
:004C639A jnz
Seconda_Parte ; tutto bene, qui toglie il -
:004C639C xor
eax, eax
:004C639E jmp
errore? ;
nome
.... successivamente controlla se dopo il - non c'è niente,
in tal caso va al solito msg di errore ....
arriviamo ora alla parte clue
:)
:004C63C0 ser_parte2: ; CODE XREF:
Gen1+4D j
:004C63C0 push ebx ; s
:004C63C1 call
_atol ; ebx contiene la seconda parte del seriale
:004C63C6 pop
ecx
:004C63C7 mov [ebp+var_8], eax ; mette la seconda parte del
seriale in ebp-8
:004C63CA push [ebp+s] ;
s
:004C63CD call _strlen
:004C63D2 pop
ecx
; salva in ecx il nome
:004C63D3 mov [ebp+var_C], eax
; salva in ebp-c la lunghezza
:004C63D6 xor eax, eax
:004C63D8 xor ebx,
ebx
:004C63DA mov edx,
3 ; edx
=3
:004C63DF mov ecx, [ebp+s]
; mette in ecx 9433296
:004C63E2 add ecx,
3 ;
aggiunge 3 a ecx
:004C63E5 cmp edx, [ebp+var_C] ; confronta 3 con
la lunghezza del nome
:004C63E8 jge short loc_4C6406 ; se è piu
corta salta
:004C63EA
:004C63EA loop: ; CODE XREF: Gen1+9A
j
:004C63EA movzx esi, byte ptr [ecx] ; mette il byte
puntato da ecx in esi
:004C63ED imul esi, dword_5683B8[eax*4]
:004C63F5
add ebx,
esi
; mette il risultato in ebx
:004C63F5
:004C63F7 inc
eax
; incrementa eax
:004C63F8 cmp eax,
26h ;
confronta eax con 26h ( 38 )
:004C63FB jle short Incrementa
:004C63FD xor
eax, eax
:004C63FF
:004C63FF Incrementa: ; CODE XREF: Gen1+91
j
:004C63FF inc edx
:004C6400 inc
ecx
; si sposta al carattere successivo del nome
:004C6401 cmp edx,
[ebp+var_C] ; controlla se è finito il nome
:004C6404 jl
loop
; mette il byte puntato da ecx in esi
:004C6406
:004C6406 loc_4C6406: ;
CODE XREF: Gen1+7E j
:004C6406 cmp ebx, [ebp+var_4] ;
confronta la prima parte del seriale
; generato con quello da noi immesso
:004C6409 jz short
Prima_OK ; combacia? bene ....
allora jump
:004C640B xor eax,
eax ;
altrimenti si passa alla
; procedura che mostrerà il msgbox
:004C640D jmp short
errore? ; nome
:004C640F
---------------------------------------------------------------------------
:004C640F
:004C640F Prima_OK: ; CODE XREF: Gen1+9F j
:004C640F xor eax,
eax ;
azzera eax
:004C6411 xor ebx,
ebx ;
azzera ebx
:004C6413 mov edx,
3
; mette 3 in edx
:004C6418 mov ecx,
[ebp+s] ; mette in ecx il
nome
:004C641B add ecx,
3 ;
sposta il puntatore sul 4° carattere del nome
:004C641E cmp edx,
[ebp+var_C] ; confronta 3 con la lunghezza del
nome
:004C6421 jge short loc_4C6446 ; se è piu corta
salta
:004C6423
:004C6423 loc_4C6423: ; CODE XREF: Gen1+DA j
:004C6423
movzx esi, byte ptr [ecx] ; mette in esi la parola
puntata da ecx
:004C6426 movzx edi, byte ptr [ecx-1]
; mette in edi la parla puntata da ecx-1
:004C642A imul esi,
edi
; moltiplica esi * edi
:004C642D imul esi, dword_5683B8[eax*4]
:004C6435
add ebx,
esi
; aggiunge esi a ebx
:004C6437 inc
eax
; incrementa eax
:004C6438 cmp eax,
26h
; confronta per vedere se è minore di 26h
:004C643B jle short
loc_4C643F
:004C643D xor eax, eax
:004C643F
:004C643F loc_4C643F: ;
CODE XREF: Gen1+D1 j
:004C643F inc edx
:004C6440 inc ecx
:004C6441 cmp
edx, [ebp+var_C]
:004C6444 jl short
loc_4C6423 ; mette in esi la
parola puntata da ecx
:004C6446
:004C6446 loc_4C6446: ; CODE XREF:
Gen1+B7 j
:004C6446 cmp ebx,
[ebp+var_8] ; confronta se siamo
arrivati
; alla fine della stringa
:004C6449 jz short
Successo
:004C644B xor eax, eax
:004C644D jmp short
errore?
; nome
:004C644F
---------------------------------------------------------------------------
:004C644F
:004C644F Successo: ; CODE XREF: Gen1+DF j
:004C644F mov eax,
1
:004C6454
:004C6454 errore?: ; CODE XREF: Gen1+1C j
:004C6454 ;
Gen1+34 j ...
:004C6454 pop
edi ;
nome
:004C6455 pop
esi ;
seriale
:004C6456 pop
ebx ;
nome + altri dati
:004C6457 mov esp, ebp
:004C6459 pop ebp
:004C645A
retn 8
:004C645A Gen1 endp
Bene bene, dopo questa vagonata di
codice commentato, vediamo di capire cosa fa l'algoritmo innanzi tutto sposta il
puntatore al nome avanti di 3 posizioni
add ecx, 3 ; // noi abbiamo il
primo carattere in ecx[0], cosi andiamo in ecx[2]
per dirla alla
C
confronta se la stringa è + corta e in tal caso salta, altimenti mette
in ESI il primo
byte di ecx
movzx
esi, byte ptr [ecx]
dopodiche esegue una moltiplicazione tra esi e
una parola contenuta in 5683B8[eax*4] che
non è altro che un array avente i
seguenti valori
0x0b, 0x06, 0x11, 0x0c,
/*è stata riportata nel seguente modo x semplificare la visione,
puo
sembrare una marice ma
0x0c, 0x0e, 0x05, 0x0c, visto che viene presa la
parola puntata da eax*4, e eax viene
sempre incrementato,allora cadiamo in
0x10, 0x0a, 0x0b, 0x06, un semplice array; da notare che a 004C63F8 e
004C6438 c'è un
controllo per vedere se eax = 26
0x0e, 0x0e, 0x04, 0x0b,
che in decimale sarebbe 38 questo per non uscire da questo array
*/
0x06,
0x0e, 0x0e, 0x04,
0x0b, 0x09, 0x0c, 0x0b,
0x0a, 0x08, 0x0a, 0x0a,
0x10, 0x08, 0x04, 0x06,
0x0a, 0x0c, 0x10, 0x08,
0x0a, 0x04, 0x10, 0x0
;
dopodiche viene aggiunto esi a ebx
add ebx,
esi;
Questo ciclo viene ripetuto fino a quando è finito il nome, quando
arriviamo a 004C6406 viene effettuato un confronto fra ebx (il seriale generato
fino a questo momento) e ebp-4 ( il seriale immesso da noi). Se combaciano
procede altrimenti messaggio di errore
[P.S. probabilmente non avete
immesso il numero seriale correto al primo colpo, quindi per risolvere il
problema, cambiamoil valore del registro z , per far questo in softice scriviamo
r fl z]
Ok.... procediamo adesso alla seconda parte dell'algoritmo,anche
qui controlla se la lunghezza è minore di 3, in tal caso salta poi finalmente a
004C6423 riprende il carattere puntato da ecx e lo salva in esi, a 004C6426
mette in edi il carattere puntatoda ecx-1 (ecco una prima differenza rispetto
alla prima parte dell'algo). Esegue la moltiplicazione intera fra i due e salva
il risultato in esi, moltiplica esi per il valore in 5683B8[eax*4] e lo aggiunge
a ebx, questo viene ripetuto fino a quando non è finito il
nome.
........Dopo il programma non fa altro che ritornare dala call e
scrivere i dati nel registro.......
Bene bene siamo arrivati alla fine di
questo tutorial che fra l'altro è il primo che scrivo, quindi scusate eventuali
omissioni e/o vuoti nell' analisi dell'algoritmo, è il momento dei
ringraziamenti, in primis voglio ringraziare Ntoskrnl per il suo tutorial su
mirc e per il supporto datomi in chan, dopodichè si ringraziano in ordine
puramente casuale Quequero, TheMr, Andreageddon , tutti i membri di #crack-it e
anche di #pmode.
Attacco qui sotto una parte di codice C che rappresenta
quello che fa il programma asm, in ogni caso è un codice poco ottimizzato, ma
funzionante
void Generate(char *name,
char **serial)
{
int temp=0;
//Used in the second part of the calculation
char
*secondhalf=NULL, //used to store second part of the
serial
*name2=NULL; //copy of
*name ,used to not destroy the original one
int
lenght=strlen(name),
cur=3,
//indicates the offsett relative to name begin
eax=0,
//used as an index for the array
ebx=0;
//used to store calculations
int array[40] = {0x0b, 0x06, 0x11, 0x0c, 0x0c, 0x0e, 0x05,
0x0c, 0x10, 0x0a, 0x0b, 0x06, 0x0e, 0x0e,
0x04, 0x0b, 0x06, 0x0e, 0x0e, 0x04, 0x0b,
0x09, 0x0c, 0x0b, 0x0a, 0x08, 0x0a, 0x0a,
0x10, 0x08, 0x04, 0x06, 0x0a, 0x0c, 0x10,
0x08, 0x0a, 0x04, 0x10, 0x0 }; //taken from mIRC .... it was a matrix used by
the algo
name2=(char*)calloc(1,lenght+1); //alloc space for the
copying name ... +1 for \n char.
secondhalf=(char*)calloc(1,10);
strncpy(name2,name,lenght); //copy the name in
name2
lenght-=3; //mIRC itself
ecludes first tree char. in first part of algo
//First
Part of Algo
while(lenght>0 && eax <
26)
{
ebx+=name2[cur]*array[eax]; //imul esi,
dword_5683B8[eax*4];
lenght--;
eax++;
cur++;
}
sprintf(*serial,"%d-",ebx);
//Second part of
algo
eax=0; //reset
counters....
cur=3;
ebx=0;
lenght=strlen(name);
strncpy(name2,name,lenght); //refresh the name2
string
lenght-=3; //again we
remove the offsett at lenght[2]
while(lenght >0
&& eax<26)
{
temp=name2[cur-1]*name2[cur]; //movzx esi, byte ptr
[ecx] ;
//movzx edi, byte ptr
[ecx-1] ;
//imul esi, edi ;
temp*=array[eax]; //imul esi,
dword_5683B8[eax*4];
ebx+=temp; //add ebx, esi
;
lenght--;
eax++;
cur++;
}
sprintf(secondhalf,"%d",ebx);
strcat(*serial,secondhalf);
}
..... e con questo è
veramente tutto, ricordate, se ritenete utile questo programma compratelo, noi
reversiamo al fine di migliorare l'asm, non venite quindi a chiedere un crack o
serial per una specifica applicazione .......
-Rev20