Esteganografia en GIF

Introducció

Salutacions.
No fa molt, quan estudiava a la universitat, hi havia un curs sobre la disciplina "Mètodes de programari de seguretat de la informació". La tasca ens va obligar a crear un programa que incrusté un missatge en fitxers GIF. Vaig decidir fer-ho en Java.

En aquest article descriuré alguns punts teòrics, així com com es va crear aquest petit programa.

Part teòrica

Format GIF

GIF (Graphics Interchange Format - un format per intercanviar imatges) és un format per emmagatzemar imatges gràfiques, capaç d'emmagatzemar dades comprimides sense pèrdua de qualitat en un format de fins a 256 colors. Aquest format va ser desenvolupat l'any 1987 (GIF87a) per CompuServe per transmetre imatges ràster a través de xarxes. El 1989 es va modificar el format (GIF89a), es va afegir suport per a la transparència i l'animació.

Els fitxers GIF tenen una estructura de blocs. Aquests blocs sempre tenen una longitud fixa (o depèn d'algunes banderes), per la qual cosa és gairebé impossible cometre un error sobre on es troba cada bloc. L'estructura de la imatge GIF no animada més senzilla en format GIF89a:

Esteganografia en GIF

De tots els blocs de l'estructura, en aquest cas ens interessarà el bloc de paleta global i els paràmetres responsables de la paleta:

  • CT — presència d'una paleta global. Si s'estableix aquesta marca, la paleta global ha de començar immediatament després de la identificació de la pantalla lògica.
  • Size — mida de la paleta i nombre de colors de la imatge. Valors d'aquest paràmetre:

mida
Nombre de colors
Mida de la paleta, bytes

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Mètodes de xifratge

S'utilitzaran els mètodes següents per xifrar missatges en fitxers d'imatge:

  • Mètode LSB (Least Significant Bit).
  • Mètode d'addició de paletes

Mètode LSB - un mètode comú d'esteganografia. Consisteix a substituir els últims bits significatius del contenidor (en el nostre cas, els bytes de la paleta global) pels bits del missatge ocult.

El programa utilitzarà els dos últims bits dels bytes de la paleta global com a part d'aquest mètode. Això vol dir que per a una imatge de 24 bits, on la paleta de colors és de tres bytes per a vermell, blau i verd, després d'incrustar-hi un missatge, cada component de color canviarà en un màxim de 3/255 gradacions. Aquest canvi, en primer lloc, serà invisible o difícil de notar a l'ull humà i, en segon lloc, no serà visible en dispositius de sortida d'informació de baixa qualitat.

La quantitat d'informació dependrà directament de la mida de la paleta d'imatges. Com que la mida màxima de la paleta és de 256 colors, i si s'escriuen dos bits de missatge al component de cada color, la longitud màxima del missatge (amb la paleta màxima de la imatge) és de 192 bytes. Un cop incrustat el missatge a la imatge, la mida del fitxer no canvia.

Mètode d'expansió de la paleta, que només funciona per a l'estructura GIF. Serà més eficaç en imatges amb una paleta petita. La seva essència és que augmenta la mida de la paleta, proporcionant així espai addicional per escriure els bytes necessaris en lloc dels bytes de color. Si tenim en compte que la mida mínima de la paleta és de 2 colors (6 bytes), aleshores la mida màxima del missatge incrustat pot ser de 256 × 3–6 = 762 bytes. El desavantatge és la baixa seguretat criptogràfica; el missatge incrustat es pot llegir amb qualsevol editor de text si el missatge no ha estat sotmès a xifratge addicional.

Part pràctica

Disseny del programa

Totes les eines necessàries per implementar algorismes de xifratge i desxifrat s'inclouran al paquet com.tsarik.steganography. Aquest paquet inclou la interfície Encryptor amb mètodes encrypt и decrypt, Classe Binary, que ofereix la possibilitat de treballar amb matrius de bits, així com amb classes d'excepció UnableToEncryptException и UnableToDecryptException, que s'hauria d'utilitzar en mètodes d'interfície Encryptor en cas d'errors de codificació i descodificació respectivament.

Paquet de programa principal com.tsarik.programs.gifed inclourà una classe de programa executable amb un mètode estàtic main, que us permet executar el programa; una classe que emmagatzema els paràmetres del programa; i paquets amb altres classes.

La implementació dels propis algorismes es presentarà al paquet com.tsarik.programs.gifed.gif classes GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Ambdues classes implementaran la interfície Encryptor.

A partir de l'estructura del format GIF, podeu crear un algorisme general per introduir un missatge a la paleta d'imatges:

Esteganografia en GIF

Per determinar la presència d'un missatge en una imatge, cal afegir una determinada seqüència de bits al començament del missatge, que el descodificador llegeix primer i comprova la correcció. Si no coincideix, es considera que no hi ha cap missatge ocult a la imatge. A continuació, heu d'especificar la longitud del missatge. A continuació, el text del missatge en si.

Diagrama de classes de tota l'aplicació:

Esteganografia en GIF

Implementació del programa

La implementació de tot el programa es pot dividir en dos components: implementació de mètodes de xifratge i desxifrat de la interfície Encryptor, a les classes GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, i la implementació de la interfície d'usuari.

Considereu la classe GIFEncryptorByLSBMethod.

Esteganografia en GIF

camps firstLSBit и secondLSBit conté el nombre de bits de cada byte de la imatge en què s'ha d'introduir el missatge i des d'on s'ha de llegir el missatge. Camp checkSequence emmagatzema una seqüència de bits de verificació per garantir el reconeixement del missatge incrustat. Mètode estàtic getEncryptingFileParameters retorna els paràmetres del fitxer especificat i les característiques del missatge potencial.

Algoritme del mètode encrypt classe GIFEncryptorByLSBMethod:

Esteganografia en GIF

I el seu codi:

@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();
}

Algorisme i codi font del mètode decrypt classe GIFEncryptorByLSBMethod:

Esteganografia en 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);
}

Implementació de la classe GIFEncryptorByPaletteExtensionMethod serà similar, només el mètode de desar/llegir la informació és diferent.

A classe MainFrame Els mètodes d'embolcall es descriuen: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), processant els resultats dels mètodes d'interfície Encryptor i interactuar amb l'usuari, és a dir, obrir un diàleg de selecció de fitxers, mostrar missatges d'error, etc.; així com altres mètodes: openImage(), que permet a l'usuari seleccionar una imatge, exit(), que surt de l'aplicació. Aquests mètodes s'anomenen de Actionelements del menú corresponents. Aquesta classe també implementa mètodes auxiliars: createComponents() - Creació de components del formulari, loadImageFile(File f) — carregar una imatge en un component especial d'un fitxer. Implementació de la classe GIFEncryptorByPaletteExtensionMethod similar a la implementació de la classe GIFEncryptorByLSBMethod, la diferència principal està en la manera com s'escriuen i llegeixen els bytes del missatge des de la paleta.

Funcionament del programa

Mètode LBS

Suposem que hi ha una imatge com aquesta:

Esteganografia en GIF

En aquesta imatge, la paleta consta de 256 colors (com desa Paint). Els quatre primers colors són: blanc, negre, vermell, verd. Altres colors són negres. La seqüència global de bits de la paleta serà la següent:

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

Esteganografia en GIF

Un cop incrustat el missatge, els bits subratllats es substituiran pels bits del missatge. La imatge resultant gairebé no és diferent de l'original.

Original
Imatge amb missatge incrustat

Esteganografia en GIF
Esteganografia en GIF

Mètode d'expansió de la paleta

Quan obriu una imatge que conté un missatge amb aquest mètode, veureu la imatge següent:

Esteganografia en GIF

Està clar que aquest mètode no funcionarà per a activitats d'espionatge de ple dret i pot requerir un xifratge addicional del missatge.

El xifratge/desxifrat en imatges animades funciona igual que en imatges estàtiques normals, però l'animació no es trenca.

Fonts utilitzades:

Descàrrega:

Font: www.habr.com

Afegeix comentari