Steganografija u GIF-u

Uvod

Pozdravljam vas.
Nedavno, dok sam studirao na sveučilištu, imao sam završni rad iz predmeta „Programske metode zaštite informacija“. Prema zadatku, trebalo je izraditi program koji bi ugrađivao poruke u GIF datoteke. Odlučio sam to napraviti u Javi.

U ovom članku ću opisati neke teorijske aspekte, kao i način na koji je ovaj mali program nastao.

Teorijski dio

GIF format

GIF (eng. Graphics Interchange Format — format za razmjenu slika) — format za pohranu grafičkih slika, sposoban pohranjivati komprimirane podatke bez gubitka kvalitete s do 256 boja. Ovaj format je razvijen 1987. godine (GIF87a) od strane tvrtke CompuServe za prijenos rasterskih slika kroz mreže. Godine 1989. format je modificiran (GIF89a), dodana je podrška za prozirnost i animaciju.

GIF datoteke imaju blokovnu strukturu. Ti blokovi uvijek imaju fiksnu duljinu (ili ona ovisi o nekim oznakama), tako da je gotovo nemoguće pogriješiti gdje se koji blok nalazi. Struktura najjednostavnijeg neanimiranog GIF-a formata GIF89a:

Steganografija u GIF-u

Od svih blokova strukture, u ovom slučaju zanimaju nas blok globalne palete i parametri koji se odnose na paletu:

  • CT — postojanje globalne palete. Ako je ova oznaka postavljena, globalna paleta bi trebala početi odmah nakon deskriptora logičkog zaslona.
  • Size — veličina palete i broj boja slike. Vrijednosti ovog parametra:

Size
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

Kao metode šifriranja poruka u slikovnim datotekama koriste se:

  • LSB metoda (Least Significant Bit, najmanje značajni bit)
  • Metoda dopunjavanja palete

LSB metoda — raširena metoda steganografije. To se sastoji u zamjeni posljednjih značajnih bitova u kontejneru (u našem slučaju bajtovi globalne palete) bitovima skrivene poruke.

U ovom programu će se koristiti dva posljednja bita u bajtovima globalne palete unutar ove metode. To znači da će za 24-bitnu sliku, gdje boja palete predstavlja tri bajta za crvenu, plavu i zelenu boju, nakon umetanja poruke, svaka komponenta boje promijeniti maksimalno 3/255 gradacije. Ova promjena, prvo, bit će neprimjetna ili teško primjetna ljudskom oku, a drugo, neće biti uočljiva na uređajima s niskom kvalitetom prikaza.

Količina informacija će izravno ovisiti o veličini palete slike. Budući da maksimalna veličina palete iznosi 256 boja, i ako se upisuju dva bita poruke u komponentu svake boje, maksimalna dužina poruke (prilikom maksimalne palete u slici) iznosi 192 bajta. Nakon umetanja poruke u sliku, veličina datoteke se ne mijenja.

Metoda ekstenzije palete, koja radi samo za GIF strukturu. Bit će najučinkovitija za slike s malim paletama. Njena suština je da povećava veličinu palete, čime osigurava dodatni prostor za zapis potrebnih bajtova na mjestu bajtova boja. Uz napomenu da je minimalna veličina palete 2 boje (6 bajtova), maksimalna veličina umetnute poruke može biti 256×3–6=762 bajta. Nedostatak je niska kriptosigurnost, umetnutu poruku može pročitati bilo koji tekstualni urednik, osim ako poruka nije bila podvrgnuta dodatnoj enkripciji.

Praktični dio

Projektiranje programa

Svi potrebni alati za implementaciju algoritama šifriranja i dešifriranja bit će u paketu com.tsarik.steganography. Ovaj paket uključuje sučelje Encryptor s metodama encrypt i decrypt, klasu Binary, koja omogućava rad s nizovima bitova, kao i klase izuzetaka UnableToEncryptException i UnableToDecryptException, koje se moraju koristiti u metodama sučelja Encryptor u slučaju grešaka kod kodiranja i dekodiranja.

Glavni paket programa com.tsarik.programs.gifed bit će uključivati izvršnu klasu programa sa statičkom metodom main, koja omogućava pokretanje programa; klasu koja pohranjuje parametre programa; i pakete s drugim klasama.

Implementacija samih algoritama bit će predstavljena u paketu com.tsarik.programs.gifed.gif klasama GIFEncryptorByLSBMethod i GIFEncryptorByPaletteExtensionMethod. Oba ta klasa implementirat će sučelje Encryptor.

Na temelju strukture GIF formata može se sastaviti opći algoritam za umetanje poruke u paletu slike:

Steganografija u GIF-u

Kako bi se utvrdila prisutnost poruke u slici, potrebno je na početak poruke dodati određenu sekvencu bitova koju dešifrirac prvo pročita i provjeri ispravnost. Ako se ne podudara, smatra se da u slici nema skrivenih poruka. Zatim treba navesti duljinu poruke. Nakon toga dolazi sam tekst poruke.

Dijagram klasa cijele aplikacije:

