Steganografie v GIF

Úvod

Dobrý den.
Nedávno, když jsem studoval na univerzitě, jsem měl semestrální práci na téma „Softwarové metody ochrany informací“. Podle zadání bylo potřeba vytvořit program, který začlení zprávu do souborů formátu GIF. Rozhodl jsem se to udělat v Javě.

V tomto článku popíšu některé teoretické aspekty a také to, jak se vytvářel tento malý program.

Teoretická část

Formát GIF

GIF (angl. Graphics Interchange Format — formát pro výměnu obrázků) — formát pro ukládání grafických obrázků, schopný uchovávat komprimovaná data bez ztráty kvality v rozsahu až 256 barev. Tento formát byl vyvinut v roce 1987 (GIF87a) firmou CompuServe pro přenos rastrových obrázků po sítích. V roce 1989 byl formát modifikován (GIF89a), přidala se podpora průhlednosti a animace.

Soubory formátu GIF mají blokovou strukturu. Tyto datové bloky mají vždy pevnou délku (nebo je tato délka závislá na některých příznacích), takže je téměř nemožné udělat chybu v tom, kde se který blok nachází. Struktura nejjednoduššího neanimovaného GIF obrázku formátu GIF89a:

Steganografie v GIF

Z všech bloků struktury nás v tomto případě budou zajímat blok globální palety a parametry, které odpovídají za paletu:

  • CT. — přítomnost globální palety. Pokud je tento příznak nastaven, měla by se globální paleta objevit ihned po popisu logického obrazovky.
  • Velikost — velikost palety a počet barev obrázku. Hodnoty tohoto parametru:

Velikost
Počet barev
Velikost 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

Metody šifrování

Jako metody šifrování zpráv v obrázkových souborech budou použity:

  • Metoda LSB (Least Significant Bit, nejméně významný bit)
  • Metoda doplnění palety

Metoda LSB — rozšířená metoda steganografie. Spočívá v nahrazení posledních významných bitů v kontejneru (v našem případě bajty globální palety) bity skryté zprávy.

V rámci této metody budou použity dva poslední bity v bajtech globální palety. To znamená, že u 24bitového obrázku, kde je barva palety tvořena třemi bajty pro červenou, modrou a zelenou barvu, po vložení zprávy se každá složka barvy změní maximálně o 3/255 odstupňování. Tato změna bude jednak neviditelná nebo obtížně postřehnutelná pro lidské oko, a jednak nebude rozlišitelná na nízkokvalitních výstupních zařízeních.

Množství informací bude přímo závislé na velikosti palety obrázku. Jelikož maximální velikost palety činí 256 barev a pokud se zapisují po dva bity zprávy do složky každé barvy, maximální délka zprávy (při maximální paletě v obrázku) činí 192 bajtů. Po vložení zprávy do obrázku se velikost souboru nemění.

Metoda rozšíření palety, fungující pouze pro strukturu GIF. Bude nejúčinnější u obrázků s malou paletou. Podstatou je, že zvyšuje velikost palety, čímž poskytuje další prostor pro zápis potřebných bajtů na místo bajtů barev. Pokud vezmeme v úvahu, že minimální velikost palety činí 2 barvy (6 bajtů), maximální velikost vložené zprávy může být 256×3–6=762 bajtů. Nevýhodou je nízká úroveň kryptoprotekce, vloženou zprávu lze přečíst pomocí jakéhokoliv textového editoru, pokud nebyla podrobena dodatečnému šifrování.

Praktická část

Návrh programu

Všechny potřebné nástroje k realizaci šifrovacích a dešifrovacích algoritmů budou obsahovány v balíčku com.tsarik.steganography. Tento balíček obsahuje rozhraní Encryptor s metodami šifrovat a decrypt, třídu Binary, poskytující možnost práce s poli bitů, a také třídy výjimek UnableToEncryptException a UnableToDecryptException, které by měly být použity v metodách rozhraní Encryptor v případě chyb kódování a dešifrování.

Hlavní balíček programu com.tsarik.programs.gifed bude obsahovat spouštěcí třídu programu se statickou metodou main, která umožňuje spustit program; třídu, která uchovává parametry programu; a balíčky s dalšími třídami.

Implementace samotných algoritmů bude představena v balíčku com.tsarik.programs.gifed.gif třídami GIFEncryptorByLSBMethod a GIFEncryptorByPaletteExtensionMethod. Obě tyto třídy budou implementovat rozhraní Encryptor.

Na základě struktury formátu GIF lze sestavit obecný algoritmus pro vložení zprávy do palety obrázku:

Steganografie v GIF

Pro určení přítomnosti zprávy v obrázku je nutné do začátku zprávy přidat určitou sekvenci bitů, kterou dekodér nejprve přečte a ověří její správnost. Pokud se neshoduje, považuje se, že v obrázku není skrytá zpráva. Dále je třeba uvést délku zprávy. A poté samotný text zprávy.

Diagram tříd celé aplikace:

Steganografie v GIF

Implementace programu

Implementaci celého programu lze rozdělit na dva komponenty: implementaci metod šifrování a dešifrování rozhraní Encryptor, v třídách GIFEncryptorByLSBMethod a GIFEncryptorByPaletteExtensionMethod, a implementaci uživatelského rozhraní.

Zvažme třídu GIFEncryptorByLSBMethod.

Steganografie v GIF

Pole firstLSBit a secondLSBit obsahují čísla bitů každého bajtu obrázku, do kterých by měla být zpráva zapsána a odkud má být přečtena. Pole checkSequence uchovává kontrolní sekvenci bitů pro zajištění rozpoznání vložené zprávy. Statická metoda getEncryptingFileParameters vrací parametry uvedeného souboru a charakteristiky potenciální zprávy.

Algoritmus metody šifrovat třídy GIFEncryptorByLSBMethod:

Steganografie 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("Vstupní soubor je null");
	}
	if (out == null) {
		throw new NullPointerException("Výstupní soubor je null");
	}
	if (text == null) {
		throw new NullPointerException("Text je null");
	}
	
	// přečíst bajty ze vstupního souboru
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// zkontrolovat formát
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Vstupní soubor má nesprávný GIF formát");
	}
	
	// přečíst velikost palety z prvních tří bitů v 10. bajtu ze souboru
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// vypočítat počet barev a možnou délku zprávy
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // jeden bajt pro kontrolu a jeden bajt pro délku zprávy
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Text je příliš velký");
	}
	
	int n = 13;
	
	// napsat kontrolní sekvenci
	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++;
	}
	
	// napsat délku textu
	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++;
	}
	
	// napsat zprávu
	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++;
		}
	}
	
	// napsat výstupní soubor
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Algoritmus a zdrojový kód metody decrypt třídy GIFEncryptorByLSBMethod:

Steganografie v GIF

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Vstupní soubor je null");
	}
	
	// čtení bajtů ze vstupního souboru
	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("Vstupní soubor má špatný GIF formát");
	}
	
	// čtení velikosti palety z prvních třech bitů v 10. bajtu ze souboru
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// výpočet počtu barev a možné délky zprávy
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // jeden bajt pro kontrolu a jeden bajt pro délku zprávy
	
	int n = 13;
	
	// čtení kontrolní sekvence
	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("V obrázku není zašifrovaná zpráva (Kontrolní sekvence je nesprávná)");
	}
	
	// čtení délky 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("Dekódovaná délka textu je menší než 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Není žádná zpráva (Dekódovaná délka zprávy (" + textLength + ") je menší než možná délka zprávy (" + possibleTextLength + "))");
	}
	
	// čtení bitů textu a vytváření bajtů 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);
}

Implementace třídy GIFEncryptorByPaletteExtensionMethod bude podobná, pouze se liší metodou ukládání/čtení informací.

Ve třídě MainFrame jsou popsány metody "wrapper": encryptImage(Encryptor encryptor) a decryptImage(Encryptor encryptor), které zpracovávají výsledky metod rozhraní Encryptor a zajišťují interakci s uživateli, tj. otevírají dialog pro výběr souborů, zobrazují chybové zprávy atd.; a také další metody: openImage(), která umožňuje uživateli vybrat obrázek, exit(), provádějící odhlášení z aplikace. Tyto metody jsou volány z Akce‘ov odpovídajících položek nabídky. V této třídě jsou navíc implementovány pomocné metody: createComponents() — vytvoření komponentů formuláře, loadImageFile(File f) — nahrání obrázku do specializované komponenty ze souboru. Implementace třídy GIFEncryptorByPaletteExtensionMethod je podobná implementaci třídy GIFEncryptorByLSBMethod, hlavní rozdíl spočívá ve způsobu zápisu a čtení bajtů zprávy z palety.

Fungování programu

Metoda LBS

Předpokládejme, že je zde takový obrázek:

Steganografie v GIF

Na tomto obrázku paleta obsahuje 256 barev (jak ukládá Paint). První čtyři barvy: bílá, černá, červená, zelená. Ostatní barvy jsou černé. Následnost bitů globální palety bude následující:

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

Steganografie v GIF

Po vložení zprávy budou podtržené bity nahrazeny bity ze zprávy. Získaný obrázek se od originálu téměř neliší.

Originál
Obrázek s vloženou zprávou

Steganografie v GIF
Steganografie v GIF

Metoda rozšíření palety

Při otevření obrázku, do kterého byla zpráva vložena tímto způsobem, lze spatřit následující scénu:

Steganografie v GIF

Je jasné, že pro plnohodnotnou špionážní činnost tento způsob nevyhovuje a vyžaduje možná dodatečné šifrování zprávy.

Šifrování/dešifrování v animovaných obrázcích funguje stejně jako v obyčejných statických obrázcích, přičemž animace zůstává nedotčena.

Použité zdroje:

Stáhnout:

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster