EffeTech HTTP sniffer reverse engineering 1°parte Analisi di algoritmi, keygenning e cracking
Questo articolo non è una guida completa al reverse engineering, è impossibile, sarà esclusivamente uno studio dell'algoritmo
di validazione del codice seriale di un determinato programma, per capirne le vulnerabilità e come è possibile aggirarle. Sono necessari alcuni requisiti preliminari per poter capire questo articolo come la conoscenza del linguaggio assembler, l'utilizzo di un debugger, una minima conscenza delle API di windows e la capacità di programmazione in linguaggi di alto livello. Ricordo comunque che violare la licenza di un programma è reato, per continuare a utilizzare software al di fuori del periodo di trial concesso occorre acquistare regolarmente una licenza.
Generalmente nello sviluppo completo di una applicazione si usano quasi interamente linguaggi di programmazione ad alto livello,
di conseguenza quindi dovrebbe essere ovvio capire che dopo la compilazione nessuno che non disponga del codice sorgente di tale
programma sarà mai più in grado di leggerne il codice ad alto livello. Quello che tutti possono fare però è, a patto di una buona
conoscenza di tale linguaggio, leggerne il codice assembly istruzione per istruzione e cercare di "risalire" manualmente ad un codice
di più alto livello meglio comprensibile. Ovviamente questo procedimento è tutt'altro che facile e immediato, in quanto a volte anche solo una istruzione di alto livello viene codificata in linguaggio macchina con decine di istruzioni assembly, e da questo si intuisce anche perchè sarebbe praticamente impossibile reversare un'intera applicazione. Quello che verrà trattato in questo articolo è l'analisi di un algoritmo di protezione di un particolare programma, capendone il funzionamento, riportandolo a codice di alto livello e studiandone le vulnerabilità. Vi pare chiaro che se dopo aver letto questo primo passo vi siete accorti che non sapete minimamente cosa sia il linguaggio assembler, un algoritmo di protezione, o non avete ancora idea di come poter leggere il codice di una applicazione compilata potete già cambiare pagina.
Il programma che verrà analizzato in questo articolo è EffeTech HTTP sniffer v 4.0.0.0, un packet sniffer locale per le richieste HTTP che ha un periodo
di trial di 15 giorni. Il programma è disponibile per il download sul o tra le. Dopo aver installato il programma e aperto la prima cosa che compare all'avvio è la segunte schermata: ![]() Cioè la richiesta dell'introduzione di un nome utente e un codice seriale per la registrazione del prodotto al di fuori del periodo di prova. La protezione che andremo a studiare in questo articolo sarà dunque questa: l'algoritmo di validazione della combinazione user/serial per la registrazione del programma (in licenza Non-Commercial e Commercial).
Il metodo più veloce per localizzare la parte di algoritmo che ci interessa all'interno delle migliaia di istruzioni in codice assembly del programma la maggior parte delle volte
è tramite l'utilizzo di un debugger. Ognuno può utilizzare il debugger che preferisce, io personalmente come la maggior parte delle persone utilizzo il
comodo e pratico . Con un minimo di conoscenza della programmazione a oggetti di windows
e delle api la prima cosa che si pensa è che il programma leggerà i dati da quelle due textbox tramite funzioni come GetDlgItemText,GetWindowText o magari addirittura SendMessage con parametro
WM_GETTEXT (volendo si potrebbe leggere la tabella delle funzioni importate, ma si fa prima a "manoni"). Il primo passo da fare quindi è lanciare il debugger e attaccare il processo EHSniffer.exe o lanciare l'applicazione da
dentro il debugger (come si preferisce) e posizionare breakpoint sulla prima di queste API (ollydbg: Alt+F1 e scrivere bp GetDlgItemTextA, A per la versione Ansi).
Il debugger non fermerà l'esecuzione del processo dopo la pressione del tasto Register, quindi riproviamo posizionando un breakpoint su GetWindowText (bp GetWiundowTextA) e noteremo che debugger fermerà l'esecuzione del processo
all'interno della user32.dll la libreria con questa api. Usciamo da tale libreria con Ctrl+F9 e ci troveremo all'indirizzo 0x004438C7 subito dopo la sua chiamata. E' plausibile pensare che ci troviamo non troppo lontani da un prossimo utilizzo dei dati che il programma sta leggendo, comuqnue ricordo che una delle parti più difficili del reversing, anche se leggendo un articolo già fatto non si nota, è trovare la posizione esatta dell'inizio e della fine dell'algoritmo da studiare, senza perdere ore di lavoro addentrandosi in sotto-sotto-...-sotto routine inutili per i nostri scopi e questo lo si impara solo provando, riprovando e facendo esperienza. Comunque quello che ci troveremo davanti se abbiamo fatto tutto bene è il seguente spezzone di codice:
Ci troviamo comunque ancora all'interno di varie sotto-call del programma, quello che dobbiamo fare e arrivare al punto giusto del codice.
Continuando il debug passo passo si uscirà da questa call (che chiamerò readRoutine) che non fa altro che chiamare le API per la lettura della
lunghezza dell'input, salvarla in memoria, leggere la stringa e salvarla in memoria il tutto due volte (per entrambe le textbox). Alla fine ci troveremo all'indirizzo 0x00415308.
Evitando di debuggare due routine inutili ed uscendo da due sotto-routine in cui siamo innestati ci troveremo
finalmente all'inizio del codice che ci interessa, ovvere all'indirizzo 0x0041548E. Adesso inizia il compito più complesso:
dobbiamo analizzare e comprendere nei minimi dettagli il codice che abbiamo davanti utilizzando solo il debbugger.
Solo l'analisi del codice assembly richiede parecchie ore di debug e di varie prove che ognuno dovrebbe provare a fare,
ma ovviamente riporto il codice già commentato e spiegato alla fine di questa analisi. Dunque il primo controllo che l'algoritmo di validazione del seriale fa è il seguente:
Dunque subito capiamo che il programma si aspetta di leggere un seriale di 0x12 (18) caratteri se no subito verrà
stampato il messaggio di errore "Wrong user or Serial Number!". Tradotto in un linguaggio
ad alto livello (C) questo controllo diventa:
A parte uno strano e inutile controllo che non si avvererà mai a metà ciclo (EDX == 0x12) il funzionamento generale
di questa parte di codice è abbastanza facile. All'uscita dal ciclo avremo salvati questi valori:
Stack [ESP+10] = serial[10]
Il che tradotto in linguaggio C diventa banalmente:
Una piccola nota: Come si vede tutto il codice esaminato era sviluppabile evitando un ciclo in quanto i vari offset controllati
(0x1 0x06 0x0E...) sono statici e non variano in base a dei calcoli. E' probabile quindi che questo spezzone di codice era stato
complicato apposta per confondere chiunque avesse cercato di reversare questo algoritmo. Fermiamoci un attimo a fare il punto della situazione: per adesso il nome inserito non è stato minimante utilizzato dal codice, ma per noi è meglio cosi, ora per poter continuare a debuggare senza visualizzare il messaggio di errore occorre costruirsi un seriale "provvisorio" che sia conforme anche a questo secondo controllo. Il primo che ho calcolato è stato: Pxxxxxxx9xAxxxxzxx, dove si può verificare che:
serial[0] = 'P' = 0x50;
Come si nota viene pushato il seriale, viene chiamata una routine e al ritorno dalla chiamata si controlla
il valore assunto da EAX, se EAX == 0 viene chiamata la solita funzione di errore, se no si prosegue con alcune istruzioni
che fanno pensare a una registrazione del seriale nel registro di sistema (proprio così infatti). Appurato ciò abbiamo due strade davanti a noi adesso: la via più semplice, cambiare con un editor esadecimale all'indirizzo 0x00415540 il JE in un JNE in modo da rendere validi tutti i seriali che rispecchino solo fino alla 2 caratteristica, o andare avanti a reversare e vedere cosa altro troveremo. Script Execution Time: 0.313165 seconds - Visite: 112832 Copyright © 2007-2009 Suondmao v0.1.5-1 |