Steganografia in GIF

Introduzione

Benvenuti.
Non molto tempo fa, quando studiavo all'università, c'erano corsi nella disciplina "Metodi software di sicurezza delle informazioni". Il compito ci richiedeva di creare un programma che incorporasse un messaggio nei file GIF. Ho deciso di farlo in Java.

In questo articolo descriverò alcuni punti teorici, nonché come è stato creato questo piccolo programma.

La parte teorica

Formato GIF

GIF (Graphics Interchange Format - un formato per lo scambio di immagini) è un formato per la memorizzazione di immagini grafiche, in grado di memorizzare dati compressi senza perdita di qualità in un formato fino a 256 colori. Questo formato è stato sviluppato nel 1987 (GIF87a) da CompuServe per la trasmissione di immagini raster su reti. Nel 1989 il formato fu modificato (GIF89a), fu aggiunto il supporto per la trasparenza e l'animazione.

I file GIF hanno una struttura a blocchi. Questi blocchi hanno sempre una lunghezza fissa (o dipende da alcuni flag), quindi è quasi impossibile commettere errori su dove si trova ciascun blocco. La struttura dell'immagine GIF non animata più semplice nel formato GIF89a:

Steganografia in GIF

Di tutti i blocchi della struttura, in questo caso saremo interessati al blocco della tavolozza globale e ai parametri responsabili della tavolozza:

  • CT — presenza di una tavolozza globale. Se questo flag è impostato, la tavolozza globale deve iniziare immediatamente dopo l'handle logico dello schermo.
  • Size — dimensione della tavolozza e numero di colori nell'immagine. Valori per questo parametro:

Taglia
Numero di colori
Dimensioni della tavolozza, byte

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Metodi di crittografia

Verranno utilizzati i seguenti metodi per crittografare i messaggi nei file di immagine:

  • Metodo LSB (bit meno significativo).
  • Metodo di aggiunta tavolozza

Metodo LSB - un metodo comune di steganografia. Consiste nel sostituire gli ultimi bit significativi del contenitore (nel nostro caso i byte della palette globale) con i bit del messaggio nascosto.

Il programma utilizzerà gli ultimi due bit nei byte della tavolozza globale come parte di questo metodo. Ciò significa che per un'immagine a 24 bit, in cui la tavolozza dei colori è di tre byte per rosso, blu e verde, dopo aver incorporato un messaggio al suo interno, ciascun componente di colore cambierà di un massimo di 3/255 gradazioni. Tale cambiamento, in primo luogo, sarà invisibile o difficile da notare all'occhio umano e, in secondo luogo, non sarà visibile su dispositivi di output di informazioni di bassa qualità.

