Szteganográfia GIF-ben

Bevezetés

Приветствую.
Nem is olyan régen, amikor az egyetemen tanultam, volt egy tanfolyam az „Információbiztonság szoftveres módszerei” tudományágból. A feladathoz olyan programot kellett létrehoznunk, amely üzenetet ágyaz be GIF-fájlokba. Úgy döntöttem, hogy Java-ban csinálom.

Ebben a cikkben leírok néhány elméleti pontot, valamint azt, hogyan készült ez a kis program.

Elméleti rész

GIF formátum

A GIF (Graphics Interchange Format – a képek cseréjének formátuma) egy grafikus képek tárolására szolgáló formátum, amely képes tömörített adatok tárolására minőségromlás nélkül, akár 256 szín formátumban. Ezt a formátumot 1987-ben (GIF87a) fejlesztette ki a CompuServe raszterképek hálózaton keresztüli továbbítására. 1989-ben módosították a formátumot (GIF89a), hozzáadták az átlátszóság és az animáció támogatását.

A GIF-fájlok blokkszerkezettel rendelkeznek. Ezek a blokkok mindig fix hosszúságúak (vagy ez néhány zászlótól függ), így szinte lehetetlen tévedni az egyes blokkok elhelyezkedését illetően. A legegyszerűbb, nem animált GIF-kép felépítése GIF89a formátumban:

Szteganográfia GIF-ben

A szerkezet összes blokkja közül ebben az esetben a globális palettablokk és a palettáért felelős paraméterek érdekelnek minket:

  • CT — globális paletta jelenléte. Ha ez a jelző be van állítva, a globális palettának közvetlenül a logikai képernyőfogó után kell kezdődnie.
  • Size — paletta mérete és színek száma a képen. Ennek a paraméternek az értékei:

Méret
Színek száma
Paletta mérete, bájt

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Titkosítási módszerek

A képfájlokban lévő üzenetek titkosításához a következő módszereket kell használni:

  • LSB (Least Significant Bit) módszer
  • Paletta hozzáadása módszer

LSB módszer - a szteganográfia elterjedt módszere. Ez abból áll, hogy a tároló utolsó jelentős bitjeit (esetünkben a globális paletta bájtjait) a rejtett üzenet bitjeire cseréljük.

A program a globális paletta bájtjainak utolsó két bitjét használja ennek a módszernek a részeként. Ez azt jelenti, hogy egy 24 bites képnél, ahol a színpaletta három bájt a piros, a kék és a zöld színénél, egy üzenet beágyazása után minden színösszetevő legfeljebb 3/255 gradációval változik. Egy ilyen változás egyrészt láthatatlan vagy nehezen észrevehető lesz az emberi szem számára, másrészt nem lesz látható a rossz minőségű információs kimeneti eszközökön.

Az információ mennyisége közvetlenül függ a képpaletta méretétől. Mivel a paletta maximális mérete 256 szín, és ha minden szín komponensébe két üzenetbitet írunk, akkor a maximális üzenethossz (a képen látható maximális palettával) 192 bájt. Ha az üzenet beágyazódik a képbe, a fájl mérete nem változik.

Paletta bővítési módszer, amely csak a GIF struktúrához működik. Kis palettával rendelkező képeken lesz a leghatékonyabb. Lényege, hogy megnöveli a paletta méretét, ezáltal további helyet biztosít a szükséges bájtok írására a színbájtok helyére. Ha figyelembe vesszük, hogy a paletta minimális mérete 2 szín (6 bájt), akkor a beágyazott üzenet maximális mérete 256 × 3–6 = 762 bájt lehet. Hátránya az alacsony kriptográfiai biztonság, a beágyazott üzenet bármely szövegszerkesztővel olvasható, ha az üzenetet nem tették alá további titkosításnak.

Gyakorlati rész

Programtervezés

A csomag tartalmazza a titkosítási és visszafejtési algoritmusok megvalósításához szükséges összes eszközt com.tsarik.steganography. Ez a csomag tartalmazza az interfészt Encryptor módszerekkel encrypt и decrypt, Osztály Binary, amely lehetővé teszi a bittömbök, valamint a kivételosztályok kezelését UnableToEncryptException и UnableToDecryptException, amelyet az interfész módszerekben kell használni Encryptor kódolási és dekódolási hibák esetén.

