Steganografia în GIF

Introducere

Bine ai venit.
Nu cu mult timp în urmă, când studiam la universitate, exista un curs la disciplina „Metode software de securitate a informațiilor”. Misiunea ne-a cerut să creăm un program care să încorporeze un mesaj în fișiere GIF. Am decis să o fac în Java.

În acest articol voi descrie câteva puncte teoretice, precum și modul în care a fost creat acest mic program.

Partea teoretică

format GIF

GIF (Graphics Interchange Format - un format pentru schimbul de imagini) este un format pentru stocarea imaginilor grafice, capabil să stocheze date comprimate fără pierderi de calitate într-un format de până la 256 de culori. Acest format a fost dezvoltat în 1987 (GIF87a) de către CompuServe pentru transmiterea imaginilor raster prin rețele. În 1989, formatul a fost modificat (GIF89a), a fost adăugat suport pentru transparență și animație.

Fișierele GIF au o structură de bloc. Aceste blocuri au întotdeauna o lungime fixă ​​(sau depinde de unele steaguri), așa că este aproape imposibil să faci o greșeală cu privire la locul în care se află fiecare bloc. Structura celei mai simple imagini GIF neanimate în format GIF89a:

Steganografia în GIF

Dintre toate blocurile structurii, în acest caz ne vom interesa blocul paletă globală și parametrii responsabili pentru paletă:

  • CT — prezența unei palete globale. Dacă acest indicator este setat, paleta globală trebuie să înceapă imediat după mânerul logic al ecranului.
  • Size — dimensiunea paletei și numărul de culori din imagine. Valori pentru acest parametru:

Mărimea
Numărul de culori
Dimensiunea paletei, octeți

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Metode de criptare

Următoarele metode vor fi utilizate pentru a cripta mesajele din fișierele imagine:

  • Metoda LSB (Least Significant Bit).
  • Metoda de adăugare a paletei

Metoda LSB - o metodă comună de steganografie. Constă în înlocuirea ultimilor biți semnificativi din container (în cazul nostru, octeții paletei globale) cu biții mesajului ascuns.

Programul va folosi ultimii doi biți din octeții paletei globale ca parte a acestei metode. Aceasta înseamnă că pentru o imagine pe 24 de biți, în care paleta de culori este de trei octeți pentru roșu, albastru și verde, după încorporarea unui mesaj în ea, fiecare componentă de culoare se va schimba cu maximum 3/255 de gradări. O astfel de schimbare, în primul rând, va fi invizibilă sau greu de observat pentru ochiul uman și, în al doilea rând, nu va fi vizibilă pe dispozitivele de ieșire a informațiilor de calitate scăzută.

Cantitatea de informații va depinde direct de dimensiunea paletei de imagini. Deoarece dimensiunea maximă a paletei este de 256 de culori și dacă în componenta fiecărei culori sunt scriși doi biți de mesaj, atunci lungimea maximă a mesajului (cu paleta maximă din imagine) este de 192 de octeți. Odată ce mesajul este încorporat în imagine, dimensiunea fișierului nu se modifică.

Metoda de extindere a paletei, care funcționează numai pentru structura GIF. Va fi cel mai eficient pe imagini cu o paletă mică. Esența sa este că mărește dimensiunea paletei, oferind astfel spațiu suplimentar pentru scrierea octeților necesari în locul octeților de culoare. Dacă avem în vedere că dimensiunea minimă a paletei este de 2 culori (6 octeți), atunci dimensiunea maximă a mesajului încorporat poate fi de 256 × 3–6 = 762 de octeți. Dezavantajul este securitatea criptografică scăzută; mesajul încorporat poate fi citit folosind orice editor de text dacă mesajul nu a fost supus unei criptări suplimentare.

Partea practică

Proiectarea programului

Toate instrumentele necesare pentru implementarea algoritmilor de criptare și decriptare vor fi incluse în pachet com.tsarik.steganography. Acest pachet include interfața Encryptor cu metode encrypt и decrypt, Clasa Binary, care oferă posibilitatea de a lucra cu matrice de biți, precum și cu clase de excepție UnableToEncryptException и UnableToDecryptException, care ar trebui utilizat în metodele de interfață Encryptor în cazul erorilor de codificare și respectiv decodare.

