-
www,crackmes.de. Il keygenme in questione è il KeyGen-me N° 1 di devilz. Lo trovate nella sezione "A-D".
- Un keygenme, niente di più :)
-
Weeeeeeee, finalmente si iniziaaaaaaaaaaa! :P
Mano a IDA (o quello che preferite) e iniziamo subito il reversing del programma...
Diamo uno sguardo alle funzioni importate, notiamo subito che l'unica funzione che potrebbe prendere il testo
dai textbox è GetDlgItemTextA (perchè potrebbe benissimo fare un SendMessageA con WM_GETTEXT). Doppioclikkiamoci
sopra e andiamo a guardare le xref. Ce ne sono 4, noi dirigiamoci verso la prima.
Inizio subito col dire che ho rinominato un po' di locazioni per semplificarmi la vita, quindi voi sul vostro disassemblato
le cose le vedrete leggermente diverse :)
Con un po' di intuito possiamo subito capire che quella chiamata prende il nome dal textbox:
.text:004010E1 pusha
.text:004010E2 push 0Ch --------------------------> ; nMaxCount
.text:004010E4 push offset Nome ------------------> ; lpString
.text:004010E9 push 64h --------------------------> ; nIDDlgItem
.text:004010EB push [ebp+hDlg] -------------------> ; hDlg
.text:004010EE call GetDlgItemTextA --------------> ; Si prende il nome
.text:004010F3 or eax, eax
.text:004010F5 jnz short PijaSerial --------------> ; Lunghezza diversa da 0? Salta
.text:004010F7 push 0 ----------------------------> ; uType
.text:004010F9 push offset aFillInTheBlank -------> ; lpCaption
.text:004010FE push offset aTheNamePlease --------> ; lpText
.text:00401103 push 0 ----------------------------> ; hWnd
.text:00401105 call MessageBoxA ------------------> ; Altrimenti stampami la msgbox d'errore
.text:0040110A leave
.text:0040110B retn 10h
Bene, abbiamo preso il nome, ora passiamo al seriale:
.text:00401110 PijaSerial:
.text:00401110 push 0Ch --------------------------> ; nMaxCount
.text:00401112 push offset Nome ------------------> ; lpString
.text:00401117 push 0C8h -------------------------> ; nIDDlgItem
.text:0040111C push [ebp+hDlg] -------------------> ; hDlg
.text:0040111F call GetDlgItemTextA --------------> ; Prendi il serial
.text:00401124 or eax, eax
.text:00401126 jnz short GenSerial ---------------> ; Lunghezza diversa da 0? Salta
.text:00401128 push 0 ----------------------------> ; uType
.text:0040112A push offset aFillInTheBla_0 -------> ; lpCaption
.text:0040112F push offset aTheSerialPleas -------> ; lpText
.text:00401134 push 0 ----------------------------> ; hWnd
.text:00401136 call MessageBoxA ------------------> ; Altrimenti stampa la msgbox d'errore
.text:0040113B leave
.text:0040113C retn 10h
Praticamente è uguale alla prima sezione di codice, andiamo avanti nel "cuore dell'algoritmo". Infatti in questa
parte viene generato il seriale:
.text:0040113F GenSerial:
.text:0040113F push 0Ch --------------------------> ; nMaxCount
.text:00401141 push offset Nome ------------------> ; lpString
.text:00401146 push 64h --------------------------> ; nIDDlgItem
.text:00401148 push [ebp+hDlg] -------------------> ; hDlg
.text:0040114B call GetDlgItemTextA --------------> ; Si riprende il nome
.text:00401150 xor edx, edx ----------------------> ; Azzera edx...
.text:00401152 xor ebx, ebx ----------------------> ; ...ebx
.text:00401154 xor ecx, ecx ----------------------> ; ...ecx
.text:00401156 xor eax, eax ----------------------> ; ...eax
.text:00401158 mov esi, offset Nome --------------> ; inizializza esi
.text:0040115D
.text:0040115D loop:
.text:0040115D mov bl, [ecx+esi] -----------------> ; Prende ogni char del nome
.text:00401160 add eax, ebx ----------------------> ; e somma il suo valore hex a eax
.text:00401162 inc ecx ---------------------------> ; incrementa ecx
.text:00401163 cmp bl, 0 -------------------------> ; NON siamo arrivati alla fine del nome?
.text:00401166 jnz short loop --------------------> ; allora ripeti il ciclo
.text:00401168 mov edx, 28h ----------------------> ; sposta in edx il valore 28h
.text:0040116D mul edx ---------------------------> ; moltiplica eax per edx (28h)
.text:0040116F add eax, 19h ----------------------> ; aggiunge 19h a eax
.text:00401172 push eax --------------------------> ; converte a stringa
.text:00401173 push offset unk_0_403364 ----------> ; il serial giusto (che si trova in eax)
.text:00401178 push offset RightSerial -----------> ; e lo memorizza in right serial
.text:0040117D call wsprintfA --------------------> ; con wsprintf
.text:00401182 add esp, 0Ch ----------------------> ; aggiunge 0Ch allo stack pointer
.text:00401185 mov edx, offset RightSerial -------> ; sposta in edx il serial giusto
.text:0040118A push edx --------------------------> ; e lo pusha nello stack
.text:0040118B push 0Ch --------------------------> ; nMaxCount
.text:0040118D push offset SerialInserito --------> ; lpString
.text:00401192 push 0C8h -------------------------> ; nIDDlgItem
.text:00401197 push [ebp+hDlg] -------------------> ; hDlg
.text:0040119A call GetDlgItemTextA --------------> ; riprende il serial
.text:0040119F mov esi, offset SerialInserito ----> ; sposta il serial inserito in esi
.text:004011A4 pop edx ---------------------------> ; si riprende il serial giusto dallo stack
.text:004011A5 mov eax, [esi] --------------------> ; Carica in eax il serial inserito
.text:004011A7 mov ebx, [edx] --------------------> ; Carica in ebx il serial giusto
.text:004011A9 cmp eax, ebx ----------------------> ; Confronta eax con ebx (serial vero con serial inserito)
.text:004011AB jnz short BadSerial ---------------> ; Se non coincidono salta
.text:004011AD push 0 ----------------------------> ; uType
.text:004011AF push offset aGoodWork -------------> ; lpCaption
.text:004011B4 push offset aGoodSerialNowS -------> ; lpText
.text:004011B9 push 0 ----------------------------> ; hWnd
.text:004011BB call MessageBoxA ------------------> ; altrimenti... congratulazioni :)
.text:004011C0 jmp short loc_0_4011D5
.text:004011C2 ; ----------------------------------------------------------------------
.text:004011C2
.text:004011C2 BadSerial:
.text:004011C2 push 0 ----------------------------> ; uType
.text:004011C4 push offset aFatalError -----------> ; lpCaption
.text:004011C9 push offset aBadBoyReadMore -------> ; lpText
.text:004011CE push 0 ----------------------------> ; hWnd
.text:004011D0 call MessageBoxA ------------------> ; chiama la msgbox d'errore
.text:004011D5
.text:004011D5 loc_0_4011D5:
.text:004011D5 popa
.text:004011D6 leave
.text:004011D7 retn 10h
Il listato è molto semplice da comprendere, anche perchè c'è praticamente un commento per ogni istruzione :) Tuttavia,
volevo precisare che il serial è memorizzato in chiaro, senza nessun tipo di "protezione", percui quando
arrivate al cmp eax, ebx (a 004011A9), se fate da SoftICE ? eax avrete il seriale giusto in
bella mostra.
Però noi vogliamo farci un keygen, quindi i passi da compiere sono:
- Acquisire il nome
- Prendere ogni char del nome e sommare il suo valore in hex ad una variabile che parte da 0
- Moltiplicare questa variabile per 28h
- E aggiungerci 19h
In definitiva, il codice del keygen è questo:
#include <string.h>
#include <stdio.h>
int
main()
{
char username[13];
int serial=0;
unsigned int i;
printf("Insert your name: ");
scanf("%s", &username);
for(i=0; i<=strlen(username); i++)
serial += username[i];
serial *= 0x28;
serial += 0x19;
printf("Serial calculated: %d\n", serial);
return 0;
}
Prendo 13 caratteri, perchè se notate, nella prima chiamata a GetDlgItemTextA, viene passato come ultimo parametro (il
primo a partire da sopra, visto che lo stack usa l'architettura LIFO, Last In First Out) il massimo numero di caratteri
da prendere, che equivale a 0Ch = 13d.
Compilate il tutto ed eseguite il programma, ed ecco che il nostro keygen è pronto :)
Bene, ma avevo detto che avremmo convinto il programma a svelarci il seriale, quindi all'opera :)
Come ho fatto notare in precedenza, il seriale è memorizzato in chiaro, quindi non avremo bisogno di complicarci
la vita con decrypting vari.
Potremmo far comparire, al posto della messagebox che ci dice che il serial che abbiamo inserito è sbagliato, una msgbox
che ha come testo il serial giusto :)
Diamo un'occhiata a 004011C9: lì viene pushato il testo della msgbox d'errore, quindi perchè non modificare quell'istruzione
in modo che ci mostri il nostro serial? La modifica è a dir poco banale:
- apriamo Hiew
- andiamo a 004011C9
- F3->F2 e mettiamo push edx seguito da 4 nop (per bilanciare gli opcode)
- F9
- F10
Perchè pushiamo edx? Semplice, perchè in edx c'è il nostro vero serial che NON è stato modificato in nessun modo :)
Alcuni di voi diranno, "ma non si dovrebbe fare push edx?". Si, si dovrebbe, ma se guardate qualche istruzione
prima, c'è un bel mov edx, offset RightSerial che rende inutile il
push offset.
E' stato abbastanza semplice, non trovate? :)
kratorius
Un saluto a tutti gli amici di #crack-it, in particolare a Ironspark, AndreaGeddon, al Que che m'ha pubblicato sta specie
di tutorial (e che magari si faceva vedere ogni tanto :)), a Ntoskrnl e a chi ho dimenticato :)
Niente di illegale stavolta, stiamo reversando un keygenme :)