Steganografija u GIF-u

Uvod

Pozdrav.
Ne tako davno, dok sam studirao na fakultetu, postojao je kurs iz discipline „Softverske metode informacione sigurnosti“. Zadatak je zahtijevao od nas da kreiramo program koji ugrađuje poruku u GIF datoteke. Odlučio sam da to uradim u Javi.

U ovom članku ću opisati neke teorijske tačke, kao i kako je nastao ovaj mali program.

Teorijski dio

GIF format

GIF (Graphics Interchange Format - format za razmjenu slika) je format za pohranjivanje grafičkih slika, sposoban za pohranjivanje komprimiranih podataka bez gubitka kvalitete u formatu do 256 boja. Ovaj format je 1987. godine (GIF87a) razvio CompuServe za prenos rasterskih slika preko mreža. 1989. godine format je izmijenjen (GIF89a), dodata je podrška za transparentnost i animaciju.

GIF datoteke imaju blok strukturu. Ovi blokovi uvijek imaju fiksnu dužinu (ili zavisi od nekih zastavica), tako da je gotovo nemoguće pogriješiti gdje se koji blok nalazi. Struktura najjednostavnije neanimirane GIF slike u GIF89a formatu:

Steganografija u GIF-u

Od svih blokova strukture, u ovom slučaju će nas zanimati globalni blok palete i parametri odgovorni za paletu:

  • CT — prisustvo globalne palete. Ako je ova zastavica postavljena, globalna paleta mora početi odmah nakon logičke ručke ekrana.
  • Size — veličina palete i broj boja na slici. Vrijednosti za ovaj parametar:

veličina
Broj boja
Veličina palete, bajtovi

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

Sljedeće metode će se koristiti za šifriranje poruka u slikovnim datotekama:

  • Metoda LSB (najmanje značajnog bita).
  • Metoda dodavanja palete

LSB metoda - uobičajena metoda steganografije. Sastoji se od zamjene posljednjih bitnih bitova u kontejneru (u našem slučaju, bajtova globalne palete) sa bitovima skrivene poruke.

Program će koristiti posljednja dva bita u bajtovima globalne palete kao dio ove metode. To znači da će se za 24-bitnu sliku, gdje je paleta boja tri bajta za crvenu, plavu i zelenu, nakon ugrađivanja poruke u nju svaka komponenta boje promijeniti za najviše 3/255 gradacija. Takva promjena, prvo, ljudskom će oku biti nevidljiva ili teško uočljiva, a drugo, neće biti vidljiva na uređajima za izlaz informacija niske kvalitete.

Količina informacija direktno će ovisiti o veličini palete slika. Budući da je maksimalna veličina palete 256 boja, i ako su dva bita poruke upisana u komponentu svake boje, tada je maksimalna dužina poruke (sa maksimalnom paletom na slici) 192 bajta. Kada se poruka ugradi u sliku, veličina datoteke se ne mijenja.

Metoda proširenja palete, koji radi samo za GIF strukturu. Najefikasniji će biti na slikama s malom paletom. Njegova suština je da povećava veličinu palete, čime se daje dodatni prostor za upisivanje potrebnih bajtova umjesto bajtova boje. Ako uzmemo u obzir da je minimalna veličina palete 2 boje (6 bajtova), onda maksimalna veličina ugrađene poruke može biti 256 × 3–6 = 762 bajta. Nedostatak je niska kriptografska sigurnost; ugrađena poruka može se pročitati pomoću bilo kojeg uređivača teksta ako poruka nije podvrgnuta dodatnoj enkripciji.

Praktični deo

Dizajn programa

Svi potrebni alati za implementaciju algoritama enkripcije i dešifriranja će biti uključeni u paket com.tsarik.steganography. Ovaj paket uključuje interfejs Encryptor sa metodama encrypt и decrypt, Class Binary, koji pruža mogućnost rada sa nizovima bitova, kao i klasama izuzetaka UnableToEncryptException и UnableToDecryptException, koji treba koristiti u metodama interfejsa Encryptor u slučaju grešaka kodiranja i dekodiranja.

Glavni programski paket com.tsarik.programs.gifed uključit će izvodljivu programsku klasu sa statičkom metodom main, što vam omogućava da pokrenete program; klasa koja pohranjuje parametre programa; i paketi sa drugim klasama.

Implementacija samih algoritama će biti predstavljena u paketu com.tsarik.programs.gifed.gif casovi GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Obje ove klase će implementirati interfejs Encryptor.

Na osnovu strukture GIF formata, možete kreirati opći algoritam za uvođenje poruke u paletu slika:

Steganografija u GIF-u

Da bi se utvrdilo prisustvo poruke na slici, potrebno je na početak poruke dodati određeni niz bitova koje dekoder prvo čita i provjerava ispravnost. Ako se ne poklapa, onda se smatra da na slici nema skrivene poruke. Zatim morate odrediti dužinu poruke. Zatim tekst same poruke.

Dijagram klasa cijele aplikacije:

Steganografija u GIF-u

Implementacija programa

Implementacija cjelokupnog programa može se podijeliti na dvije komponente: implementaciju interfejsa enkripcije i metode dešifriranja Encryptor, u časovima GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethodi implementaciju korisničkog interfejsa.

Razmotrite klasu GIFEncryptorByLSBMethod.

Steganografija u GIF-u

polja firstLSBit и secondLSBit sadrže brojeve bitova svakog bajta slike u koji treba uneti poruku i odakle poruku treba pročitati. Polje checkSequence pohranjuje sekvencu bitova za provjeru kako bi se osiguralo prepoznavanje ugrađene poruke. Statička metoda getEncryptingFileParameters vraća parametre navedene datoteke i karakteristike potencijalne poruke.

Metodski algoritam encrypt razred GIFEncryptorByLSBMethod:

Steganografija u GIF-u

I njegov kod:

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

Algoritam i izvorni kod metode decrypt razred 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 klase GIFEncryptorByPaletteExtensionMethod će biti sličan, samo je način čuvanja/čitanja informacija drugačiji.

Na času MainFrame opisane su metode omotača: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), obrada rezultata metoda interfejsa Encryptor i interakciju s korisnikom, tj. otvaranje dijaloga za odabir datoteke, prikazivanje poruka o grešci, itd.; kao i druge metode: openImage(), omogućavajući korisniku da odabere sliku, exit(), koji izlazi iz aplikacije. Ove metode se pozivaju iz Action's odgovarajuće stavke menija. Ova klasa dodatno implementira pomoćne metode: createComponents() - kreiranje komponenti forme, loadImageFile(File f) — učitavanje slike u posebnu komponentu iz datoteke. Implementacija klase GIFEncryptorByPaletteExtensionMethod slično implementaciji klase GIFEncryptorByLSBMethod, glavna razlika je u načinu na koji se bajtovi poruke pišu i čitaju sa palete.

Rad programa

LBS metoda

Recimo da postoji slika poput ove:

Steganografija u GIF-u

Na ovoj slici, paleta se sastoji od 256 boja (kako Paint sprema). Prve četiri boje su: bijela, crna, crvena, zelena. Ostale boje su crne. Slijed bitova globalne palete bit će sljedeći:

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

Steganografija u GIF-u

Kada se poruka ugradi, podvučeni bitovi će biti zamijenjeni bitovima iz poruke. Dobivena slika se gotovo ne razlikuje od originala.

Original
Slika sa ugrađenom porukom

Steganografija u GIF-u
Steganografija u GIF-u

Metoda proširenja palete

Kada otvorite sliku koja sadrži poruku koristeći ovaj metod, vidjet ćete sljedeću sliku:

Steganografija u GIF-u

Jasno je da ova metoda neće raditi za punopravne špijunske aktivnosti i može zahtijevati dodatno šifriranje poruke.

Šifriranje/dešifriranje u animiranim slikama radi isto kao i u običnim statičnim slikama, ali animacija nije prekinuta.

Korišteni izvori:

Preuzimanje:

izvor: www.habr.com

Dodajte komentar