Steganografija u GIF-u

Uvod

Pozdravljam.
Nedavno, ko sem študiral na univerzi, sem imel projekt pri predmetu 'Programske metode za zaščito informacij'. Po nalogu je bilo treba izdelati program, ki vgradi sporočilo v datoteke v formatu GIF. Odločil sem se, da to naredim v Javi.

V tem članku bom opisal nekatere teoretične točke, pa tudi, kako je bila ustvarjena ta majhna programska oprema.

Teoretični del

Format GIF

GIF (angleško Graphics Interchange Format - format za izmenjavo slik) je format za shranjevanje grafičnih slik, ki lahko shrani stisnjene podatke brez izgub v formatu do 256 barv. Ta format je bil razvijen leta 1987 (GIF87a) s strani podjetja CompuServe za prenos rasteriziranih slik po omrežju. Leta 1989 je bil format spremenjen (GIF89a), dodana je bila podpora za prosojnost in animacijo.

Datoteke v formatu GIF imajo blokovno strukturo. Ti podatkovni bloki imajo vedno fiksno dolžino (ali pa je ta dolžina odvisna od nekaterih zastavic), zato je skoraj nemogoče zgrešiti, kje se kateri blok nahaja. Struktura najpreprostejšega neanimiranega GIF-slikovnega formata GIF89a:

Steganografija u GIF-u

Od vseh blokov strukture nas bodo v tem primeru zanimali blok globalne palete in parametri, ki odgovarjajo za paleto:

  • CT — prisotnost globalne palete. Če je ta zastavica nastavljena, potem mora takoj po descriptorju logičnega zaslona slediti globalna paleta.
  • Velikost — velikost palete in število barv slike. Vrednosti tega parametra so:

Velikost
Število barv
Velikost palete, bajti

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Metode šifriranja

Kot metode za šifriranje sporočil v slikovnih datotekah bodo uporabljene:

  • Metoda LSB (Least Significant Bit, najmanj pomemben bit)
  • Metoda dopolnjevanja palete

Metoda LSB — razširjena metoda steganografije. Sestoji iz zamenjave zadnjih pomembnih bitov v vsebniku (v našem primeru bajti globalne palete) z bitom skritega sporočila.

V programu se bosta za to metodo uporabila zadnja dva bita v bajtih globalne palete. To pomeni, da se bo za 24-bitno sliko, kjer barva palete predstavlja tri bajte za rdečo, modro in zeleno barvo, po vstavitvi sporočila vsaka sestavina barve spremenila največ za 3/255 stopinj. Takšna sprememba bo, prvič, neopažena ali težko opazna za človeško oko, in drugič, ne bo zaznavna na nizkokakovostnih izhodnih napravah.

Količina informacij bo neposredno odvisna od velikosti palete slike. Ker je maksimalna velikost palete 256 barv in če v vsako sestavino barve vstavimo po dva bita sporočila, potem maksimalna dolžina sporočila (pri maksimalni paleti v sliki) znaša 192 bajta. Po vstavitvi sporočila v sliko se velikost datoteke ne spremeni.

Metoda širjenja palete, ki deluje samo za strukturo GIF. Najbolj učinkovita bo pri slikah z majhno paleto. Njen bistvo je, da povečuje velikost palete, s čimer daje dodaten prostor za zapis potrebnih bajtov namesto bajtov barv. Če upoštevamo, da je minimalna velikost palete 2 barvi (6 bajtov), potem lahko maksimalna velikost vstavljenega sporočila znaša 256×3–6=762 bajtov. Slabost je nizka zaščita šifriranja, vstavljenega sporočila ni mogoče prebrati z uporabo katerega koli besedilnega urejevalnika, če sporočilo ni bilo dodatno šifrirano.

Praktični del

Načrtovanje programa

Vsi potrebni pripomočki za izvajanje algoritmov šifriranja in dešifriranja bodo v paketu com.tsarik.steganography. Ta paket vključuje vmesnik Encryptor s metodami encrypt in decrypt, razred Binary, ki omogoča delo z nizi bitov, ter razrede izjem UnableToEncryptException in UnableToDecryptException, ki jih je treba uporabiti v metodah vmesnika Encryptor v primeru napak pri kodiranju in dekodiranju.

Glavni paket programa com.tsarik.programs.gifed bo vseboval zagnani razred programa s statično metodo main, ki omogoča zagon programa; razred, ki shranjuje parametre programa; in pakete z drugimi razredi.

Izvedba samih algoritmov bo predstavljena v paketu com.tsarik.programs.gifed.gif razredih GIFEncryptorByLSBMethod in GIFEncryptorByPaletteExtensionMethod. Obe ti razredi bosta implementirala vmesnik Encryptor.

Na podlagi strukture formata GIF lahko sestavimo splošni algoritem za vstavitev sporočila v paleto slike:

Steganografija u GIF-u

Za določitev prisotnosti sporočila v sliki je potrebno na začetek sporočila dodati neko zaporedje bitov, ki ga dešifrator najprej prebere in preveri pravilnost. Če se ne ujema, se šteje, da v sliki ni skritega sporočila. Nato je treba navesti dolžino sporočila. Potem samo besedilo sporočila.

Diagram razredov celotne aplikacije:

Steganografija u GIF-u

Izvedba programa

Izvedbo celotnega programa lahko razdelimo na dva dela: izvedbo metod šifriranja in dešifriranja vmesnika Encryptor, v razredih GIFEncryptorByLSBMethod in GIFEncryptorByPaletteExtensionMethod, in izvedbo uporabniškega vmesnika.

Oglejmo si razred GIFEncryptorByLSBMethod.

Steganografija u GIF-u

Polja firstLSBit in secondLSBit vsebuje številke bitov vsakega bajta slike, v katerega je treba shraniti in od koder se bere sporočilo. Polje checkSequence hrani kontrolno zaporedje bitov za zagotavljanje prepoznavanja vgrajenega sporočila. Statistična metoda getEncryptingFileParameters vrne parametre navedenega datoteke in značilnosti potencialnega sporočila.

Algoritem metode encrypt klasa GIFEncryptorByLSBMethod:

Steganografija u GIF-u

In njena koda:

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

Algoritem in izvorna koda metode decrypt klasa GIFEncryptorByLSBMethod:

Steganografija u GIF-u

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

Implementacija razreda GIFEncryptorByPaletteExtensionMethod bit će slična, ali se razlikuje po metodi spremanja/čitanja informacija.

U razredu MainFrame opisane su metode-obloge: encryptImage(Encryptor encryptor) in decryptImage(Encryptor encryptor), koje obrađuju rezultate metoda interfejsa Encryptor i ostvaruju interakciju s korisnikom, tj. otvaraju dijalog za izbor datoteka, prikazuju poruke o greškama itd.; kao i druge metode: openImage(), omogućava korisniku da odabere sliku, exit(), koji izlazi iz aplikacije. Ove metode se pozivaju iz Action‘ova odgovarajućih opcija menija. U ovom razredu dodatno su implementirane pomoćne metode: createComponents() — kreiranje komponenata forme, loadImageFile(File f) — učitavanje slike u specijalnu komponentu iz datoteke. Implementacija razreda GIFEncryptorByPaletteExtensionMethod je slična implementaciji razreda GIFEncryptorByLSBMethod, osnovna razlika je u načinu zapisivanja i čitanja bajtova poruke iz palete.

Delovanje programa

Metoda LBS

Pretpostavimo da postoji ova slika:

Steganografija u GIF-u

V tem prikazu paleta obsega 256 barv (to ohranja Paint). Prve štiri barve so: bela, črna, rdeča, zelena. Preostale barve so črne. Zaporedje bitov globalne palete bo naslednje:

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

Steganografija u GIF-u

Po vstavitvi sporočila bodo podčrtani biti zamenjani z biti iz sporočila. Pridobljena slika se skoraj ne razlikuje od izvirnika.

Izvirnik
Slika z vstavljenim sporočilom

Steganografija u GIF-u
Steganografija u GIF-u

Metoda širjenja palete

Ko odprete sliko, v katero je vstavljen sporočilo na ta način, lahko ugotovite takšno sliko:

Steganografija u GIF-u

Seveda ta metoda ni primerna za popolno vohunsko dejavnost in zahteva morda dodatno šifriranje sporočila.

Šifriranje/razšifriranje v animiranih slikah deluje enako kot pri običajnih statičnih slikah, pri čemer animacija ni motena.

Uporabljeni viri:

Preuzmi:

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster