Zoom Icon

Corso UIC Avanzato 02

From UIC Archive

Corso UIC Avanzato 02

Contents


Corso UIC Avanzato 02
Author: Quequero
Email: Email
Website: http://quequero.org
Date: 19/09/1999 (dd/mm/yyyy)
Level: Working brain required
Language: Italian Flag Italian.gif
Comments:



Essay

Benvenuti a tutti nella seconda lezione della UIC, stavolta il livello sarà leggermente più alto della prima lezione, oggi parleremo di programmi che generano una chiave e la salvano nel seriale nel registro in forma crittata.

Nella prima lezione avevamo fatto un accenno ai generatori di chiavi ma non eravamo andati oltre dal momento che dovevamo crackare il programma e non reversarlo, questa volta invece vi verrà chiesto di creare un keymaker per un programma e di spiegare la routine di generazione, quindi questo corso è indicato a chi ama di più il reversing rispetto al cracking.

Dal momento che dovevamo parlare di programmi che creano un seriale da un nome facciamo subito un piccolo esempio: al giorno d'oggi la maggior parte dei programmi shareware mette a disposizione dell'utente un box di registrazione nel quale deve essere inserito il proprio nome ed un numero che verrebbe venduto in genere a molto da una SoftwareHouse, il lavoro del reverser è allora quello di capire cosa cerca il programma in quel box e darglielo. Come al solito per brekkare nella routine principale si possono usare i tre breakpoint più famosi nel mondo del reversing cioè: GetWindowTextA, GetDlgItemTextA, hmemcpy

una volta premuto il pulsante di registrazione steppate finchè vi ritrovate nella routine, una volta dentro osservate il codice ed andate avanti, in linea di massima vedrete che ogni carattere del vostro nome verrà manipolato nei più vari modi per poi essere confrontato con il seriale che avevate inserito, o meglio vedrete che verrà generato un seriale dal vostro nome, verrà poi crittato e dopo ancora verrà confrontato con il vostro serial crittato allo stesso modo, per rendervi le idee più chiare vi faccio uno schemetto che riassume il modo "generale" di agire di quasi tutti i programmi di questo genere:

Fase 1

Viene preso il nome > che viene manipolato > E crittato!
Esempio
Quequero > (Quequero*2)+(Que*quero)+(Que/2) = 568 (568 xor 25)rol6(xor 40) > 874

Fase 2

Viene preso il numero > Che viene crittato allo stesso modo
666111666 > (568 xor 25)rol6(xor 40)...874

Fase 3 I due numeri crittati vengono confrontati..... 874 & 874 = OK 874 & 975 = NO ...E se combaciano il numero viene salvato nel registro con il nome, altrimenti NO, talvolta il numero viene salvato anche se sbagliato ma il programma quando viene avviato se ne accorge e si setta automaticamente in modalità shareware.

Adesso che sapete come agisce la generazione del numero, vediamo anche come ne avviene la lettura al riavvio del programma:

1) Il prg apre la propria chiave nel registro (RegOpenKeyA) e la legge (RegQueryValueA)

2) Il prg apre la chiave che contiene il nostro nome e lo legge, lo manipola, lo critta e lo salva

3) Il prg richiama il numero e nome (manipolato) letti e li confronta

4) Sono uguali? Setta modalità Registrata altrimenti setta modalità Shareware

Da questo momento sapete che per brekkare nella routine avete ben due momenti cioè: la registrazione e la lettura, se volete crackare il target vi consiglio di usare il secondo attacco, se invece lo volete reversare vi consiglio il primo. Attenzione però che alcuni programmi (come Advanced Zip Password Recovery) fanno un terzo check al numero ogni volta che avviate una qualunque funzioni, quindi amici crackatori state attenti. Dopo questo noioso ma indispensabile pezzo di teoria vi presento qualche esempietto pratico, partiamo con una semplice routine di manipolazione che manipola i primi 4 caratteri del nome:

Call GetWindowTextA, hwnd, offset nome, 15 // Prende il nome e lo mette nel buffer "nome" mov edi, offset nome // routine di calcolo della lunghezza del nome or ecx, -1 // routine di calcolo della lunghezza del nome not ecx // lunghezza in ECX xor eax, eax mov eax, dword ptr [nome] // Muovi 4 caratteri del nome in eax cdq // Convert DoubleWord to Quad per preparare una divisione mov edi, 185h // Prepara il divisore div edi // Dividi EDI per eax (nome/185h) add eax, edx // Aggiungi al risultato il resto della divisione sub ecx, 4 // Sottrai 4 lettere alla lunghezza del nome (le abbiamo messe in eax) cmp ecx, 0 // il nome è finito? jle ......

