Zairon's CrackMe
From UIC
Zairon's Crackme Serial Fishing
Contents |
| Infos | |
|---|---|
| Author: | Spider |
| Email: | spider_xx87 (AT) hotmail (DOT) com |
| Website: | http://bigspider.has.it/ |
| Date: | 01/03/2002 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Que: Al prossimo form messo male ti uccido :) cmq bravo :PPP |
Un bel crackme nome/serial by ZaiRoN. Scaricate qui il sorgente del KeyGenerator.
Introduzione
Il programma vittima è un crackme di ZaiRon. Vediamo le rulez:
- NO patching - NO brute forcing - keygen it!
Bene... vediamo cosa possiamo fare! :-)
Tools
- SoftICE qualunque versione
- un disassembler: spesso il codice è più chiaro da IDA che da softice :-)
Link e Riferimenti
Questo è il Crackme n° 41 disponibile alla pagina Crackme 26 - 50.
Essay
Bene, iniziamo. Mettiamo un nome (Spider) ed un seriale a caso (123123123) e da sice mettiamo un breakpoint su GetWindowTextA e GetDlgItemTextA. Premiamo OK e SoftICE poppa sullo schermo. Siamo alla riga successiva alla chiamata a GetDlgItemTextA che si occupa di prelevare il nome immesso; poche righe più tardi troviamo la seconda chiamata che si occupa invece di prelevare il serial inserito.
All'offset 004010AA troviamo un "call 0040115E". Vediamo cosa c'è a quell'indirizzo:
<nowiki>:</nowiki>00401160 xor eax, eax ;azzera eax
<nowiki>*</nowiki> Jump at Address:
|:00401178(U)
|
<nowiki>:</nowiki>00401162 cmp byte ptr [ecx+esi], 30h ;se l'ASCII del char è minore di 30h...
<nowiki>:</nowiki>00401166 jb 0040119E ;...serial errato
<nowiki>:</nowiki>00401168 cmp byte ptr [ecx+esi], 3Ah ;se non è minore di 3Ah...
<nowiki>:</nowiki>0040116C jnb 0040117A ;...salta a 0040117A
<nowiki>:</nowiki>0040116E sub byte ptr [ecx+esi], 30h ;sottrae 30h
<nowiki>:</nowiki>00401172 inc ecx ;incrementa ecx per passare al prossimo char
<nowiki>*</nowiki> Jump at Address:
|:0040119C(U)
|
<nowiki>:</nowiki>00401173 cmp ecx, 00000009 ;se abbiamo già controllato 9 caratteri
<nowiki>:</nowiki>00401176 je 004011A3 ;salta al "ret"
<nowiki>:</nowiki>00401178 jmp 00401162 ;altrimenti ripete il ciclo
<nowiki>*</nowiki> Jump at Address:
|:0040116C(C)
|
<nowiki>:</nowiki>0040117A cmp ecx, 00000002 ;se il carattere non numerico è il terzo
<nowiki>:</nowiki>0040117D je 0040118B ;salta a 40118B
<nowiki>:</nowiki>0040117F cmp ecx, 00000005 ;se è il sesto
<nowiki>:</nowiki>00401182 je 0040118B ;salta a 40118B
<nowiki>:</nowiki>00401184 cmp ecx, 00000008 ;se è il nono
<nowiki>:</nowiki>00401187 je 0040118B ;salta a 40118B
<nowiki>:</nowiki>00401189 jmp 0040119E ;altrimenti... serial errato
<nowiki>*</nowiki> Jump at Addresses:
|:0040117D(C), :00401182(C), :00401187(C)
|
<nowiki>:</nowiki>0040118B cmp byte ptr [ecx+esi], 41h ;se il carattere è minore di "A" (41h)...
<nowiki>:</nowiki>0040118F jb 0040119E ;...serial errato
<nowiki>:</nowiki>00401191 cmp byte ptr [ecx+esi], 45h ;se il carattere è maggiore di "E" (45h)...
<nowiki>:</nowiki>00401195 ja 0040119E ;...serial errato
<nowiki>:</nowiki>00401197 sub byte ptr [ecx+esi], 37h ;sottrae 37h al carattere
<nowiki>:</nowiki>0040119B inc ecx ;incrementa ecx
<nowiki>:</nowiki>0040119C jmp 00401173 ;salta a 00401173
<nowiki>*</nowiki> Jump at Addresses:
|:00401166(C), :00401189(U), :0040118F(C), :00401195(C)
|
<nowiki>:</nowiki>0040119E mov eax, 00000001 ;eax viene messo ad 1 quando il serial è errato
<nowiki>*</nowiki> Jump at Address:
|:00401176(C)
|
<nowiki>:</nowiki>004011A3 ret
Questa procedura lavora solo sui primi 9 caratteri del serial. Sono considerati caratteri validi soltanto i caratteri numerici (da '0' a '9', ossia da 30h a 39h), eccetto per il terzo, il sesto e il nono carattere che oltre a valori numerici possono avere i valori 'A' (41h), 'B' (42h), 'C' (43h), 'D' (44h), 'E' (45h). Nel caso di valori numerici viene sottratto 30h, quindi il valore finale sarà un numero compreso tra 0 e 9; nel caso di valori letterali viene invece sottratto 37h, e il valore finale sarà un numero compreso tra 10 e 14 (in pratica se ne considera il valore esadecimale. Ad esempio 0Ah esadecimale equivale a 10 decimale). Chiaro?
Bene, immettiamo un nuovo seriale, stando attenti a rispettare le condizioni imposte dalla procedura appena vista. Io ho usato il serial '123123123'. Dopo la call di poco fa troviamo un "test eax, eax" e un "jne" che, nel caso eax fosse stato messo ad 1, considera il seriale errato senza bisogno di ulteriori controlli.
Subito dopo avremo davanti una procedura che esegue una serie di calcoli sui caratteri del serial (opportunamente modificato dalla precedente procedura) e confronta il risultato finale con i caratteri del nome inserito. Vengono presi 3 caratteri del serial per volta e il ciclo è ripetuto tre volte. In ogni ripetizione del ciclo il risultato dei calcoli viene confrontato con un carattere del nome inserito (il primo carattere per il primo ciclo, il secondo per il secondo ciclo e il terzo carattere per il terzo ciclo [ma và...]). Per semplificare i calcoli, assegniamo delle lettere ai caratteri del serial coinvolti nel calcolo: a per il primo carattere, b per il secondo e c per il terzo.
<nowiki>:</nowiki>004010B9 mov edx, 00000000 ;azzera edx / come contatori, ma per scopi diversi
<nowiki>:</nowiki>004010BE push edx
<nowiki>*</nowiki> Jump at Address:
|:0040110D(U)
|
<nowiki>:</nowiki>004010BF movzx ebx, byte ptr [edi+0040301B] ;ebx = b
<nowiki>:</nowiki>004010C6 movzx ecx, byte ptr [edi+0040301A] ;ecx = a
<nowiki>:</nowiki>004010CD mov esi, ecx ;esi = a
<nowiki>:</nowiki>004010CF imul eax, ebx, 3 ;eax = 3b
<nowiki>:</nowiki>004010D2 add esi, eax ;esi = a+3b
<nowiki>:</nowiki>004010D4 mov eax, ebx ;eax = b
<nowiki>:</nowiki>004010D6 mul ebx ;eax = b<sup>2</sup>
<nowiki>:</nowiki>004010D8 add esi, eax ;esi = a+3b+b<sup>2</sup>
<nowiki>:</nowiki>004010DA mov eax, ecx ;eax = a
<nowiki>:</nowiki>004010DC mul ecx ;eax = a<sup>2</sup>
<nowiki>:</nowiki>004010DE add esi, eax ;esi = a+3b+a<sup>2</sup>+b<sup>2</sup>
<nowiki>:</nowiki>004010E0 imul ebx, ecx ;ebx = ab
<nowiki>:</nowiki>004010E3 imul ebx, 2 ;ebx = 2ab
<nowiki>:</nowiki>004010E6 add esi, ebx ;esi = a+3b+a<sup>2</sup>+b<sup>2</sup>+2ab
<nowiki>:</nowiki>004010E8 mov eax, esi ;eax = a+3b+a<sup>2</sup>+b<sup>2</sup>+2ab
<nowiki>:</nowiki>004010EA mov edx, 2
<nowiki>:</nowiki>004010EF div dl ;eax = (a+3b+a<sup>2</sup>+b<sup>2</sup>+2ab)/2
<nowiki>:</nowiki>004010F1 imul eax, 0Eh ;eax = (a+3b+a<sup>2</sup>+b<sup>2</sup>+2ab)*7
<nowiki>:</nowiki>004010F4 add al, byte ptr [edi+0040301C] ;eax = (a+3b+a<sup>2</sup>+b<sup>2</sup>+2ab)*7+c
<nowiki>:</nowiki>004010FA pop edx
<nowiki>:</nowiki>004010FB cmp al, byte ptr [edx+00403007] ;confronta con un char del nome
<nowiki>:</nowiki>00401101 jnz short SerialErrato ;se sono diversi... serial errato :)
<nowiki>:</nowiki>00401103 add edi, 3 ;passa ai 3 char successivi del serial
<nowiki>:</nowiki>00401106 cmp edi, 9 ;se il ciclo è stato eseguito 3 volte...
<nowiki>:</nowiki>00401109 jz short 0040110F ;...salta a 0040110F
<nowiki>:</nowiki>0040110B inc edx ;incrementa edx
<nowiki>:</nowiki>0040110C push edx
<nowiki>:</nowiki>0040110D jmp short 004010BF
Bene... o forse male: ci troviamo di fronte ad un'equazione a 3 incognite. Dobbiamo infatti trovare 3 valori a, b e c tali che (a+3b+a2+b2+2ab)*7+c = 83 (83 è il valore decimale di 'S'), e poi lo stesso con 112 ('p') e con 105 ('i'). Le equazioni a 3 incognite hanno infinite soluzioni, ma... un momento! a, b e c hanno un range molto limitato: a e b hanno valori compresi tra 0 e 9, mentre c ha un valore compreso tra 0 e 13, come abbiamo visto prima. Pertanto le combinazioni sono poche, potremmo anche andare a tentativi per trovare i valori delle incognite, ma... meglio far fare tutto al nostro caro PC... Codiamo un piccolo bruteforcer per trovare la giusta combinazione. Ecco il source (scritto in VC++):
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
char name[] = "Spi"; //ci bastano i primi 3 caratteri
char serial[10];
unsigned int a;
unsigned int b;
unsigned int c;
unsigned int count;
for (count = 0; count < 9; count = count + 3) {
for (a = 0; a < 10; a++) {
for (b = 0; b < 10; b++) {
for (c = 0; c < 14; c++) {
if (((a+3*b+b*b+a*a+a*b*2)*7+c) == name[count/3]) {
serial[count + 0] = a + 48;
serial[count + 1] = b + 48;
if (c < 10) {
serial[count + 2] = c + 48;
}
else {
serial[count + 2] = c + 48 + 7;
}
}
}
}
}
}
for (count = 0; count < 10; count++) {
serial[17-count] = serial[count];
}
serial[9] = 0;
MessageBox(NULL,serial,"I've found it!",MB_ICONINFORMATION);
return 0;
}
Bene. Compiliamo, eseguiamo... DING! Ecco la nostra bella MessageBox che ci comunica di aver trovato il serial! Nel mio caso è 02D120217. Abbiamo quindi trovato i primi 9 caratteri del nostro seriale :-)
Osserviamo adesso il codice immediatamente successivo alla procedura di poco fa:
|:00401109(C)
|
<nowiki>:</nowiki>0040110F push 0000001E
<nowiki>:</nowiki>00401111 push 0040301A
<nowiki>:</nowiki>00401116 push 00000BBC
<nowiki>:</nowiki>0040111B push dword ptr [00403031]
<nowiki>*</nowiki> Reference To: GetDlgItemTextA
|
<nowiki>:</nowiki>00401121 Call 004014E4
<nowiki>:</nowiki>00401126 cmp eax, 00000012
<nowiki>:</nowiki>00401129 ja 00401149
Il crackme richiama un'altra volta GetDlgItemTextA per prelevare il seriale, dato che quello prelevato prima era stato modificato per il check. Confronta poi la lunghezza del serial con 12h (18 decimale), e se è maggiore salta alla solita locazione di wrong serial. La lunghezza del seriale è quindi presumibilmente di 18 caratteri.
Dopo questo codice, ha inizio una lunghissima procedura che non riporto a causa delle dimensioni. Inizialmente non avevo capito a cosa servisse; solo steppandola e risteppandola più volte ho intuito la sua funzione. In realtà è semplicissimo: controlla che i secondi 9 caratteri (dal decimo in poi) siano esattamente l'inverso dei primi 9. Pertanto se i primi 9 caratteri erano 02D120217, il serial completo sarà 02D120217712021D20. Inseriamo il serial, premiamo ok e... "Good work!".
Spider
Note Finali
Saluto TheMR che mi ha segnalato il prog e ovviamente tutti gli studenti e frequentatori di UIC, Ringzer0 (finalmente up!), #asm, #crack-it, e, last but not least, il Quequero nazionale a cui dobbiamo l'UIC :)
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.