Steganografija u GIF-u

Implementacija programa

Implementaciju cijelog programa može se podijeliti na dva sastavna dijela: implementaciju metoda šifriranja i dešifriranja sučelja Encryptor, u klasama GIFEncryptorByLSBMethod i GIFEncryptorByPaletteExtensionMethod, i implementaciju korisničkog sučelja.

Razmotrimo klasu GIFEncryptorByLSBMethod.

Steganografija u GIF-u

Polja firstLSBit i secondLSBit sadrže brojeve bitova svakog bajta slike, u koje treba unositi i odakle se treba čitati poruka. Polje checkSequence pohranjuje kontrolnu sekvencu bitova za osiguranje prepoznavanja ugrađene poruke. Statička metoda getEncryptingFileParameters vraća parametre navedene datoteke i karakteristike potencijalne poruke.

Algoritam metode encrypt klase GIFEncryptorByLSBMethod:

Steganografija u GIF-u

I njezin kod:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Ulazna datoteka je null");
	}
	if (out == null) {
		throw new NullPointerException("Izlazna datoteka je null");
	}
	if (text == null) {
		throw new NullPointerException("Tekst je null");
	}
	
	// čitaj bajtove iz ulazne datoteke
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// provjeri format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Ulazna datoteka ima pogrešan GIF format");
	}
	
	// pročitaj svojstvo veličine palete iz prva tri bita u desetom bajtu iz datoteke
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// izračunaj broj boja i moguću duljinu poruke
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // jedan bajt za provjeru i jedan bajt za duljinu poruke
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Tekst je prevelik");
	}
	
	int n = 13;
	
	// napiši sekvencu provjere
	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++;
	}
	
	// napiši duljinu teksta
	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++;
	}
	
	// napiši poruku
	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++;
		}
	}
	
	// napiši izlaznu datoteku
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Algoritam i izvorni kod metode decrypt klase GIFEncryptorByLSBMethod:

Steganografija u GIF-u

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Ulazna datoteka je null");
	}
	
	// čitaj bajtove iz ulazne datoteke
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// provjeri format
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Ulazna datoteka ima pogrešan GIF format");
	}
	
	// pročitaj veličinu palete iz prva tri bita 10. bajta iz datoteke
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// izračunaj broj boja i moguću dužinu poruke
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // jedan bajt za provjeru i jedan bajt za dužinu poruke
	
	int n = 13;
	
	// pročitaj provjeru sekvence
	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("Ne postoji šifrirana poruka u slici (Provjera sekvence je neispravna)");
	}
	
	// pročitaj dužinu teksta
	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("Duljina dekodiranog teksta je manja od 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Nema poruka (Duljina dekodirane poruke (" + textLength + ") je manja od Moguće duljine poruke (" + possibleTextLength + "))");
	}
	
	// pročitaj bite teksta i napravi byte-ove teksta
	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čna, samo se razlikuje metoda za spremanje/čitanje informacija.

U klasi MainFrame opisani su „wrapper“ metodi: encryptImage(Encryptor encryptor) i decryptImage(Encryptor encryptor), koje obrađuju rezultate metoda sučelja Encryptor i omogućuju interakciju s korisnikom, odnosno otvaraju dijalog za odabir datoteka, prikazuju poruke o greškama itd.; kao i druge metode: openImage(), koja omogućuje korisniku odabir slike, exit(), koja završava aplikaciju. Ove metode se pozivaju iz Actionodgovarajućih stavki izbornika. U ovoj se klasi dodatno implementiraju pomoćne metode: createComponents() — izrada komponenti forme, loadImageFile(File f) — učitavanje slike u posebnu komponentu iz datoteke. Implementacija klase GIFEncryptorByPaletteExtensionMethod slična je implementaciji klase GIFEncryptorByLSBMethod, osnovna razlika je u načinu zapisivanja i čitanja bajtova poruke iz palete.

Rad programa

Metoda LBS

Pretpostavimo da postoji takva slika:

Steganografija u GIF-u

Na ovoj slici paleta se sastoji od 256 boja (kako čuva Paint). Prve četiri boje: bijela, crna, crvena, zelena. Ostale boje su crne. Sekvenca bita globalne palete biće sledeća:

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

Steganografija u GIF-u

Nakon umetanja poruke, podvučeni bitovi će biti zamenjeni bitovima iz poruke. Dobijena slika se gotovo ne razlikuje od originala.

Izvorni tekst
Slika s umetnutom porukom

Steganografija u GIF-u
Steganografija u GIF-u

Metoda ekstenzije palete

Otvarajući sliku u koju je umetnuta poruka ovom metodom, može se uočiti ovakva slika:

Steganografija u GIF-u

Jasno je da za punu špijunsku aktivnost ovakav metod nije dovoljan i zahteva dodatnu enkripciju poruke.

Enkripcija / dekripcija u animiranim slikama funkcioniše, kao i u običnim statičnim slikama, pri čemu animacija nije narušena.

Korišćeni izvori:

Preuzmi:

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster