Corso UIC Newbies 05 Guancy
From UIC
Totally Newbies 5°
Contents |
| Infos | |
|---|---|
| Author: | Guancy |
| Email: | ilm_sat@yahoo.it |
| Website: | *** |
| Date: | 28/09/2006 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Corso n° 5 per totali NewBies |
Introduction
This is lesson number 5 of NewBies course.
We will analyse the code to implement a keygen.
Tools
Link e Riferimenti
Questo è la soluzione del Corso UIC Newbies n°05 disponibile alla pagina Corso UIC Newbies 05
Essay
Avviando il programma ci appaiono due caselle di testo nelle quali viene 'richiesto un nome ed un codice seriale.
Io inserisco i miei soliti dati:
Nome: Guancy
Seriale: 666111666
clicco su "Check" e mi appare una brutta MessageBox con il fatidico testo "Serial errato. Lavora ancora".
Carichiamo il programma con OllyDbg (d'ora in poi Olly) e diamo subito uno sguardo alle chiamate intermodulari con "Search for --> Name (label) in current module" e ci accorgiamo con dolore che sono chiamate MFC42.
Cerchiamo allora la stringa della MessageBox precedente con il comando "Search for --> All referenced text strings".
Una volta arrivati all'indirizzo di chiamata dell'API diamo uno sguardo al codice precedente e vedremo che la soluzione sta tutta li:
4017BB CALL <JMP.&MFC42.#3098>//chiamata all'API GetDlgItemTextA (lettura Nome Utente)
4017C0 LEA ECX,[LOCAL.52]//salva in EAX il numero di caratteri del Nome Utente
4017C6 PUSH 1E//massima lunghezza del seriale (1E=30)
4017C8 PUSH ECX//carica indirizzo buffer ove salvare il seriale letto
4017C9 PUSH 3EA//identificatore del controllo
4017CE MOV ECX,ESI//handle della casella di testo
4017D0 CALL <JMP.&MFC42.#3098>//chiamata all'API GetDlgItemTexA (lettura Seriale)
4017D5 XOR ESI,ESI//azzera ESI
4017D7 XOR EDI,EDI//azzera EDI
//----------------------> INIZIO CICLO DI CODIFICA DEL SERIALE <------------------------
4017D9 MOV AL,SS:[EBP+ESI-88]//sposta in AL il byte puntato (i-esimo char del Nome)
4017E0 CMP AL,0//verifica se e' nullo
4017E2 JE SHORT 0040184A//se e' nullo salta a 0040184A
4017E4 MOV CL,SS:[EBP+ESI-40]//sposta in CL il byte puntato da EBP+ESI-40
4017EB XOR AL,CL//esegui lo xor del byte caricato con il char i-esimo del nome
4017ED MOV CL,SS:[EBP+ESI-20]//sposta in CL il byte puntato da EBP+ESI-20
4017F4 XOR AL,CL//esegui lo xor del byte caricato con il char i-esimo del nome
4017F6 MOV CL,SS:[EBP+ESI-60]//sposta in CL il byte puntato da EBP+ESI-60
4017FD XOR AL,CL//esegui lo xor del byte caricato con il char i-esimo del nome
4017FF MOV BL,SS:[EBP+EDI-D0]//sposta in BL il char i-esimo del seriale
401806 SUB BL,30//sottrai 30h al char(in modo da ottenere un numero)
401809 CMP BL,9//confronta il char con il valore 9
40180C JLE SHORT 00401811//se e' minore o uguale salta a 00401811
40180E SUB BL,7//togli ancora 7 (ottieni numero se char era hex [numeri+lettere A-F])
401811 MOV DL,SS:[EBP+EDI-CF]//sposta il char i-esimo+1 in DL
401818 SUB DL,30//sottrai 30h al char(in modo da ottenere un numero)
40181B CMP DL,9//confronta il char con il valore 9
40181E JLE SHORT 00401823//se e' minore o uguale salta a 00401823
401820 SUB DL,7//togli ancora 7 (ottieni numero se char era hex [numeri+lettere A-F])
401823 SHL BL,4 //shift dei bit di BL di 4 posizioni a sinistra in modo da<br>spostare il risultato a sinistra di una posizione nella word (0000000F-->000000F0)
401826 ADD DL,BL//somma BL a DL in modo tale che in DL ci sia un byte dato<br>dalla coppia di char i-esimo ed i-esimo+1
401828 INC ESI//incrementa ESI
401829 INC EDI//incrementa EDI
40182A INC EDI//incrementa EDI
40182B CMP AL,DL//confronta se il char calcolato e quello inserito sono uguali
40182D JE SHORT 004017D9//se sono uguali continua il ciclo
//--------------------> FINE CICLO DI CODIFICA DEL SERIALE <----------------------
[...]
Alla fine del ciclo ci sono altre istruzioni (che ho omesso) che effettuano poi un controllo sul fatto che il nome utente sia almeno lungo 6 caratteri e, nel qual caso non lo fosse, stampa a video un errore.
Si vede chiaramente dal codice che per generare correttamente il seriale e' necessario effettuare su OGNI char del nome 3 xor con dei valori presenti in 3 tabelle memorizzate in memoria.
Come faccio a sapere quali sono i valori delle 3 tabelle? Ma ovviamente con Olly! Basta andare all'indirizzo 4017FF e cliccare con il tasto destro del mouse su [EBP+ESI-60] e scegliere "Follow in dump --> Memory address". Appariranno in basso a destra le tre tabelle codificate in esadecimale (e con relativa traduzione in ASCII o un UNICODE [a seconda di come avete impostato Olly]).
Perche' hai scelto di partire dalla tabella il cui valore viene caricato per ultimo? Per il semplice motivo che le 3 tabelle sono tutte di lunghezza 20h (=32) byte e partono proprio da EBP+ESI-60: infatti il primo char caricato della seconda tabella e' a EBP+ESI-40 e il primo byte della prima tabella e' a EBP+ESI-20. Facendo un dump della memoria a partire da EBP+ESI-60 salvo i successivi 30 byte ed ho tutte e tre le tabelle! Comunque lo XOR e' un operatore commutativo quindi l'ordine delle operazioni non cambia il risultato.
E' da sottolineare inoltre che la lunghezza massima del nome utente che si puo' inserire e' di 32 char, allora le tabelle avranno lunghezza UTILE pari a 30 byte.
1° TABELLA = "FF FE FD FC FB FA EF EE ED EC EB EA DF DE DD DC DB DA CF CE CD CC CB CE CA BF BE BD"
2° TABELLA = "10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68"
3° TABELLA = "11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69"
Eseguendo quindi l'algoritmo sul mio nome utente otterro' questo seriale:B98A9D939982
| CHAR | OPERAZIONE | RISULTATO |
| G | ((47 XOR FF) XOR 10) XOR 11 | B9 |
| u | ((75 XOR FE) XOR 12) XOR 13 | 8A |
| a | ((61 XOR FD) XOR 14) XOR 15 | 9D |
| n | ((6E XOR FC) XOR 16) XOR 17 | 93 |
| c | ((63 XOR FB) XOR 18) XOR 19 | 99 |
| y | ((79 XOR FA) XOR 20) XOR 21 | 82 |
Scrittura di un keygen
Posto di seguito il codice di un semplice keygen che ho scritto in C.
Il keygen non esegue un controllo sul numero di caratteri inseriti (che devono essere compresi tra 6 e 30).
// PRIMA TABELLA
int tab1[30]={0x10,0x12,0x14,0x16,0x18,0x20,0x22,0x24,0x26,0x28,0x30,0x32,0x34,0x36,0x38,0x40,
0x42,0x44,0x46,0x48,0x50,0x52,0x54,0x56,0x58,0x60,0x62,0x64,0x66,0x68};
// SECONDA TABELLA
int tab2[30]={0x11,0x13,0x15,0x17,0x19,0x21,0x23,0x25,0x27,0x29,0x31,0x33,0x35,0x37,0x39,0x41,
0x43,0x45,0x47,0x49,0x51,0x53,0x55,0x57,0x59,0x61,0x63,0x65,0x67,0x69};
// TERZA TABELLA
int tab3[30]={0xFF,0xFE,0xFD,0xFC,0xFB,0xFA,0xEF,0xEE,0xED,0xEC,0xEB,0xEA,0xDF,0xDE,0xDD,0xDC,
0xDB,0xDA,0xCF,0xCE,0xCD,0xCC,0xCB,0xCE,0xCA,0xBF,0xBE,0xBD,0xBC,0xBB};
// VETTORE CHE CONTERRA' IL NOME (max 30 char)
char nome[30];
main()
{
int i,j,c; // indici tabelle
printf("Quinto corso NewBies by Andrea Geddon\n");
printf("=====================================\n\n");
printf("Inserisci il nome (ALMENO 6 CARATTERI): ");
for (i=0;(c=getchar())!='\n';i++) //fino a che il carattere non e' ritorno a capo...
nome[i]=c; //... salva i nel vettore i caratteri inseriti
printf("Il seriale e': ");
for (j=0;j<i;j++)
// calcola il seriale direttamente in fase di print
printf("%X",((nome[j]^tab1[j])^tab2[j])^tab3[j]);
printf("\n\nKeyGen by Guancy");
getch();
Note Finali
Grazie alla U.I.C. di esistere ed a Andrea Geddon per i suoi preziosi consigli :-)
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.