<----------------------------------------------------------------------------> <- __ ___ _ ----->>---- Nascondere dati in un file JPG ----<<-----> <- / \ | \ | | /\ ------------------------------------------------------> <- / /\ \| |\ \| |/ / --- Autore: --------------- ORK ----------------------> <- / /__\ \ /| / ----Contatti: ------------- orkmail@katamail.com -----> <- \______/ |\ \| | \ ------------------------------------------------------> <---------|_| \_\_|\_\ ------------------------------------------------------> <----------------------------------------------------------------------------> Indice: 0 - Introduzione 1 - Il formato di un file JPG 2 - L'idea 3 - L'implementazione 4 - I possibili impieghi 5 - Riferimenti 0 - Introduzione ---------------- Leggendo l'articolo di valv{0} sul numero 9 di BFi mi e' tornata in mente un'idea che mi era passata per la testa un po' di tempo fa. Fondamentalmente il problema e' sempre quello: come comunicare dati a qualcuno in modo insospettabile e con il minimo rischio di essere scoperti??? L'idea ce l'ha data valv{0} nel suo articolo (in realta' io ci stavo pensando da un po' di tempo), ovvero nascondere i dati all'interno di qualcosa di insospettabile... e cosa puo' essere piu' insospettabile delle foto della cresima di vostro fratello, scattate con la vostra macchina digitale??? Come avrete capito, quello che sto proponendo e' inserire dei dati all'interno di files di immagini, in particolare all'interno di files JPG. La scelta di usare i files JPG per nascondere dei dati non e' stata presa casualmente. I files JPG negli ultimi anni hanno visto una diffusione altissima, infatti li si puo' trovare dovunque, partendo dalla maggior parte dei siti web, per arrivare agli album fotografici digitali. Sicuramente se provate a lanciare il seguente comando $ find / -name *.jpg nei vostri computer di casa salteranno fuori diverse centinaia di files (meglio sorvolare su che immagini sono :) Percio' per il principio che le cose si nascondono meglio nella confusione e all'interno di altre cose che non attirano l'attenzione i files JPG mi sono sembrati la scelta migliore. Come ha detto valv{0} nel suo articolo, comunque, la maggior parte dei formati complessi puo' essere usato come cavallo di Troia :) 1 - Il formato di un file JPG ----------------------------- I files JPG sono formati da un certo numero di segmenti, ognuno dei quali puo' essere lungo al massimo 65535 (2^16) Byte ed inizia con un marcatore. Ogni marcatore e' formato da 2 Byte, il primo sempre con valore 0xFF ed il secondo con un valore compreso tra 0x01 e 0xFE. Il secondo byte specifica il tipo di marcatore. Se il segmento contiene dei dati i 2 Byte che seguono il marcatore indicano la grandezza dei dati. Se, invece, il segmento non contiene dati dopo il marcatore comincia subito il segmento successivo. Ogni segmento percio' ha la seguente struttura: [FFxx][nnnn][dati] ^ ^ ^ | | Dati (nnnn Byte - 2) ) Parti | Intero contenente la lunghezza dei dati + 2 (2 Byte) ) Facoltative Marcatore (2 Byte) Si noti che il numero contenente la grandezza dai dati e' memorizzato con la notazione Bin Endian, e non Little Endian come si e' abituati nei sistemi x86. Tutti i files JPG iniziano con un segmento senza dati identificato dal marcatore FFD8 e terminano con un segmento sempre senza dati identificato dal marcatore FFD9. Tra questi 2 segmenti si possono trovare un numero qualsiasi di altri segmenti. Per capire meglio si prenda in esempio la seguente rappresentazione esadecimale (parziale) di un file JPG. Come si puo' vedere i primi 2 byte sono esattamente il marcatore di inizio immagine FFD8. Siccome questo marcatore non prevede dati aggiuntivi per il segmento i seguenti 2 Byte dovranno essere per forza il marcatore di un nuovo segmento. Ed infatti e' proprio cosi', il segmento seguente e' identificato dal marcatore FFE0. Siccome questo marcatore prevede dati, i seguenti 2 Byte identificano la grandezza della parte dati del segmento. A questo punto per sapere a che indirizzo inizia il segmento successivo basta sommare all'indirizzo del Byte successivo al marcatore la grandezza della parte dati, quindi 0x00000004 + 0x0010 = 0x00000014. A questo indirizzo infatti si trova proprio FFED che e' un marcatore valido. Gli ultimi 2 Byte del file sono FFD9 ovvero il marcatore che identifica la fine dell'immagine. 00000000 FFD8 FFE0 0010 4A46 4946 0001 0201 012C ......JFIF....., 00000010 012C 0000 FFED 00AA 5068 6F74 6F73 686F .,......Photosho 00000020 7020 332E 3000 3842 494D 03ED 0000 0000 p 3.0.8BIM...... 00000030 0010 012B FFD9 0002 0002 012B FFD9 0002 ...+.......+.... 00000040 0002 3842 494D 03F3 0000 0000 0008 0000 ..8BIM.......... 00000050 0000 0000 0000 3842 494D 2710 0000 0000 ......8BIM'..... 00000060 000A 0001 0000 0000 0000 0002 3842 494D ............8BIM 00000070 03F4 0000 0000 0012 0035 0000 0001 002D .........5.....- ... ... ... 00006780 E8DF 87EC D37C 8B6B 5F6F E1EB 5DF1 4D07 .....|.k_o..].M. 00006790 D7F5 053A 573A 469D CFD0 4AF5 A60D DF3F ...:W:F...J....? 000067A0 FFD9 .. In realta' le cose sono un po' piu' complicate, ma per la comprensione e la realizzazione di quello che voglio proporre non serve sapere di piu'. 2 - L'idea ---------- L'idea che mi e' venuta per nascondere i dati e' molto semplice. Ogni immagine comincia con il marcatore FFD8 e termina con il marcatore FFD9. Tutti gli eventuali dati che seguono quest'ultimo marcatore vengono ignorati dai browser o dai programmi di gestione di immagini in quanto l'immagine e' gia' terminata. Quindi un ottimo posto dove nascondere i dati e' proprio la parte finale del file. Per provare che il tutto non crea problemi ai programmi di gestione di immagini provate a lanciare un comando del genere $cat trash >> image.jpg e poi provate ad aprire l'immagine. Non noterete niente di strano. Se al posto di accodare trash si accodasse un file contenente dati significativi (ovviamente criptati... la prudenza non e' mai troppa) si otterrebbe un file JPG perfettamente funzionante, contenente i nostri dati. Per la maggior parte della gente questa sembrera' un'immagine normalissima, mentre per quelli che invece conoscono il segreto questa immagine acquistera' un valore particolare. Di seguito riporto i listati di due programmini pseudo-idioti che si occupano rispettivamente di criptare e fondere un file contenente dei dati con un file JPG e di estrarre e decriptare i dati precedentemente inseriti. Breve descrizione di JPG-Fusion.c Richiede in input 3 nomi di files: il nome del file JPG, il nome del file contenente i dati da nascondere e il nome di file che verra' creato. Il programma banalmente copia il contenuto del file JPG nel nuovo file, cripta il file di dati e lo accoda sempre al nuovo file creato. Come algoritmo di criptazione e' stato usato un algoritmo a chiave simmetrica. La chiave di 16 Byte viene creata prendendo 16 Byte casuali del file JPG. I dati vengono criptati facendo lo XOR tra essi e la chiave. Viene sfruttato il fatto che A XOR B = C e C XOR B = A. Il programma restituisce 2 numeri che sono necessari a JPG-Split.c per recuperare i dati. Il primo numero non e' altro che la grandezza del file JPG, sapendo questo ovviamente si sa dove comincia il file dati. Il secondo numero invece e' il numero di Byte del file JPG da cui si comincia a prelevare la chiave per criptare. Possibili Miglioramenti: - Sarebbe possibile senza troppi sforzi inserire anche il nome del file di dati all'interno del file di Output. Per il momento il nome del file viene perso, lo si deve infatti fornire in input a JPG-Split. - Volendo si potrebbe cambiare algoritmo di criptazione in uno a chiave pubblica e privata. - Si potrebbe aggiungere alla fine del file creato il marcatore di fine immagine (FFD9) cosi' che il file possa sembrare corretto ad un'analisi superficiale. Breve descrizione di JPG-Split.c Richiede in input 4 parametri: il nome del file da cui prelevare i dati, il nome del file da creare, la lunghezza del file JPG e il numero del Byte da cui iniziare a creare la chiave per decriptare i dati. Il programma banalmente estrae i dati, li decripta e li salva sul file. Possibili Miglioramenti: - Sarebbe possibile trovare in maniera automatica la lunghezza del file JPG in base alla posizione del marcatore FFD9. 3 - L'implementazione --------------------- <-|jpg/JPG-Fusion.c |-> /* <----------------------------------------------------------------------------> <- __ ___ _ ----->>---- JPG-Fusion.c ----<<---> <- / \ | \ | | /\ --- -> <- / /\ \| |\ \| |/ / --- Sorgente allegato all'articolo -> <- / /__\ \ /| / --- "Nascondere dati in un file JPG" -> <- \______/ |\ \| | \ --- -> <- |_| \_\_|\_\ ----------------------------------------------------> <----------------------------------------------------------------------------> */ #include #include void cript (char *, char *, int); main(int argc, char *argv[]) { FILE *file1, *file2, *file3; char *tutto1, *tutto2, pad[16]; long file1lung, file2lung; int startpad; time_t secondi; if (argc<4) { printf("USO: %s file1 file2 file3:\n",argv[0]); printf("file1 - file JPG\n"); printf("file2 - file da nascondere\n"); printf("file3 - nome del file di Output\n"); exit(-1); } file1 = fopen(argv[1], "r"); if (file1 == NULL) { printf("Errore di apertura nel file %s\n",argv[1]); exit(-1); } file2 = fopen(argv[2], "r"); if (file2 == NULL) { printf("Errore di apertura nel file %s\n",argv[2]); exit(-1); } file3 = fopen(argv[3], "w"); if (file3 == NULL) { printf("Errore di apertura nel file %s\n",argv[3]); exit(-1); } /* Trova la lunghezza dei 2 files */ fseek(file1, 0, SEEK_END); file1lung = ftell(file1); rewind(file1); fseek(file2, 0, SEEK_END); file2lung = ftell(file2); rewind(file2); /* Legge il contenuto dei 2 files */ tutto1 = (char *) malloc(file1lung); fread(tutto1, 1, file1lung, file1); tutto2 = (char *) malloc(file2lung); fread(tutto2, 1, file2lung, file2); /* Crea la chiave che servira' per criptare i dati */ secondi = time(NULL); srand(secondi); startpad = rand(); startpad= startpad%(file1lung-16); strncpy(pad, tutto1+startpad, 16); /* Cripta i dati */ cript(tutto2, pad, file2lung); /* Crea il file scrivendo prima il file JPG e poi i dati criptati */ fwrite (tutto1, 1, file1lung, file3); fwrite (tutto2, 1, file2lung, file3); printf("Dati necessari per estrarre i dati:\n\nByte: %d\nPad : %d\n", file1lung, startpad); fclose(file1); fclose(file2); fclose(file3); } void cript (char *buff, char *pad, int lung) { int h; for (h=0;h <-| jpg/JPG-Split.c |-> /* <----------------------------------------------------------------------------> <- __ ___ _ ----->>---- JPG-Split.c ----<<---> <- / \ | \ | | /\ --- -> <- / /\ \| |\ \| |/ / --- Sorgente allegato all'articolo -> <- / /__\ \ /| / --- "Nascondere dati in un file JPG" -> <- \______/ |\ \| | \ --- -> <- |_| \_\_|\_\ ----------------------------------------------------> <----------------------------------------------------------------------------> */ #include void decript (char *, char *, int); main(int argc, char *argv[]) { FILE *file1, *file2; char *tutto, pad[16]; long file1lung, file2lung, file3lung; if (argc<5) { printf("USO: %s file1 file2 num:\n",argv[0]); printf("file1 - file JPG\n"); printf("file2 - nome del file di Output\n"); printf("Bytes - numero di byte dell'immagine originale\n"); printf("Pad - numero iniziale del pad\n"); exit(-1); } file1 = fopen(argv[1], "r"); if (file1 == NULL) { printf("Errore di apertura nel file %s\n",argv[1]); exit(-1); } file2 = fopen(argv[2], "w"); if (file2 == NULL) { printf("Errore di apertura nel file %s\n",argv[2]); exit(-1); } /* Trova la lunghezza dei 3 files */ fseek(file1, 0, SEEK_END); file1lung = ftell(file1); file2lung = atoi(argv[3]); file3lung = file1lung-file2lung; if (file2lung>file1lung) { printf("Parametro \"Bytes\" sbagliato\n"); exit(-1); } /* Legge solo la parte dei dati criptati */ rewind(file1); fseek(file1, file2lung, SEEK_CUR); tutto = (char *) malloc(file3lung); fread(tutto, 1, file3lung, file1); /* Crea la chiave che servira' per decriptare i dati */ rewind(file1); fseek(file1, atoi(argv[4]), SEEK_CUR); fread(pad, 1, 16, file1); /* Decripta i dati */ decript(tutto, pad, file3lung); /* Salva i dati sul file */ fwrite (tutto, 1, file3lung, file2); fclose(file1); fclose(file2); } void decript (char *buff, char *pad, int lung) { int h; for (h=0;h 4 - I possibili impieghi ------------------------ Gli impieghi possibili di questa tecnica sono limitati solamente dalla fantasia di chi ne fa uso. Alcuni suggerimenti possono essere i seguenti. Immaginate di avere la necessita' di rendere disponibili dei dati attraverso Internet, in modo da poterli recuperare dovunque vi troviate. Questi dati pero' sono altamente confidenziali e non volete che altre persone, oltre a voi e ai vostri fidatissimi amici, ne vengano in possesso. Quale miglior nascondiglio potreste trovare rispetto alla sezione "Foto della gita in montagna" della vostra Home Page??? Sicuramente questa sara' la sezione piu' noiosa e meno visitata del vostro sito e sicuramente nessuno pensera' che una delle 100 foto di voi con la vostra ragazza che camminate in mezzo alle pecore contenga una sorpresina :) Immaginate oppure di dover far avere delle informazioni private ad un vostro amico attraverso un supporto fisico (tipo un CD) e sapete che nel tragitto probabilmente passera' per mani indiscrete. Cosa c'e' meglio di una bella directory con un migliaio di foto XXX??? Altra applicazione possibile si puo' avere nel caso in cui si abbia la sensazione di avere la casella di posta elettronica controllata. Invece di cambiare indirizzo e-mail o cominciare a criptare le mail (cose che darebbero nell'occhio) basta accordarsi con l'interlocutore per lo scrivere cose semi-inutili nel corpo della mail e allegare un'immagine che in realta' avra' lo scopo di contenere le informazioni importanti. Chiaramente questa tecnica ha anche pesanti limiti, non e' possibili infatti distribuire grosse quantita' di dati. Sicuramente uno che si trova di fronte ad un file JPG di 50 MB si insospettisce come minimo. 5 - Riferimenti --------------- [1] - "CRYX's note about the JPEG decoding algorithm" Writed by Cristi Cuturicu (cccrx@kermit.cs.pub.ro) <----------------------------------------------------------------------------> <------------------- ** Information wants to be Free !! ** ------------------> <----------------------------------------------------------------------------> <------------------------------------------------------------------ By ORK --> <---------------------------------------------------------------------------->