Steganografia vo formáte GIF

Úvod

Pozdravy.
Nie je to tak dávno, keď som študoval na univerzite, bol kurz v disciplíne „Softvérové ​​metódy informačnej bezpečnosti“. Zadanie vyžadovalo, aby sme vytvorili program, ktorý vloží správu do súborov GIF. Rozhodol som sa to urobiť v Jave.

V tomto článku popíšem niektoré teoretické body, ako aj to, ako tento malý program vznikol.

Teoretická časť

vo formáte GIF

GIF (Graphics Interchange Format – formát na výmenu obrázkov) je formát na ukladanie grafických obrázkov, schopný ukladať komprimované dáta bez straty kvality vo formáte až 256 farieb. Tento formát bol vyvinutý v roku 1987 (GIF87a) spoločnosťou CompuServe na prenos rastrových obrázkov cez siete. V roku 1989 bol upravený formát (GIF89a), pridaná podpora priehľadnosti a animácie.

Súbory GIF majú blokovú štruktúru. Tieto bloky majú vždy pevnú dĺžku (alebo to závisí od niektorých príznakov), takže je takmer nemožné urobiť chybu v tom, kde sa ktorý blok nachádza. Štruktúra najjednoduchšieho neanimovaného obrázka GIF vo formáte GIF89a:

Steganografia vo formáte GIF

Zo všetkých blokov štruktúry nás v tomto prípade bude zaujímať globálny blok palety a parametre zodpovedné za paletu:

  • CT — prítomnosť globálnej palety. Ak je tento príznak nastavený, globálna paleta musí začínať hneď za logickým ovládačom obrazovky.
  • Size — veľkosť palety a počet farieb na obrázku. Hodnoty pre tento parameter:

Veľkosť
Počet farieb
Veľkosť palety, bajty

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Metódy šifrovania

Na šifrovanie správ v obrazových súboroch sa použijú nasledujúce metódy:

  • Metóda LSB (Least Significant Bit).
  • Metóda pridávania palety

LSB metóda - bežná metóda steganografie. Pozostáva z nahradenia posledných významných bitov v kontajneri (v našom prípade bajtov globálnej palety) bitmi skrytej správy.

Program použije posledné dva bity v bajtoch globálnej palety ako súčasť tejto metódy. To znamená, že pri 24-bitovom obrázku, kde je farba palety tri bajty pre červenú, modrú a zelenú, sa po vložení správy do nej každá farebná zložka zmení maximálne o 3/255 gradácie. Takáto zmena bude po prvé pre ľudské oko neviditeľná alebo ťažko postrehnuteľná a po druhé nebude viditeľná na zariadeniach na výstup informácií nízkej kvality.

Množstvo informácií bude priamo závisieť od veľkosti palety obrázkov. Keďže maximálna veľkosť palety je 256 farieb a ak sú do zložky každej farby zapísané dva bity správy, potom maximálna dĺžka správy (s maximálnou paletou na obrázku) je 192 bajtov. Po vložení správy do obrázka sa veľkosť súboru nezmení.

Metóda rozšírenia palety, ktorý funguje len pre štruktúru GIF. Najúčinnejšie to bude na obrázkoch s malou paletou. Jeho podstatou je, že zväčšuje veľkosť palety, čím poskytuje dodatočný priestor na zapisovanie potrebných bajtov na miesto farebných bajtov. Ak uvážime, že minimálna veľkosť palety sú 2 farby (6 bajtov), ​​potom maximálna veľkosť vloženej správy môže byť 256 × 3–6 = 762 bajtov. Nevýhodou je nízka kryptografická bezpečnosť, vložená správa sa dá prečítať pomocou ľubovoľného textového editora, ak správa nebola podrobená dodatočnému šifrovaniu.

Praktická časť

Dizajn programu

Všetky potrebné nástroje na implementáciu šifrovacích a dešifrovacích algoritmov budú súčasťou balenia com.tsarik.steganography. Tento balík obsahuje rozhranie Encryptor s metódami encrypt и decrypt, Trieda Binary, ktorý poskytuje možnosť pracovať s bitovými poľami, ako aj s triedami výnimiek UnableToEncryptException и UnableToDecryptException, ktoré by sa mali používať v metódach rozhrania Encryptor v prípade chýb kódovania a dekódovania.

Hlavný programový balík com.tsarik.programs.gifed bude obsahovať triedu spustiteľných programov so statickou metódou main, ktorý vám umožní spustiť program; trieda, ktorá ukladá parametre programu; a balíčky s inými triedami.

Implementácia samotných algoritmov bude prezentovaná v balíku com.tsarik.programs.gifed.gif triedy GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Obe tieto triedy budú implementovať rozhranie Encryptor.

Na základe štruktúry formátu GIF môžete vytvoriť všeobecný algoritmus na vloženie správy do palety obrázkov:

Steganografia vo formáte GIF

Na zistenie prítomnosti správy v obrázku je potrebné na začiatok správy pridať určitú postupnosť bitov, ktoré dekodér prečíta ako prvý a skontroluje správnosť. Ak sa nezhoduje, má sa za to, že na obrázku nie je žiadna skrytá správa. Ďalej musíte zadať dĺžku správy. Potom samotný text správy.

Schéma tried celej aplikácie:

Steganografia vo formáte GIF

Implementácia programu

Implementáciu celého programu možno rozdeliť na dve zložky: implementáciu šifrovania rozhrania a metód dešifrovania Encryptor, v triedach GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethoda implementáciu používateľského rozhrania.

Zvážte triedu GIFEncryptorByLSBMethod.

Steganografia vo formáte GIF

poľa firstLSBit и secondLSBit obsahujú počty bitov každého bajtu obrázku, do ktorého sa má správa zadať a odkiaľ sa má správa prečítať. Lúka checkSequence ukladá riadiacu bitovú sekvenciu na zabezpečenie rozpoznania vloženej správy. Statická metóda getEncryptingFileParameters vráti parametre zadaného súboru a charakteristiky potenciálnej správy.

Algoritmus metódy encrypt trieda GIFEncryptorByLSBMethod:

Steganografia vo formáte GIF

A jeho kód:

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

Algoritmus a zdrojový kód metódy decrypt trieda GIFEncryptorByLSBMethod:

Steganografia vo formáte 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);
}

Realizácia triedy GIFEncryptorByPaletteExtensionMethod budú podobné, len spôsob ukladania/čítania informácií je odlišný.

V triede MainFrame metódy balenia sú opísané: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), spracovanie výsledkov metód rozhrania Encryptor a interakciu s používateľom, t.j. otvorenie dialógového okna na výber súboru, zobrazenie chybových hlásení atď.; ako aj iné metódy: openImage(), čo umožňuje používateľovi vybrať obrázok, exit(), ktorý ukončí aplikáciu. Tieto metódy sa volajú z Actionzodpovedajúce položky ponuky. Táto trieda navyše implementuje pomocné metódy: createComponents() - tvorba komponentov formulára, loadImageFile(File f) — načítanie obrázka do špeciálneho komponentu zo súboru. Realizácia triedy GIFEncryptorByPaletteExtensionMethod podobne ako pri implementácii triedy GIFEncryptorByLSBMethod, hlavný rozdiel je v spôsobe, akým sa bajty správy zapisujú a čítajú z palety.

Prevádzka programu

LBS metóda

Povedzme, že existuje takýto obrázok:

Steganografia vo formáte GIF

Na tomto obrázku sa paleta skladá z 256 farieb (ako Paint ukladá). Prvé štyri farby sú: biela, čierna, červená, zelená. Ostatné farby sú čierne. Globálna bitová sekvencia palety bude nasledovná:

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

Steganografia vo formáte GIF

Akonáhle je správa vložená, podčiarknuté bity budú nahradené bitmi zo správy. Výsledný obrázok sa takmer nelíši od originálu.

Originál
Obrázok s vloženou správou

Steganografia vo formáte GIF
Steganografia vo formáte GIF

Metóda rozšírenia palety

Keď otvoríte obrázok obsahujúci správu pomocou tejto metódy, uvidíte nasledujúci obrázok:

Steganografia vo formáte GIF

Je jasné, že táto metóda nebude fungovať pre plnohodnotné špionážne aktivity a môže vyžadovať dodatočné šifrovanie správy.

Šifrovanie/dešifrovanie v animovaných obrázkoch funguje rovnako ako v bežných statických obrázkoch, ale animácia nie je prerušená.

Použité zdroje:

Stiahnutie:

Zdroj: hab.com

Pridať komentár