La quantità di informazioni dipenderà direttamente dalla dimensione della tavolozza delle immagini. Poiché la dimensione massima della tavolozza è di 256 colori e se nel componente di ciascun colore vengono scritti due bit di messaggio, la lunghezza massima del messaggio (con la tavolozza massima nell'immagine) è di 192 byte. Una volta incorporato il messaggio nell'immagine, la dimensione del file non cambia.

Metodo di espansione della tavolozza, che funziona solo per la struttura GIF. Sarà più efficace sulle immagini con una tavolozza piccola. La sua essenza è che aumenta la dimensione della tavolozza, fornendo così spazio aggiuntivo per scrivere i byte necessari al posto dei byte di colore. Se consideriamo che la dimensione minima della tavolozza è di 2 colori (6 byte), allora la dimensione massima del messaggio incorporato può essere 256 × 3–6 = 762 byte. Lo svantaggio è la bassa sicurezza crittografica; il messaggio incorporato può essere letto utilizzando qualsiasi editor di testo se il messaggio non è stato sottoposto a crittografia aggiuntiva.

Parte pratica

Progettazione del programma

Tutti gli strumenti necessari per implementare gli algoritmi di crittografia e decrittografia saranno inclusi nel pacchetto com.tsarik.steganography. Questo pacchetto include l'interfaccia Encryptor con metodi encrypt и decrypt, Classe Binary, che offre la possibilità di lavorare con matrici di bit e classi di eccezioni UnableToEncryptException и UnableToDecryptException, che dovrebbe essere utilizzato nei metodi di interfaccia Encryptor rispettivamente in caso di errori di codifica e decodifica.

Pacchetto principale del programma com.tsarik.programs.gifed includerà una classe di programma eseguibile con un metodo statico main, permettendoti di eseguire il programma; una classe che memorizza i parametri del programma; e pacchetti con altre classi.

L'implementazione degli algoritmi stessi verrà presentata nel pacchetto com.tsarik.programs.gifed.gif classi GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Entrambe queste classi implementeranno l'interfaccia Encryptor.

In base alla struttura del formato GIF, puoi creare un algoritmo generale per introdurre un messaggio nella tavolozza delle immagini:

Steganografia in GIF

Per determinare la presenza di un messaggio in un'immagine, è necessario aggiungere una certa sequenza di bit all'inizio del messaggio, che il decodificatore legge per primo e ne verifica la correttezza. Se non corrisponde, si considera che non ci sia alcun messaggio nascosto nell'immagine. Successivamente è necessario specificare la lunghezza del messaggio. Quindi il testo del messaggio stesso.

Diagramma delle classi dell'intera applicazione:

Steganografia in GIF

Attuazione del programma

L'implementazione dell'intero programma può essere divisa in due componenti: implementazione della crittografia dell'interfaccia e dei metodi di decrittografia Encryptor, nelle classi GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethode l'implementazione dell'interfaccia utente.

Considera la classe GIFEncryptorByLSBMethod.

Steganografia in GIF

Campo firstLSBit и secondLSBit contengono i numeri di bit di ciascun byte dell'immagine in cui deve essere inserito il messaggio e da dove deve essere letto il messaggio. Campo checkSequence memorizza una sequenza di bit di controllo per garantire il riconoscimento del messaggio incorporato. Metodo statico getEncryptingFileParameters restituisce i parametri del file specificato e le caratteristiche del potenziale messaggio.

Algoritmo del metodo encrypt classe GIFEncryptorByLSBMethod:

Steganografia in GIF

E il suo codice:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Input file is null");
	}
	if (out == null) {
		throw new NullPointerException("Output file is null");
	}
	if (text == null) {
		throw new NullPointerException("Text is null");
	}
	
	// read bytes from input file
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// check format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Input file has wrong GIF format");
	}
	
	// read palette size property from first three bits in the 10-th byte from the file
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculate color count and possible message length
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;// one byte for check and one byte for message length
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Text is too big");
	}
	
	int n = 13;
	
	// write check sequence
	for (int i = 0; i < checkSequence.length/2; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		ba[firstLSBit] = checkSequence[2*i];
		ba[secondLSBit] = checkSequence[2*i+1];
		bytes[n] = Binary.toByte(ba);
		n++;
	}
	
	// write text length
	byte[] cl = Binary.toBitArray((byte)text.length());
	for (int i = 0; i < cl.length/2; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		ba[firstLSBit] = cl[2*i];
		ba[secondLSBit] = cl[2*i+1];
		bytes[n] = Binary.toByte(ba);
		n++;
	}
	
	// write message
	byte[] textBytes = text.getBytes();
	for (int i = 0; i < textBytes.length; i++) {
		byte[] c = Binary.toBitArray(textBytes[i]);
		for (int ci = 0; ci < c.length/2; ci++) {
			byte[] ba = Binary.toBitArray(bytes[n]);
			ba[firstLSBit] = c[2*ci];
			ba[secondLSBit] = c[2*ci+1];
			bytes[n] = Binary.toByte(ba);
			n++;
		}
	}
	
	// write output file
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Algoritmo e codice sorgente del metodo decrypt classe GIFEncryptorByLSBMethod:

Steganografia in GIF

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Input file is null");
	}
	
	// read bytes from input file
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// check format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Input file has wrong GIF format");
	}
	
	// read palette size property from first three bits in the 10-th byte from the file
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// calculate color count and possible message length
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;	// one byte for check and one byte for message length
	
	int n = 13;
	
	// read check sequence
	byte[] csBits = new byte[checkSequence.length];
	for (int i = 0; i < 4; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		csBits[2*i] = ba[firstLSBit];
		csBits[2*i+1] = ba[secondLSBit];
		n++;
	}
	byte cs = Binary.toByte(csBits);
	
	if (cs != Binary.toByte(checkSequence)) {
		throw new UnableToDecodeException("There is no encrypted message in the image (Check sequence is incorrect)");
	}
	
	// read text length
	byte[] cl = new byte[8];
	for (int i = 0; i < 4; i++) {
		byte[] ba = Binary.toBitArray(bytes[n]);
		cl[2*i] = ba[firstLSBit];
		cl[2*i+1] = ba[secondLSBit];
		n++;
	}
	byte textLength = Binary.toByte(cl);
	
	if (textLength < 0) {
		throw new UnableToDecodeException("Decoded text length is less than 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("There is no messages (Decoded message length (" + textLength + ") is less than Possible message length (" + possibleTextLength + "))");
	}
	
	// read text bits and make text bytes
	byte[] bt = new byte[textLength];
	for (int i = 0; i < bt.length; i++) {
		byte[] bc = new byte[8];
		for (int bci = 0; bci < bc.length/2; bci++) {
			byte[] ba = Binary.toBitArray(bytes[n]);
			bc[2*bci] = ba[firstLSBit];
			bc[2*bci+1] = ba[secondLSBit];
			n++;
		}
		bt[i] = Binary.toByte(bc);
	}
	
	return new String(bt);
}

Implementazione della lezione GIFEncryptorByPaletteExtensionMethod sarà simile, solo il metodo di salvataggio/lettura delle informazioni è diverso.

In classe MainFrame i metodi wrapper sono descritti: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), elaborando i risultati dei metodi di interfaccia Encryptor e interagire con l'utente, ovvero aprire una finestra di dialogo per la selezione dei file, mostrare messaggi di errore, ecc.; così come altri metodi: openImage(), consentendo all'utente di selezionare un'immagine, exit(), che esce dall'applicazione. Questi metodi vengono chiamati da Actionle voci di menu corrispondenti. Questa classe implementa inoltre metodi ausiliari: createComponents() - creazione di componenti del modulo, loadImageFile(File f) — caricamento di un'immagine in un componente speciale da un file. Implementazione della lezione GIFEncryptorByPaletteExtensionMethod simile all'implementazione della classe GIFEncryptorByLSBMethod, la differenza principale sta nel modo in cui i byte del messaggio vengono scritti e letti dalla tavolozza.

Funzionamento del programma

Metodo LBS

Diciamo che c'è un'immagine come questa:

Steganografia in GIF

In questa immagine, la tavolozza è composta da 256 colori (come salva Paint). I primi quattro colori sono: bianco, nero, rosso, verde. Altri colori sono il nero. La sequenza di bit della tavolozza globale sarà la seguente:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000...

Steganografia in GIF

Una volta incorporato il messaggio, i bit sottolineati verranno sostituiti con i bit del messaggio. L'immagine risultante non è quasi diversa dall'originale.

originale
Immagine con messaggio incorporato

Steganografia in GIF
Steganografia in GIF

Metodo di espansione della tavolozza

Quando apri un'immagine contenente un messaggio utilizzando questo metodo, vedrai la seguente immagine:

Steganografia in GIF

È chiaro che questo metodo non funzionerà per attività di spionaggio a tutti gli effetti e potrebbe richiedere un'ulteriore crittografia del messaggio.

La crittografia/decrittografia nelle immagini animate funziona esattamente come nelle normali immagini statiche, ma l'animazione non viene interrotta.

Fonti utilizzate:

scaricare:

Fonte: habr.com

Aggiungi un commento