Pachetul principal de programe com.tsarik.programs.gifed va include o clasă de program rulabilă cu o metodă statică main, permițându-vă să rulați programul; o clasă care stochează parametrii programului; și pachete cu alte clase.

Implementarea algoritmilor înșiși va fi prezentată în pachet com.tsarik.programs.gifed.gif clase GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Ambele clase vor implementa interfața Encryptor.

Pe baza structurii formatului GIF, puteți crea un algoritm general pentru introducerea unui mesaj în paleta de imagini:

Steganografia în GIF

Pentru a determina prezența unui mesaj într-o imagine, este necesar să adăugați o anumită secvență de biți la începutul mesajului, pe care decodorul îi citește mai întâi și verifică corectitudinea. Dacă nu se potrivește, atunci se consideră că nu există niciun mesaj ascuns în imagine. Apoi trebuie să specificați lungimea mesajului. Apoi textul mesajului în sine.

Diagrama de clasă a întregii aplicații:

Steganografia în GIF

Implementarea programului

Implementarea întregului program poate fi împărțită în două componente: implementarea metodelor de criptare a interfeței și de decriptare Encryptor, la cursuri GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, și implementarea interfeței cu utilizatorul.

Luați în considerare clasa GIFEncryptorByLSBMethod.

Steganografia în GIF

domenii firstLSBit и secondLSBit conțin numerele de biți din fiecare octet al imaginii în care ar trebui să fie introdus mesajul și de unde ar trebui să fie citit mesajul. Camp checkSequence stochează o secvență de biți de control pentru a asigura recunoașterea mesajului încorporat. Metoda statica getEncryptingFileParameters returnează parametrii fișierului specificat și caracteristicile mesajului potențial.

Algoritmul metodei encrypt clasă GIFEncryptorByLSBMethod:

Steganografia în GIF

Și codul lui:

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

Algoritmul și codul sursă al metodei decrypt clasă GIFEncryptorByLSBMethod:

Steganografia în 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);
}

Implementarea clasei GIFEncryptorByPaletteExtensionMethod va fi similar, doar metoda de salvare/citire a informațiilor este diferită.

In clasa MainFrame sunt descrise metodele de ambalare: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), procesând rezultatele metodelor de interfață Encryptor și interacțiunea cu utilizatorul, adică deschiderea unui dialog de selecție a fișierelor, afișarea mesajelor de eroare etc.; precum și alte metode: openImage(), permițând utilizatorului să selecteze o imagine, exit(), care iese din aplicație. Aceste metode sunt numite de la Actionelementele corespunzătoare din meniu. Această clasă implementează în plus metode auxiliare: createComponents() - crearea de componente de formular, loadImageFile(File f) — încărcarea unei imagini într-o componentă specială dintr-un fișier. Implementarea clasei GIFEncryptorByPaletteExtensionMethod similar cu implementarea clasei GIFEncryptorByLSBMethod, principala diferență constă în modul în care octeții de mesaje sunt scrieți și citiți din paletă.

Funcționarea programului

Metoda LBS

Să presupunem că există o imagine ca aceasta:

Steganografia în GIF

În această imagine, paleta constă din 256 de culori (cum salvează Paint). Primele patru culori sunt: ​​alb, negru, roșu, verde. Alte culori sunt negre. Secvența globală de biți a paletei va fi următoarea:

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

Steganografia în GIF

Odată ce mesajul este încorporat, biții subliniați vor fi înlocuiți cu biții din mesaj. Imaginea rezultată nu este aproape deloc diferită de cea originală.

Original
Imagine cu mesaj încorporat

Steganografia în GIF
Steganografia în GIF

Metoda de extindere a paletei

Când deschideți o imagine care conține un mesaj folosind această metodă, veți vedea următoarea imagine:

Steganografia în GIF

Este clar că această metodă nu va funcționa pentru activități de spionaj cu drepturi depline și poate necesita criptarea suplimentară a mesajului.

Criptarea/decriptarea în imaginile animate funcționează la fel ca în imaginile statice obișnuite, dar animația nu este întreruptă.

Surse utilizate:

descărcare:

Sursa: www.habr.com

Adauga un comentariu