Fő programcsomag com.tsarik.programs.gifed tartalmazni fog egy statikus metódussal futtatható programosztályt main, amely lehetővé teszi a program futtatását; programparamétereket tároló osztály; és csomagok más osztályokkal.

Maguk az algoritmusok megvalósítása a csomagban lesz bemutatva com.tsarik.programs.gifed.gif osztályok GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Mindkét osztály megvalósítja az interfészt Encryptor.

A GIF formátum felépítése alapján létrehozhat egy általános algoritmust az üzenet képpalettára való beillesztéséhez:

Szteganográfia GIF-ben

Az üzenet képben való jelenlétének meghatározásához egy bizonyos bitsorozatot kell hozzáadni az üzenet elejéhez, amelyet a dekóder először beolvas, és ellenőrzi a helyességet. Ha nem egyezik, akkor a képen nincs rejtett üzenet. Ezután meg kell adnia az üzenet hosszát. Aztán maga az üzenet szövege.

A teljes alkalmazás osztálydiagramja:

Szteganográfia GIF-ben

A program megvalósítása

A teljes program megvalósítása két részre osztható: interfész titkosítási és visszafejtési módszerek megvalósítására Encryptor, az órákon GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, valamint a felhasználói felület megvalósítása.

Vegye figyelembe az osztályt GIFEncryptorByLSBMethod.

Szteganográfia GIF-ben

mezők firstLSBit и secondLSBit tartalmazza a kép egyes bájtjainak azon bitszámát, amelybe az üzenetet be kell írni, és honnan kell olvasni az üzenetet. Terület checkSequence ellenőrző bitsorozatot tárol a beágyazott üzenet felismerésének biztosítása érdekében. Statikus módszer getEncryptingFileParameters visszaadja a megadott fájl paramétereit és a potenciális üzenet jellemzőit.

Módszer algoritmus encrypt класса GIFEncryptorByLSBMethod:

Szteganográfia GIF-ben

És a kódja:

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

A módszer algoritmusa és forráskódja decrypt класса GIFEncryptorByLSBMethod:

Szteganográfia GIF-ben

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

Az osztály megvalósítása GIFEncryptorByPaletteExtensionMethod hasonló lesz, csak az információ mentésének/olvasásának módja különbözik.

Osztályban MainFrame A csomagolási módszerek leírása: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), interfész módszerek eredményeinek feldolgozása Encryptor és interakció a felhasználóval, azaz fájlkiválasztó párbeszédablak megnyitása, hibaüzenetek megjelenítése stb.; valamint egyéb módszerek: openImage()lehetővé teszi a felhasználó számára egy kép kiválasztását, exit(), amely kilép az alkalmazásból. Ezeket a módszereket innen hívják Actiona megfelelő menüpontokat. Ez az osztály emellett kiegészítő metódusokat valósít meg: createComponents() - formaelemek létrehozása, loadImageFile(File f) — kép betöltése egy fájlból egy speciális komponensbe. Az osztály megvalósítása GIFEncryptorByPaletteExtensionMethod hasonló az osztály megvalósításához GIFEncryptorByLSBMethod, a fő különbség az üzenet bájtok írási és olvasási módja a palettáról.

A program működése

LBS módszer

Tegyük fel, hogy van egy ilyen kép:

Szteganográfia GIF-ben

Ezen a képen a paletta 256 színből áll (amint a Paint menti). Az első négy szín: fehér, fekete, piros, zöld. A többi szín fekete. A globális paletta bitsorrendje a következő lesz:

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

Szteganográfia GIF-ben

Az üzenet beágyazása után az aláhúzott bitek az üzenet bitjére cserélődnek. A kapott kép szinte semmiben sem különbözik az eredetitől.

Eredeti
Kép beágyazott üzenettel

Szteganográfia GIF-ben
Szteganográfia GIF-ben

Paletta bővítési módszer

Amikor ezzel a módszerrel megnyit egy üzenetet tartalmazó képet, a következő kép jelenik meg:

Szteganográfia GIF-ben

Nyilvánvaló, hogy ez a módszer nem működik teljes értékű kémtevékenység esetén, és további titkosítást igényelhet az üzenetben.

Az animált képek titkosítása/visszafejtése ugyanúgy működik, mint a normál statikus képeknél, de az animáció nem szakad meg.

Felhasznált források:

letöltés:

Forrás: will.com

Hozzászólás