Steganografie ve formátu GIF

úvod

Vítejte u nás.
Není to tak dávno, když jsem studoval na univerzitě, existovala výuková práce v oboru „Softwarové metody informační bezpečnosti“. Zadání vyžadovalo, abychom vytvořili program, který vloží zprávu do souborů GIF. Rozhodl jsem se to udělat v Javě.

V tomto článku popíšu některé teoretické body a také to, jak tento malý program vznikl.

Teoretická část

formát GIF

GIF (Graphics Interchange Format - formát pro výměnu obrázků) je formát pro ukládání grafických obrázků, schopný ukládat komprimovaná data bez ztráty kvality ve formátu až 256 barev. Tento formát byl vyvinut v roce 1987 (GIF87a) společností CompuServe pro přenos rastrových obrázků po sítích. V roce 1989 byl upraven formát (GIF89a), přidána podpora průhlednosti a animace.

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

Steganografie ve formátu GIF

Ze všech bloků struktury nás v tomto případě bude zajímat globální blok palety a parametry zodpovědné za paletu:

  • CT — přítomnost globální palety. Je-li tento příznak nastaven, musí globální paleta začínat bezprostředně po popisovači logické obrazovky.
  • Size — velikost palety a počet barev na obrázku. Hodnoty tohoto parametru:

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

Metody šifrování

K šifrování zpráv v souborech obrázků budou použity následující metody:

  • Metoda LSB (Least Significant Bit).
  • Metoda přidávání palet

LSB metoda - běžná metoda steganografie. Spočívá v nahrazení posledních významných bitů v kontejneru (v našem případě bajtů globální palety) bity skryté zprávy.

Program jako součást této metody použije poslední dva bity v bajtech globální palety. To znamená, že u 24bitového obrázku, kde je barevná paleta tři bajty pro červenou, modrou a zelenou, se po vložení zprávy do něj každá barevná složka změní maximálně o 3/255 gradace. Taková změna bude za prvé pro lidské oko neviditelná nebo těžko postřehnutelná a za druhé nebude viditelná na zařízeních pro výstup informací nízké kvality.

Množství informací bude přímo záviset na velikosti obrazové palety. Protože maximální velikost palety je 256 barev a pokud jsou do složky každé barvy zapsány dva bity zprávy, pak maximální délka zprávy (s maximální paletou v obrázku) je 192 bajtů. Jakmile je zpráva vložena do obrázku, velikost souboru se nezmění.

Metoda rozšíření palety, který funguje pouze pro strukturu GIF. Nejúčinnější bude na snímcích s malou paletou. Jeho podstatou je, že zvětšuje velikost palety, čímž poskytuje další prostor pro zápis potřebných bajtů na místo barevných bajtů. Pokud uvážíme, že minimální velikost palety jsou 2 barvy (6 bajtů), pak maximální velikost vložené zprávy může být 256 × 3–6 = 762 bajtů. Nevýhodou je nízká kryptografická bezpečnost, vloženou zprávu lze přečíst pomocí libovolného textového editoru, pokud zpráva nebyla podrobena dodatečnému šifrování.

Praktická část

Návrh programu

Všechny potřebné nástroje pro implementaci šifrovacích a dešifrovacích algoritmů budou součástí balíčku com.tsarik.steganography. Tento balíček obsahuje rozhraní Encryptor s metodami encrypt и decrypt, Třída Binary, který poskytuje možnost pracovat s bitovými poli a také s třídami výjimek UnableToEncryptException и UnableToDecryptException, který by měl být použit v metodách rozhraní Encryptor v případě chyb kódování a dekódování.

Hlavní programový balíček com.tsarik.programs.gifed bude obsahovat třídu spustitelného programu se statickou metodou main, což vám umožní spustit program; třída, která ukládá parametry programu; a balíčky s jinými třídami.

Implementace samotných algoritmů bude prezentována v balíčku com.tsarik.programs.gifed.gif třídy GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Obě tyto třídy budou implementovat rozhraní Encryptor.

Na základě struktury formátu GIF můžete vytvořit obecný algoritmus pro vložení zprávy do palety obrázků:

Steganografie ve formátu GIF

Pro zjištění přítomnosti zprávy v obrázku je nutné na začátek zprávy přidat určitou sekvenci bitů, kterou dekodér přečte jako první a zkontroluje její správnost. Pokud se neshoduje, má se za to, že v obrázku není žádná skrytá zpráva. Dále je třeba zadat délku zprávy. Poté samotný text zprávy.

Diagram tříd celé aplikace:

Steganografie ve formátu GIF

Realizace programu

Implementaci celého programu lze rozdělit na dvě složky: implementaci šifrování rozhraní a metod dešifrování Encryptor, ve třídách GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethoda implementaci uživatelského rozhraní.

Zvažte třídu GIFEncryptorByLSBMethod.

Steganografie ve formátu GIF

pole firstLSBit и secondLSBit obsahují počty bitů každého bajtu obrázku, do kterého se má zpráva zadat a odkud se má zpráva přečíst. Pole checkSequence ukládá kontrolní bitovou sekvenci pro zajištění rozpoznání vložené zprávy. Statická metoda getEncryptingFileParameters vrátí parametry zadaného souboru a charakteristiky potenciální zprávy.

Algoritmus metody encrypt třída GIFEncryptorByLSBMethod:

Steganografie ve formátu 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 metody decrypt třída GIFEncryptorByLSBMethod:

Steganografie ve formátu 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);
}

Realizace třídy GIFEncryptorByPaletteExtensionMethod bude podobný, liší se pouze způsob ukládání/čtení informací.

Ve třídě MainFrame metody balení jsou popsány: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), zpracování výsledků metod rozhraní Encryptor a interakce s uživatelem, tj. otevření dialogu pro výběr souboru, zobrazení chybových zpráv, atd.; stejně jako další metody: openImage(), umožňující uživateli vybrat obrázek, exit(), která ukončí aplikaci. Tyto metody jsou volány z Actionodpovídající položky nabídky. Tato třída navíc implementuje pomocné metody: createComponents() - tvorba formulářových komponentů, loadImageFile(File f) — načtení obrázku do speciální komponenty ze souboru. Realizace třídy GIFEncryptorByPaletteExtensionMethod podobná implementaci třídy GIFEncryptorByLSBMethod, hlavní rozdíl je ve způsobu zápisu a čtení bajtů zpráv z palety.

Provoz programu

LBS metoda

Řekněme, že existuje obrázek jako tento:

Steganografie ve formátu GIF

Na tomto obrázku se paleta skládá z 256 barev (jak Malování ukládá). První čtyři barvy jsou: bílá, černá, červená, zelená. Ostatní barvy jsou černé. Bitová sekvence globální palety bude následující:

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

Steganografie ve formátu GIF

Jakmile je zpráva vložena, budou podtržené bity nahrazeny bity ze zprávy. Výsledný obrázek se od originálu téměř neliší.

Původní
Obrázek s vloženou zprávou

Steganografie ve formátu GIF
Steganografie ve formátu GIF

Metoda rozšíření palety

Když pomocí této metody otevřete obrázek obsahující zprávu, uvidíte následující obrázek:

Steganografie ve formátu GIF

Je jasné, že tato metoda nebude fungovat pro plnohodnotné špionážní aktivity a může vyžadovat dodatečné šifrování zprávy.

Šifrování/dešifrování v animovaných obrázcích funguje stejně jako v běžných statických obrázcích, ale animace není narušena.

Použité zdroje:

Stáhnutí:

Zdroj: www.habr.com

Přidat komentář