Zoom Icon

Krypton reversing

From UIC Archive

Yado Krypton v1.0 Reversing generico

Contents


Krypton reversing
Author: anonymous
Email: .
Website: .
Date: 09/10/2002 (dd/mm/yyyy)
Level: Some skills are required
Language: Italian Flag Italian.gif
Comments: .



Introduzione

Eccoci arrivati! Dopo un bel paio d'ore passate a reversare 'sto crackme... devo togliermi lo sfizio di scriverci un bel tutorial dettagliato! :D


Tools


Link e Riferimenti

http://www.quequero.org


Notizie sul Programma

E' un CrackMe che non richiede di inserire nessun nome o nessun serial... o almeno... non nel classico modo! Non tentate di disassemblarlo perché è polimorfe (l'ho constatato a mie spese ;). L'unica cosa che dovete fare è avete taaaaaaaaaaaaaaanta pazienza! ;)


Essay

Si parte! Iniziamo come sappiamo tutti: apriamo il CrackMe. Come siamo soliti, critichiamo (in bene o in male) l'interfaccia grafica e procediamo con il nostro compito! ;) Non ci sono campi! Da questo possiamo dedurre qualcosa: che per registrarlo serve o un KeyFile oppure una chiave nel registro di configurazione. Ad un certo punto, dovete essere attirati dal pulsante 'Info', altrimenti avrete delle difficoltà a registrarlo ;) Quindi, dopo aver premuto il tasto, potete rendervi conto che è inutile disasmare il proggy (anche se non ne spiega il motivo) e che c'è un piccolo 'Joke' (ahò... così lo chiama :) che serve per dirottarci e che dobbiamo evitare (anche se secondo me... ne ha messi più di uno! :). Allora, attivate il SoftICE (se non lo avete già fatto) e clicchiamo sul pulsante 'Register'. Ecco che compare il bel messaggio di errore! Quindi, le possibilità sono 2: o brekiamo su CreateFile oppure su una chiamata di registro. Ma torniamo un attimo indietro: il tasto 'Info' ci ha detto che è inutile disasmare il proggy perché c'è un piccolo trucchetto... ma, a me è venuto spontaneo e l'ho passato sotto il W32Dasm :D. Ho notato che, nella String Reference, ci sono un po' di scritte incomprensibili, mentre due si leggono perfettamente: una è "12345" e l'altra è "Software\Cripton\v1.0". Mi ha colpito una cosa: come mai solo queste si leggono? E' chiaro che questa è una chiave di registro, quindi mi son detto: << sicuramente non legge nulla dalla registry >> e sono andato direttamente sulla CreateFileA (in effetti nel registro ha creato qualcosa, ma noi non abbiamo fatto niente, quindi è veramente inutile... era questo il joke di cui parlava Yado). Apriamo il FileMon e includiamo solo il Krypton. Poi clickiamo su 'Register' e vediamo quali sono i risultati! Urrrk! Oltre al suono del messaggio... non viene letto nessun file! E allora? Se non va sul registro e non va sull'hdd... dove diamine va? Niente panico! Insistete ancora: breakate su CreateFileA e premete di nuovo il tasto di registrazione. Effettivamente il Sice poppa... e vediamo che dopo ci porta ad una MessageBox di errore! Quindi... senza dubbio... va sull'hdd! Arrivato a questo punto... ero davvero disperato, quando mi è venuto in mente il nick di Yado che mi ha fatto insospettire: e se fosse perché c'è attivato il SoftICE? E' una buona possibilità che non abbiamo preso in considerazione! Attiviamo il FrogSICE e il FileMon e clickiamo di nuovo su 'Register'. Eccolo qua! il nostro file! Si chiama YA.DO (che nome! :) Benissimo! Ci tocca creare un file con questo nome nella directory del Krypton! Se adesso cliccate su 'Register', nel FileMon, la richiesta ritorna SUCCESS, ma, nonostante questo... il crackme continua imperterrito a mandarci a quel paese! ^_^ Adesso è arrivato il momento di scervellarci con un po' di assembly! Breakiamo sulla CreateFileA e analizziamo un po' di codice: 0177:0040114E PUSH 00 0177:00401150 PUSH 01 0177:00401152 PUSH 03 0177:00401154 PUSH 00 0177:00401156 PUSH 00 0177:00401158 PUSH 80000000 0177:0040115D PUSH 00402372 Indirizzo del nome del file 0177:00401162 CALL KERNEL32!CreateFileA Chiamata 0177:00401167 CMP EAX, -1 Il file esiste? 0177:0040116A JZ 004011B3 No (salto a errore) 0177:0040116C MOV [0040248B], EAX Si, file trovato Ecco. Se il file viene trovato, il suo handle viene salvato all'indirizzo 0040248B, altrimenti, si salta ad errore (ricordate, quindi, che 004011B3 è un salto ad errore!). Una volta che l'handle del file viene messo al sicuro, vediamo come procede il programma! 0177:00401171 PUSH 00 0177:00401173 PUSH DWORD PTR [0040248B] Viene pushato l'handle del file 0177:00401179 CALL KERNEL32!GetFileSize E ne viene calcolata la lunghezza. 0177:0040117E CMP EAX, 15 Il file è grande 15h byte? 0177:00401181 JNZ 004011B3 No (salto a errore, lo stesso errore di prima) Arrivati a questo punto, potete tranquillamente lasciare andare il SoftICE perché il file che abbiamo creato noi non è di 15h bytes! Ecco, adesso sappiamo un'altra cosa: che il programma vuole un file che sia lungo 15h byte (15h = 21). Quindi, apriamo questo file, con qualsiasi editor, e scriviamoci dentro 21 lettere a casaccio. Ribreakiamo col SICE fino ad arrivare nuovamente al punto di prima. Adesso il programma non salterà più ad errore perché il nostro file è di 21 byte. Analizziamo quindi che cosa avviene dopo: 0177:00401183 PUSH 00 0177:00401185 PUSH 004023BA 0177:0040118A PUSH 15 Prende i primi 15h caratteri del contenuto del file 0177:0040118C PUSH 00402378 0177:00401191 PUSH DWORD PTR [0040248B] 0177:00401197 CALL KERNEL32!ReadFile e li mette in 00402378 0177:0040119C PUSH DWORD PTR [0040248B] 0177:004011A2 CALL KERNEL32!CloseHandle Chiude l'handle del file Da quanto abbiamo visto, possiamo capire che dobbiamo inserire un codice valido all'interno del file YA.DO e che deve essere lungo 21 caratteri. Adesso non dobbiamo fare altro che intercettare il codice finale. Per fare questo, ci vuole un po' di pazienza in quanto il nostro Yado, quel giorno, non aveva proprio niente di più divertente da fare, quindi, si è divertito (come spiegherò di seguito) con un po' di xoring! :D Prima di analizzare il codice successivo, devo chiarire qualcosina in più sullo xoring. Mettiamo in caso di dover fare uno xoring tra la lettera A (65) e la lettera C (67). Lo xoring è un'operazione logica con due parametri: restituisce 0 se i due parametri sono uguali, mentre, restituisce 1 se i due parametri sono differenti tra loro. Vediamo quindi cosa esce xorando la A con la C. La A è uguale al valore 65, mentre la C è uguale al valore 67. Quindi deriva:

   A xor C = ?
   65 xor 67 = ?
A 1000001 xor C 1000011 = --------------  ? 0000010

Quindi, A xor C è uguale al binario 0000010 che è uguale a 2. Questo risultato lo chiamiamo R (da qui R = 2). Tutta 'sta storia a che serve? Beh... a livello pratico, non serve a niente, ma a livello teorico, ci permetterà di risalire al nostro codice. Non capite? Beh... è normale... ancora non abbiamo finito (voi dite, ma questo è pazzo! Sta dando i numeri! :)! Da questo, possiamo facilmente capire che lo xoring è un'operazione simmetrica. Questo significa che se A xor C = R allora A xor R = C. In poche parole, ottenuta la terza cifra, con una formula inversa, posso riottenere la prima! Infatti abbiamo:

   A     1000001 xor
   R     0000010 =
       --------------
   ?     1000011

Come vedete, xorando la precedente A con il valore R precedentemente ottenuto, ci siamo ricavati C, il secondo parametro precedentemente utilizzato. Dopo questo spezzone di matematica, ritorniamo al nostro Krypton. Nel file che abbiamo creato, dobbiamo scrivere un codice di 21 caratteri che ci servirà per essere identificato durante l'operazione di debugging. Il codice che ci inserisco io è "La fine del krypton!!", esattamente 21 caratteri! Salvo il file e ritorno al proggyno. L'ultima istruzione che abbiamo visto è la CloseHandle, quindi, per riprendere da dove eravamo rimasti, breakiamo su CloseHandle e premiamo F11 per entrare nella procedura pricipale. Adesso dovete fare un lavoro di ricerca: steppando sempre col SiftICE, dovete andare a vedere dove viene messo il codice che abbiamo inserito nel file. Dovete steppare fino a quando, il nostro codice, non viene usato per qualcosa. Steppa qua e steppa là, arriviamo in un punto in cui, il codice, viene puntato da EAX (ci troviamo esattamente in 00401414). A questo punto ci tocca analizzare il codice assembly: 0177:00401414 MOV EAX, 00402378 EAX punta al nostro codice 0177:00401419 MOV EBX, 004023CE EBX punta ad una stringa sconosciuta 0177:0040141E MOV ECX, 0040234C ECX punta ad un'altra stringa sconosciuta Fin qua è chiaro! Abbiamo EAX che punta proprio al nostro codice, mentre EBX ed ECX che puntano ad altre stringhe. In pratica noi dobbiamo stare attenti ai movimenti di EAX (e derivati) e dell'offset 00402378. Una volta chiarito questo, andiamo a vedere che succede dopo (commento solo le prime due parti perché le successive sono identiche e si ripetono per tutti i caratteri del codice, quini per 21 volte :): 0177:00401423 MOV CL, [ECX-01] CL assume un valore 0177:00401426 SUB CL, 10 CL = CL - 10h 0177:00401429 MOV CH, [EBX] CH assume un altro valore 0177:0040142B XOR CH, CL CH = CH xor CL 0177:0040142D MOV DH, [EAX] DH assume l'EAXesimo carattere del nostro codice 0177:0040142F ADD CL, 0D CL assume un altro valore 0177:00401432 XOR DH, CL DH = DH xor CL 0177:00401434 CMP CH, DH Il carattere è valido? 0177:00401436 JNZ 00401797 No (salta ad errore)

