Steganografia v GIF

Úvod

Zdravím.
Nedávno, keď som študoval na univerzite, mala som prácu na predmet „Programové metódy ochrany informácií“. Úlohou bolo vytvoriť program, ktorý vkladá správu do súborov vo formáte GIF. Rozhodol som sa to urobiť na Jave.

V tomto článku opíšem niektoré teoretické aspekty a tiež to, ako bola táto malá aplikácia vytvorená.

Teoretická časť

Formát GIF

GIF (angl. Graphics Interchange Format) — formát na výmenu obrázkov, ktorý dokáže ukladať komprimované dáta bez straty kvality vo formáte až do 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 formát upravený (GIF89a), bolo pridané nastavenie transparentnosti a animácie.

Súbory formátu GIF majú blokovú štruktúru. Tieto bloky majú vždy pevnú dĺžku (alebo sa táto dĺžka mení v závislosti od určitých príznakov), takže chybiť sa v tom, kde sa ktorý blok nachádza, je takmer nemožné. Štruktúra najjednoduchšieho neanimovaného GIF obrázku formátu GIF89a:

Steganografia v GIF

Z všetkých blokov štruktúry nás v tomto prípade budú zaujímať blok globálnej palety a parametre, ktoré sú zodpovedné za paletu:

  • CT — existencia globálnej palety. Ak je tento príznak nastavený, hneď po popise logického obrazovky by mala začínať globálna paleta.
  • Veľkosť — veľkosť palety a počet farieb obrázka. Hodnoty tohto parametra:

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

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

Ako metódy na šifrovanie správ v obrázkových súboroch sa budú používať:

  • Metóda LSB (Least Significant Bit, najmenej významný bit)
  • Metóda doplnenia palety

Metóda LSB — bežná metóda steganografie. Spočíva v nahradení posledných významných bitov v kontajneri (v našom prípade bajty globálnej palety) bitmi skrytej správy.

V programe sa v rámci tejto metódy použijú posledné dva bity v bajtoch globálnej palety. To znamená, že pre 24-bitový obrázok, kde farba palety predstavuje tri bajty pre červenú, modrú a zelenú farbu, po implementácii správy sa každá zložka farby zmení maximálne o 3/255 odtieňov. Takáto zmena bude, po prvé, pre ľudské oko nepostrehnuteľná alebo ťažko postrehnuteľná, a po druhé, nebude rozlíšiteľná na nízkokvalitných zariadeniach na zobrazovanie informácií.

Množstvo informácií bude priamo závisieť od veľkosti palety obrázku. Keďže maximálna veľkosť palety je 256 farieb a ak sa zaznamenáva po dva bity správy do zložky každej farby, maximálna dĺžka správy (pri maximálnej palete v obrázku) predstavuje 192 bajtov. Po implementácii správy do obrázku sa veľkosť súboru nezmení.

Metóda rozšírenia palety, fungujúca iba pre štruktúru GIF. Bude najefektívnejšia v obrázkoch s malou paletou. Jej podstatou je, že zvyšuje veľkosť palety, čím poskytuje viac priestoru na zaznamenanie požadovaných bajtov na miestach bajtov farieb. Ak zoberieme do úvahy, že minimálna veľkosť palety je 2 farby (6 bajtov), maximálna veľkosť implementovanej správy môže byť 256×3–6=762 bajtov. Nevýhodou je nízka kryptografia, implementovanú správu je možné prečítať pomocou akéhokoľvek textového editora, pokiaľ správa nebola podrobená ďalšiemu šifrovaniu.

Praktická časť

Návrh programu

Všetky potrebné nástroje na realizáciu algoritmov šifrovania a dešifrovania budú nachádzať sa vo balíku com.tsarik.steganography. Tento balík obsahuje rozhranie Encryptor s metódami encrypt a decrypt, triedu Binary, poskytujúcu možnosť práce s políčkom bitov, ako aj triedy výnimiek UnableToEncryptException a UnableToDecryptException, ktoré by mali byť použité v metódach rozhrania Encryptor v prípade chýb kódovania a dešifrovania.

Základný balík programu com.tsarik.programs.gifed bude obsahovať spúšťaciu triedu programu so statickou metódou main, umožňujúcou spustiť program; triedu, ktorá uchováva parametre programu; a balíky s ďalšími triedami.

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

Na základe štruktúry formátu GIF je možné zostaviť všeobecný algoritmus na vkladanie správy do palety obrázku:

Steganografia v GIF

Aby bolo možné určiť prítomnosť správy v obrázku, je potrebné na začiatok správy pridať určitú sekvenciu bitov, ktorú dekodér najprv prečíta a skontroluje jej správnosť. Ak sa neshoduje, považuje sa za to, že v obrázku nie je skrytá správa. Potom je potrebné uviesť dĺžku správy. Následne samotný text správy.

Diagram tried celej aplikácie:

Steganografia v GIF

Implementácia programu

Implementáciu celého programu je možné rozdeliť na dve časti: implementácia metód šifrovania a dešifrovania rozhrania Encryptor, v triedach GIFEncryptorByLSBMethod a GIFEncryptorByPaletteExtensionMethod, a implementácia používateľského rozhrania.

Pozrime sa na triedu GIFEncryptorByLSBMethod.

Steganografia v GIF

Polia firstLSBit a secondLSBit obsahujú čísla bitov každého bajtu obrázku, do ktorých sa má vkladať a odkiaľ sa má čítať správa. Pole checkSequence uchováva kontrolnú sekvenciu bitov na zabezpečenie rozpoznávania vloženej správy. Statická metóda getEncryptingFileParameters vracia parametre zadaného súboru a charakteristiky potenciálnej správy.

Algoritmus metódy encrypt triedy GIFEncryptorByLSBMethod:

Steganografia v 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 triedy GIFEncryptorByLSBMethod:

Steganografia v GIF

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Input file is null");
	}
	
	// čítanie bajtov z vstupného súboru
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// kontrola formátu
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Input file has wrong GIF format");
	}
	
	// čítať vlastnosť veľkosti palety z prvých troch bitov v 10. byte zo súboru
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// vypočítava počet farieb a možnú dĺžku správy
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // jeden bajt na kontrolu a jeden bajt na dĺžku správy
	
	int n = 13;
	
	// načítanie kontrolnej sekvencie
	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)");
	}
	
	// čítanie dĺžky textu
	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 + "))");
	}
	
	// čítanie bitov textu a vytvorenie bajtov textu
	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);
}

Implementácia triedy GIFEncryptorByPaletteExtensionMethod bude obdobná, iba sa líši metóda ukladajúca/čítajúca informácie.

V triede MainFrame sú opísané metódy-„obaly“: encryptImage(Encryptor encryptor) a decryptImage(Encryptor encryptor), spracovávajúce výsledky metód rozhrania Encryptor a realizujúce interakciu s používateľom, t.j. otvárajú dialóg na výber súborov, zobrazujú chybové hlásenia atď.; ako aj iné metódy: openImage(), umožňuje používateľovi vybrať obrázok, exit(), realizuje odhlásenie z aplikácie. Tieto metódy sú vyvolávané z Akciípríslušných položiek menu. V tejto triede sú navyše implementované pomocné metódy: createComponents() — vytvorenie komponentov formulára, loadImageFile(File f) — načítanie obrázka do špeciálnej komponenty zo súboru. Implementácia triedy GIFEncryptorByPaletteExtensionMethod je obdobná implementácii triedy GIFEncryptorByLSBMethod, hlavný rozdiel spočíva v spôsobe zapisovania a čítania bajtov správy z palety.

Práca programu

Metóda LBS

Predpokladajme, že existuje taký obrázok:

Steganografia v GIF

Na tomto obrázku paleta pozostáva zo 256 farieb (tak to ukladá Paint). Prvé štyri farby: biela, čierna, červená, zelená. Ostatné farby sú čierne. Sekvencia bitov globálnej palety bude nasledovná:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000…

Steganografia v GIF

Po zavedení správy budú zvýraznené bity nahradené bitmi zo správy. Získaný obrázok sa takmer nelíši od originálu.

Originál
Obrázok so zakomponovanou správou

Steganografia v GIF
Steganografia v GIF

Metóda rozšírenia palety

Po otvorení obrázku, do ktorého bola správa vložená týmto spôsobom, môžete objaviť takýto obraz:

Steganografia v GIF

Je zrejmé, že pre plnohodnotnú špionážnu činnosť takáto metóda nepostačuje a vyžaduje ďalšie šifrovanie správy.

Šifrovanie/dešifrovanie v animovaných obrázkoch funguje rovnako ako v obyčajných statických obrázkoch, pričom animácia nie je narušená.

Použité zdroje:

Stiahnuť:

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster