CrackMe 68 Solution
From UIC
CrackMe 68 solution
Contents |
| CrackMe 68 Solution | |
|---|---|
| Author: | Bender0 |
| Email: | |
| Website: | http://www.bender0.altervista.org/ |
| Date: | 18/02/2004 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Ho vinto il premio per la TOC più lunga della storia! |
Tools
Link e Riferimenti
Essay
Darò una spiegazione ESAUSTIVA del processo di keygenning di questo crackme.
Prima di tutto lanciate l'eseguibile per studiarlo... prova2.exe? Che nome è? Ok a parte questo, nome\serial, la about box ci dice solo che dobbiamo fare un keygen, la finestra è in tipico stile dialog il che ci orienta verso GetDlgItemText per breakkare.
Se avete PEditor o qualsiasi altro programma veloce che visualizza gli imports e le sezioni, usatelo ora. Con un'occhiata alle sezioni o alle funzioni importate si può individuare in un attimo se il prog è packato\cryptato (non andrebbe molto lontano importando solo LoadLibrary e GetProcAddress, no?). In questo caso vediamo DialogBoxParamA e GetDlgItemTextA e molte altre, inoltre le sezioni sono perfettamente normali quindi... si procede con il debugger! Fine analisi...
Reversing della Routine
Lanciate ollydbg e caricate il file (F3). Finita l'analisi del codice (olly è molto veloce) il cursore si ferma all'entry point (anche il programma :-). Date le modeste dimensioni del crackme potreste trovare il punto d'attacco solo scorrendo il codice nella CPU window ma... facciamo le cose per bene.
Ricordate gli imports? Alt+F1 e nella command-line scrivete "bp GetDlgItemTextA" (occhio alle maiuscole!) per settare un breakpoint on execution su tale API. Potete amministrare i breakpoint nella apposita finestra (Alt+B). Ora eseguite il programma (F9) e scrivete "bender0" - err - il vostro nick nel campo nome e il vostro codice fiscale o numero di telefono o quel c***o che volete nel campo serial. Premete Try it! e incrociate le dita... Boom! L'olly prende il controllo.
Vi ritrovate in user32.dll. Ora dovete uscirne, quindi o usate execute till return (Ctrl+F9) e poi steppate sul return per ritrovarvi dopo la chiamata alla API, oppure più semplicemente usate execute till user code (Alt+F9) che fa da solo. Vi trovate all'indirizzo 4010DE: nelle 5 righe precedenti vedete i parametri da passare a GetDlgItemTextA e la call. Vedete come olly vi risolve i parametri? E non è tutto... vedete che pusha esi come "buffer"? Durante la chiamata esi non viene modificato, quindi guardate a destra i registri: esi contiene l'indirizzo in memoria del buffer che contiene il vostro nome, e l'olly è così carino da scrivercelo a fianco! Segue un'altra chiamata a GetDlgItemTextA per il serial, vedete che il "buffer" è in ebp. Disabilitate il breakpoint su GetDlgItemTextA usando la breakpoint window (Alt+B) (non viene visualizzato il nome della API ma l'indirizzo, ad ogni modo sotto "module" c'è scritto user32 quindi...). Abbiamo tolto il break perchè adesso andiamo a steppare over (F8) l'altra chiamata a GetDlgItemTextA e al posto di passarci sopra come vogliamo olly avrebbe breakkato proprio sulla API. Steppata la call, vedete a destra su ebp l'indirizzo del buffer e il vostro serial.
Ora vedrete questo (i commenti li ho aggiunti io, per commentare il codice premete ';' col cursore sulla linea desiderata):
004010EB OR ECX,FFFFFFFF ; ecx = -1
004010EE XOR EAX,EAX ; eax = 0
004010F0 MOV EBX,DWORD PTR DS:[<&USER32.MessageBoxA>]
004010F6 REPNE SCAS BYTE PTR ES:[EDI] ; scorre la stringa finchè non incontra uno 0,<br />e ogni volta diminuisce di 1 ecx
004010F8 NOT ECX ; ecx viene negato (ribaltato nei positivi)
004010FA DEC ECX ; e diminuito di 1, ora contiene la lunghezza del nostro nome
004010FB CMP ECX,3 ; confronta la lunghezza con 3
004010FE JNB SHORT prova2.0040110F ; se la lunghezza del nome NON è MINORE di 3,<br />salta la messagebox di errore e prosegue
Capito? Controlla la lunghezza del nome che deve essere almeno 3 caratteri (ho riportato il codice perchè ne vedrete parecchi di questi controlli). Steppate queste righe e (se non avete un nick da 2 caratteri :-) salterete la chiamata a MessageBoxA e potrete proseguire. Notare che, messagebox o meno, il controllo del codice continua, non vi butta fuori. Vi sarete accorti che segue un controllo uguale per la lunghezza del serial... steppatelo allegramente e salterete anche la parte che vi butta fuori! Così se non si scrive niente o quasi in entrambi i campi, escono DUE messagebox scassac***o E POI vi butta fuori. Non è carino.
Ora vi trovate all'indirizzo 40113E. E indovinate? Ora il prog RICONTROLLA la lunghezza di nome e codice... che bello! Steppate pure. Dopo questi altri 2 check a 401162 c'è una routine quasi uguale (oh nooooooo)... La differenza e quel je alla fine, praticamente qui controlla che il nome non sia di zero caratteri (ma non ne sei ancora convinto?!?).
Eccoci a 401170. Alla prima riga viene caricato in ebp l'indirizzo di una particolare stringa, "0123456789:;<=>?@ABCDEFGHIJKL", che chiameremo keystring. Notare che poche linee fa ebx è stato azzerato, perchè ora farà da counter.
00401174 MOV EDX,ESI ; indirizzo del nome in edx
00401176 SUB EBP,ESI ; ebp = indirizzo keystring MENO indirizzo nome
; INIZIO LOOP
00401178 MOV AL,BYTE PTR DS:[EDX+EBP] ; prende un char da keystring e lo mette in al
0040117B MOV CL,BYTE PTR DS:[EDX] ; prende un char dal nome e lo mette in cl
0040117D XOR CL,AL ; vengono xorati questi due valori
0040117F INC EBX ; ebx fa da counter, viene incrementato
00401180 MOV BYTE PTR DS:[EDX],CL ; il valore xorato sostituisce in memoria<br />le lettere del nome
00401182 MOV EDI,ESI ; # (le istruzioni con # servono a calcolare a ogni ciclo<br />la lunghezza del nome)
00401184 OR ECX,FFFFFFFF ; #
00401187 XOR EAX,EAX ; #
00401189 INC EDX ; passa al prossimo char
0040118A REPNE SCAS BYTE PTR ES:[EDI] ; #
0040118C NOT ECX ; #
0040118E DEC ECX ; #
0040118F CMP EBX,ECX ; è l'ultimo carattere del nome?
00401191 JB SHORT prova2.00401178 ; se si prosegui se no passa al prossimo char
; FINE LOOP
Bene, avrete capito che questo loop non fa altro che xorare ogni carattere del vostro nome con quello allo stesso posto nella keystring...
Fate bene attenzione al prossimo pezzetto di codice... hee hee hee...
00401197 MOV EDI,ESI ; carica il nome xorato con keystring in edi
00401199 OR ECX,FFFFFFFF ; # (stessa storia di prima...)
0040119C XOR EAX,EAX ; #
0040119E XOR EBX,EBX ; ebx = 0
004011A0 REPNE SCAS BYTE PTR ES:[EDI] ; #
004011A2 NOT ECX ; #
004011A4 DEC ECX ; #
004011A5 JE SHORT prova2.004011C8 ; controlla che il nome xorato con keystring<br />non sia una stringa di zero caratteri
All'apparenza questo sembra un normale controllo sulla stringa che il programma ha creato nel loop precedente per evitare che contenga solo uno zero... ma provate a immaginare che succede se il primo carattere è zero... ecx conterrà zero e quindi il jump si farà, MA GUARDATE DOVE FINISCE IL JUMP! Alla messagebox di congratulazioni! Studiamo la vulnerabilità (parlo come un professore...sigh): il primo carattere di questa stringa, ovvero il nostro nome xorato con la keystring deve dare 0, la keystring comincia con '0' (30 in hex) quindi "x xor 30 = 0". Ovviamente x deve valere 30: qualsiasi nome che cominci per '0' salterà quindi il controllo e verrà accettato, provare per credere! (peccato che nel mio nick lo zero sia alla fine!)
Dopo questa (intenzionale?) vulnerabilità, abbiamo il check vero e proprio... non riporto neanche il codice perchè si tratta di un loop del tutto simile ai precedenti che ad ogni giro confronta la nuova stringa (nome xorato con keystring) con il nostro serial, carattere per carattere. Ovviamente se ne trova 2 diversi ci butta fuori e tanti saluti...
Creazione di un Keygen
Bene, ora la protezione dovrebbe esservi chiara... ecco il sorgente in c di un keygen per questo crackme:
int main()
{
// la keystring e il buffer per il vostro nick
char keystring[31] = "0123456789:;<=>?@ABCDEFGHIJKL";
char buffer[31];
// intro e raccolta dati
printf("keygen per il crackme69 by eimiar\n");
printf("il tuo nick in uppercase (max 30 chars!): ");
gets(buffer);
// loop che calcola il serial
for (int c=0;c<=30;c++)
{
if (buffer[c] == 0) break;
buffer[c] = buffer[c]^keystring[c];
}
// se poi non lo stampiamo non serve a niente, no?
printf("il tuo serial : ");
printf(buffer);
getchar();
return 0;
}
Ora, anche se il nick lo dovete scrivere voi in uppercase, il serial generato è valido, ma tante volte contiene dei caratteri strani ed è quindi difficile da riportare nel programma.
Abbiamo finito! Il crackme non era decisamente un osso duro ma almeno avete messo le mani sull'olly e avete intravisto cosà può fare...
Note Finali
Thx2:
Que & la UIC (esclusi i lameri che continuano a chiedere crack sul forum)
la blacksun research facility che mi ha insegnato un sacco di cose
phobos che ha mirrorato ringzer0!
Oleh Yuschuk, il tizio che ha scritto ollydbg - senza di lui che tute avrei scritto?
sourceforge che mi aiuta nel coding
cure e smashing pumpkins che ho ascoltato scrivendo questo tute
tool e meshuggah che ho ascoltato wikizzando questo tute ;)
la gente di deviantART che mi vuole bene!
softice che mi blocca l'orologio del win e mi fa arrivare sempre in ritardo!
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 malevole 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.