0177:0040143C MOV DL, [004022B1] Si (continua) 0177:00401442 XOR DL, DH 0177:00401444 MOV [004022E8], DL 0177:0040144A ADD EAX, 01 Incrementa i puntatori al codice 0177:0040144D ADD EBX, 01 e alla stringa sconosciuta

Continua per 21 volte Vediamo che cosa abbiamo fatto. Se osserviamo bene col debugger, arriviamo a 0040142F in cui CL assume un valore. Facendo ? CL notiamo che è uguale a 96. Andando avanti, possiamo notare che CL è sempre uguale a 96. Arriviamo poi in 00401432, dove DH (cioè uno dei caratteri del nostro codice) viene xorato con CL (sarebbe 96). In poche parole, il carattere valido dovrebbe essere DH xor 96, e questo, nel passaggio successivo, dovrebbe essere uguale a CH. In poche parole noi abbiamo:
CL = 96
CH cambia sempre (ma abbiamo anche quello)
I caratteri del nostro codice.

Quindi, per ottenere il codice finale, non dobbiamo fare altro che applicare la formula inversa dello xoring, di cui abbiamo parlato prima: Chiamiamo CV il carattere valido, CS il carattere sbagliato e CL rimane CL. Abbiamo quindi:

   CV = CS xor CL
   CV = CS xor 96

In poche parole, non dobbiamo far altro che prendere tutti e 21 i valori che CH assume man mano che passa l'algoritmo e xorarli con 96 per ottenere il carattere valido. Per ottenere tutti i valori senza saltare ad errore, basta usare il comando r per modificare il flag Zero del jump (digitare r fl z ogni volta che si incontra un salto ad errore) In questo modo, in circa 45 secondi, possiamo ricavarci il codice valido in assembly in questo modo: MyCode db 21 dup (?) è il mio codice Values db 87, 114, ecc... sono i valori che CH assume ogni volta RegCode db 21 dup (?) la variabile che conterrà il giusto codice

xor ecx, ecx

@@STARTLOOP: cmp ecx, 22 jz @@ENDLOOP mov al, byte ptr [Values+ecx] xor al, 96 mov byte ptr [RegCode+ecx], al inc ecx jmp @@STARTLOOP @@ENDLOOP: push offset RegCode push hEdit1 call SetWindowText ret Dove hEdit1 è l'handle di un componente Edit in cui verrà scritto il codice valido. I valori che CH assume sono 87, 114, 105, 116, 101, 89, 97, 100, 111, 89, 97, 100, 111, 64, 104, 111, 116, 109, 97, 105, 108, 46, 99, 111, 109. Xorando ognuno di questi valori con 96 si ottiene il codice finale ;) Forse come metodo è un po' assurdo, ma ne abbiamo approfittato per fare un po' di matematica e abbiamo reversato un crackme molto bello (almeno, io mi son divertito molto :). Ahhh se tutti i programmi fossero come questo! ;)


Note Finali

Spero di esser stato chiaro nella spiegazione! ;)


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.