Steganografija u GIF-u

Uvod

Pozdrav.
Ne tako davno, dok sam studirao na sveučilištu, postojao je kolegij iz discipline "Softverske metode informacijske sigurnosti". Zadatak je od nas zahtijevao izradu programa koji ugrađuje poruku u GIF datoteke. Odlučio sam to učiniti u Javi.

U ovom ću članku opisati neke teorijske toč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 pohranu komprimiranih podataka bez gubitka kvalitete u formatu do 256 boja. Ovaj je format 1987. (GIF87a) razvio CompuServe za prijenos rasterskih slika preko mreža. Godine 1989. format je modificiran (GIF89a), dodana je podrška za transparentnost i animaciju.

GIF datoteke imaju blok strukturu. Ovi blokovi uvijek imaju fiksnu duljinu (ili to ovisi o nekim oznakama), tako da je gotovo nemoguće pogriješiti gdje se koji blok nalazi. Struktura najjednostavnije neanimirane GIF slike u formatu GIF89a:

Steganografija u GIF-u

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

  • CT — prisutnost globalne palete. Ako je ova zastavica postavljena, globalna paleta mora započeti odmah nakon logičkog rukovatelja zaslona.
  • 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

Za šifriranje poruka u slikovnim datotekama koristit će se sljedeće metode:

  • LSB (Least Significant Bit) metoda
  • Metoda dodavanja palete

LSB metoda - uobičajena metoda steganografije. Sastoji se od zamjene zadnjih značajnih bitova u spremniku (u našem slučaju, bajtova globalne palete) s bitovima skrivene poruke.

Program će koristiti zadnja 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 stupnjeva. Takva će promjena, prvo, biti nevidljiva ili teško uočljiva ljudskom oku, a drugo, neće biti vidljiva na uređajima za izlaz informacija niske kvalitete.

Količina informacija izravno će ovisiti o veličini palete slika. Budući da je maksimalna veličina palete 256 boja, a ako su dva bita poruke upisana u komponentu svake boje, tada je maksimalna duljina poruke (s maksimalnom paletom na slici) 192 bajta. Nakon što je poruka ugrađena u sliku, veličina datoteke se ne mijenja.

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

Praktični dio

Dizajn programa

Svi potrebni alati za implementaciju algoritama enkripcije i dešifriranja bit će uključeni u paket com.tsarik.steganography. Ovaj paket uključuje sučelje Encryptor s metodama encrypt и decrypt, Razred Binary, koji pruža mogućnost rada s nizovima bitova, kao i klasama izuzetaka UnableToEncryptException и UnableToDecryptException, koji bi se trebao koristiti u metodama sučelja Encryptor u slučaju grešaka kodiranja odnosno dekodiranja.

Glavni programski paket com.tsarik.programs.gifed će uključivati ​​klasu programa koja se može izvoditi sa statičkom metodom main, omogućujući vam pokretanje programa; klasa koja pohranjuje parametre programa; i paketi s drugim razredima.

U paketu će biti predstavljena implementacija samih algoritama com.tsarik.programs.gifed.gif klase GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Obje ove klase će implementirati sučelje Encryptor.

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

Steganografija u GIF-u

Za utvrđivanje prisutnosti poruke u slici potrebno je na početak poruke dodati određeni niz bitova koje dekoder prvo čita i provjerava ispravnost. Ako se ne podudara, smatra se da na slici nema skrivene poruke. Zatim morate odrediti duljinu poruke. Zatim sam tekst poruke.

Dijagram klasa cijele aplikacije:

Steganografija u GIF-u

Provedba programa

Implementacija cjelokupnog programa može se podijeliti u dvije komponente: implementacija enkripcije sučelja i metode dešifriranja Encryptor, u razredima GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, te implementacija korisničkog sučelja.

Razmotrite klasu GIFEncryptorByLSBMethod.

Steganografija u GIF-u

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

Algoritam metode 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 bit će slični, samo je način spremanja/čitanja informacija drugačiji.

U klasi MainFrame opisane su metode omotača: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), obrada rezultata metoda sučelja Encryptor i interakcija s korisnikom, tj. otvaranje dijaloga za odabir datoteke, prikazivanje poruka o pogreškama, itd.; kao i druge metode: openImage(), dopuštajući korisniku odabir slike, exit(), koji izlazi iz aplikacije. Ove metode se pozivaju iz Actionodgovarajuće stavke izbornika. Ova klasa dodatno implementira pomoćne metode: createComponents() - stvaranje 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 iz palete.

Rad programa

LBS metoda

Recimo da postoji ovakva slika:

Steganografija u GIF-u

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

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

Steganografija u GIF-u

Nakon što je poruka ugrađena, podcrtani bitovi bit će zamijenjeni bitovima iz poruke. Dobivena slika gotovo se ne razlikuje od izvornika.

Original
Slika s ugrađenom porukom

Steganografija u GIF-u
Steganografija u GIF-u

Metoda proširenja palete

Kada ovom metodom otvorite sliku koja sadrži poruku, vidjet ćete sljedeću sliku:

Steganografija u GIF-u

Jasno je da ova metoda neće raditi za potpune š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