Prima di affrontare una routine di crittazione vorrei fare un piccolo preambolo teorico: allora, crittare un seriale significa mascherarlo in modo da non farlo sembrare ciò che in realtà è, i programmatori per confonderci ricorrono a svariate tecnice di crittazione per non farci capire niente ma con un po' di pratica tutti saranno in grado in più o meno tempo di capire l'algoritmo. Adesso vi starete chiedendo: ora che sappiamo che significa crittare un seriale come facciamo a sapere se il nostro programma usa questa tecnica? Dunque il procedimento non è troppo complesso, vi dico subito che per cifrare qualcosa si usano degli operatori matematici e logici quali ad esempio: Rol, Ror, Shl, Shr e Xor che tratteremo dopo, se durante lo step vedete una delle prime 2 istruzioni state pur certi che quella è mano del programmatore dal momento che nessun, e ribadico NESSUN compilatore produce istruzioni come Rol e Ror, in genere se in una comune routine di calcolo trovate anche qualche shl o shr sarete quasi certi che anch'essa sia una routine di crittazione, se invece state reversando una demo grafica allora non fate caso ai vari shl o shr dal momento che in quel campo sono ampiamente usati. Dopo questo breve preambolo è ora di fare le presentazioni per spiegare il funzionamento di queste istruzioni:

Ror = Rotate Right: ruota un bit a destra
Rol = Rotate Left: ruota un bit a sinistra
Shr = Shift Right: Shifta un bit a destra
Shl = Shift Left: Shifta un bit indovinate un po' dove? :)
Xor = Exclusive Or: Or esclusivo



Mentre per gli operatori Shift e Rotate non mi sembra il caso di perdere tempo a spiegare, credo che sia invece necessario per lo Xor. L'istruzione xor non fa altro che confrontare due valori e riportare un 1 se i due valori erano differenti ed uno 0 se erano uguali, ecco un esempio, prendiamo un numero binario e lo xoriamo con un altro numero sempre binario:

101100 Xor

110101  =

----------

011001 
 
ecco cosa fa lo xor, la sua peculiarità e che se il risultato si rixora per lo stesso valore riotteniamo il numero di prima: 

011001  Xor             011001  Xor

110101  =        &      101100  =

-----------             -----------

101100                  110101 
 
elementare no? Si xora un valore per un altro valore e quindi lo si critta, lo si rixora per lo stesso valore e lo si decritta.

bene bene, per capirne di più sullo Xor andate a leggere il mio tutorial alla UIC. Dunque vediamo ora come vengono usati questi operatori con un'altra semplice routine:

valore dd 1234h serial_esatto dd ?


snip snip -----

mov eax, dword ptr [serial_esatto] // Muove il serial esatto dal buffer in eax rol eax, 5 // Ruota a sinistra di 5 bit eax shr eax, 4 // Shifta a destra eax xor eax, 45h // Xora il risultato con un valore (45h) xor eax, serial_esatto // Xora eax con il serial esatto xor eax, dword ptr [valore] // Xora il risultato con un valore predefinito preso da valore


con questa routine abbiamo dimostrato alcuni dei più comuni modi di usare uno xor, ma partiamo dall'inizio, il seriale esatto viene spostato dal proprio buffer in eax, viene rotato di 5 bit, viene shiftato di 4 bit ed il risultato viene xorato con un valore predefinito, il numero viene poi rixorato con il serial esatto (e quindi con un numero variabile e non con un valore predefinito) e poi viene xorato con i primi 4 byte presi da una doubleword definita ad inizio programma, questa tecnica viene usata per non rendere troppo esplicito il valore con cui viene xorato il numero. Ma ora che il numero è crittato fino alle gengive come facciamo a decrittarlo? Innanzitutto nella routine di un programma vero non sarebbe necessario ma se volessimo farlo per sniffare il seriale che ci interessa potremmo utilizzare un metodo abbastanza banale cioè: per decrittare un valore che è stato shiftato basta rishiftarlo dall'altro lato per lo stesso valore e questo vale anche per un valore rotato, se invece c'è anche stato uno xor, basta rixorarlo per lo stesso valore, ecco ad ogni modo la routine di decrittazione che dovremmo utilizzare per leggere il numero che esce fuori dalla routine di sopra, sarebbe totalmente inutile decrittare il numero dal momento che potremmo leggerlo dal buffer [serial_esatto]: mov eax, dword ptr [serial_crittato]// Muove il serial crittato in eax ror eax, 5 // Ruota a destra di 5 bit eax shl eax, 4 // Shifta a sinistra eax xor eax, 45h // RiXora il risultato con un valore (45h) xor eax, serial_esatto // RiXora eax con il serial esatto xor eax, dword ptr [valore] // RiXora il risultato con un valore predefinito

                                   // preso da valore

come vedete decrittare qualcosa conoscendo la routine "madre" è semplice ed è sempre la stessa pappa, quando studieremo il codice auto-modificante (SMC da Self-Modifyng Code) impareremo altre tecniche di crittazione più sofisticate. Ci si ribecca alla prossima lezione :)

Obbiettivi
  • Trovare il serial relativo al proprio nome/nick e creare un piccolo keygenerator, se non conoscete alcun linguaggio (bhè almeno un pochetto di asm lo dovreste conoscere...È comunque ora di impararlo visto che in altre occasioni saranno richiesti veri e propri programmi) DOVETE spiegare molto dettagliatamente la routine utilizzata, potete usare Sice, IDA, W32Dasm, quello che volete insomma
  • OBBLIGATORIO: trovare un bel nome per l'iguana di Quequero

Quequero


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.