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