Steganograafia GIF-is

Sissejuhatus

Tere tulemast.
Mõni aeg tagasi, ülikoolis õppides, tegin kursustöö teemal „Tehnilised meetodid teabe kaitsmiseks“. Ülesanne oli luua programm, mis sisestab sõnumi GIF-vormingusse failidesse. Otsustasin teha Java keeles.

Sellest artiklist kirjeldan mõned teoreetilised aspektid ning kuidas see väike programm loodi.

Teoreetiline osa

GIF-vorming

GIF (ingl. k. Graphics Interchange Format — piltide vahetamiseks loodud vorming) on graafiliste piltide salvestamise vorming, mis suudab hoida kompressitud andmeid kvaliteedikadudeta kuni 256 värvi. See vorming loodi 1987. aastal (GIF87a) CompuServe'i poolt rasterpiltide edastamiseks võrkudes. 1989. aastal muudeti vormingut (GIF89a), lisati tugi läbipaistvusele ja animatsioonile.

GIF-failidel on plokkstruktuur. Need andmeplokid on alati fikseeritud pikkusega (või sõltuvad nad teatud lipukestest), seega on praktiliselt võimatu eksida selles, kus mingi blokk asub. Lihtsaim mitteanimatsiooni GIF-pildi struktuur vormingus GIF89a:

Steganograafia GIF-is

Struktuuri kõigist plokkidest huvitavad meid antud juhul globaalse paleti plokk ja parameetrid, mis vastutavad paleti eest:

  • CT — globaalse paleti olemasolu. Kui see lipp on seadistatud, peab loogilise ekraani deskriptorile järgima globaalse paleti algus.
  • Suurus — paleti suurus ja pildi värvide arv. Selle parameetri väärtused:

Suurus
Värvide arv
Paleti suurus, baitides

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Krüpteerimismeetodid

Kuna meetodite krüpteerimiseks sõnumite failidesse kasutatakse pilte:

  • LSB meetod (Least Significant Bit, kõige vähem oluline bitt)
  • Paleti täiendamise meetod

LSB meetod — levinud steganograafia meetod. See seisneb konteineri (meie puhul globaalse paleti baitide) viimaste oluliste bittide asendamises varjatud sõnumi bittidega.

Selle meetodi raames kasutatakse globaalse paleti baitides kahte viimast bitti. See tähendab, et 24-bitise pildi puhul, kus värvipalett koosneb kolmest baitist punase, sinise ja rohelise värvi jaoks, muutub sõnumi sisestamise järel iga värvikoostisosaga maksimaalselt 3/255 tooni. Selline muudatus on esiteks inimsilma jaoks peaaegu nähtamatu ning teiseks ei ole see eristatav madala kvaliteediga väljundi seadmetes.

Teabe hulk sõltub otseselt pildi paleti suurusest. Kuna maksimaalne paleti suurus on 256 värvi ja kui iga värvikomponendi sõnumisse sisestamiseks salvestatakse kaks bitti, siis maksimaalne sõnumi pikkus (maksimaalse paletiga pildis) on 192 baiti. Pärast sõnumi sisestamist pilti ei muutu faili suurus.

Paleti laiendamise meetod, mis töötab ainult GIF-struktuuri jaoks. See on kõige tõhusam väikeste palettide piltide puhul. Selle peamine idee on suurendada paleti suurust, suurendades seeläbi vajalikku ruumi värbiaine järjestamiseks. Arvestades, et minimaalne paleti suurus on 2 värvi (6 baiti), siis võib sisestatud sõnumi maksimaalne suurus olla 256×3–6=762 baiti. Puuduseks on madal krüptokaitse, sisestatud sõnumi saab lugeda mis tahes tekstiredaktori abil, kui sõnumit ei ole täiendavalt krüpteeritud.

Praktiline osa

Programmi projekteerimine

Kõik vajalikud tööriistad krüptimise ja dekrüptimise algoritmide teostamiseks on pakendis com.tsarik.steganography. See pakett sisaldab liidest Encryptor meetoditega encrypt ja decrypt, klass Binary, mis võimaldab töötada bitimassiividega, samuti erandiklassid UnableToEncryptException ja UnableToDecryptException, mida tuleb kasutada liidese meetodites Encryptor koodimise ja dekrüptimise vigade korral vastavalt.

Programmi põhikompakt com.tsarik.programs.gifed sisaldab käivitatavat programmi klassi koos staatilise meetodiga main, mis võimaldab programmi käivitada; klass, mis hoiab endas programmi parameetreid; ning pakette teiste klassidega.

Algoritmide reaalse rakenduse saab esitada paketis com.tsarik.programs.gifed.gif klasside GIFEncryptorByLSBMethod ja GIFEncryptorByPaletteExtensionMethod. Need kaks klassi rakendavad liidest Encryptor.

GIF formaadi struktuuri põhjal saab koostada üldise algoritmi sõnumi lisamiseks pildipaletti:

Steganograafia GIF-is

Et määrata, kas pildis on sõnum olemas, tuleb sõnumi algusesse lisada teatud bittide järjestus, mida dešifreerija loeb kõigepealt ja kontrollib selle õigsust. Kui see ei kattu, arvatakse, et pildis ei ole varjatud sõnumit. Edasi tuleb näidata sõnumi pikkus. Siis järgneb sõnumi tekst.

Kogu rakenduse klassidiagramm:

Steganograafia GIF-is

Programmi rakendamine

Kogu programmi rakenduse võib jagada kaheks osaks: krüptimise ja dekrüptimise meetodite rakendamine liideses Encryptor, klassides GIFEncryptorByLSBMethod ja GIFEncryptorByPaletteExtensionMethod, ja kasutajaliidese rakendamine.

Vaadakem klassi GIFEncryptorByLSBMethod.

Steganograafia GIF-is

Muutujad firstLSBit ja secondLSBit sisaldavad iga pildi baidi bitti numbreid, kuhu sõnum peab kirjutama ja kust see loetakse. Väli checkSequence salvestab bitise kontrolljärjestuse, et tagada sisseehitatud sõnumi tuvastamine. Statiline meetod getEncryptingFileParameters tagastab antud faili parameetrid ja potentsiaalse sõnumi omadused.

Meetodi algoritm encrypt klass GIFEncryptorByLSBMethod:

Steganograafia GIF-is

ja selle kood:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Sisendfail on tühi");
	}
	if (out == null) {
		throw new NullPointerException("Väljundfail on tühi");
	}
	if (text == null) {
		throw new NullPointerException("Tekst on tühi");
	}
	
	// loe baid sisendfailist
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// kontrolli formaati
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Sisendfailil on vale GIF formaat");
	}
	
	// loe värvipaleti suuruse omadus esimesest kolmest bitist 10. baitis failist
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// arvuta värvide arv ja võimalik sõnumi pikkus
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // üks bait kontrolliks ja üks bait sõnumi pikkuseks
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Tekst on liiga pikk");
	}
	
	int n = 13;
	
	// kirjuta kontrolljärjestus
	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++;
	}
	
	// kirjuta teksti pikkus
	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++;
	}
	
	// kirjuta sõnum
	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++;
		}
	}
	
	// kirjuta väljundfail
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Meetod ja selle lähtekood decrypt klass GIFEncryptorByLSBMethod:

Steganograafia GIF-is

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Sisendfail on tühi");
	}
	
	// loe baitid sisendfailist
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// kontrolli formaati
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Sisendfaili GIF formaat on vale");
	}
	
	// loe paleti suuruse omadus esimestest kolmest bittidest 10. baitides
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// arvuta värvide arv ja võimaliku sõnumi pikkus
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;	// üks bait kontrolliks ja üks bait sõnumi pikkuseks
	
	int n = 13;
	
	// loe kontrolljärjestus
	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("Pildis ei ole krüpteeritud sõnumit (Kontrolljärjestus on vale)");
	}
	
	// loe teksti pikkus
	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("Dekodeeritud teksti pikkus on väiksem kui 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Sõnumeid ei ole (Dekodeeritud sõnumi pikkus (" + textLength + ") on väiksem kui võimaliku sõnumi pikkus (" + possibleTextLength + "))");
	}
	
	// loe tekstibaitid ja tee tekstibaitide massiiv
	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);
}

Klassi rakendamine GIFEncryptorByPaletteExtensionMethod on sarnane, kuid erineb info salvestamise/lugemise meetodist.

Klassis MainFrame on kirjeldatud meetodid-„ümberpakendid“: encryptImage(Encryptor encryptor) ja decryptImage(Encryptor encryptor), mis töötlevad meetodite tulemusi ja suhtlevad kasutajaga, st avavad failide valimise dialooge, näitavad veateateid jne; samuti teised meetodid: Encryptor ja suhtlevad kasutajatega, avades näiteks failide valimise dialooge, näidates veateateid jms; samuti ka teised meetodid: openImage(), mis annab kasutajale võimaluse valida pildi, exit(), mis tagab rakendusest väljumise. Need meetodid kutsutakse esile vastava menüüpunkti Actionkaudu. Selles klassis on lisaks rakendatud ka abimeetodid: createComponents() — vormikomponentide loomine, loadImageFile(File f) — pildi laadimine spetsiaalsesse komponendisse failist. Klasside rakendamine GIFEncryptorByPaletteExtensionMethod on sarnane klassi rakendusele GIFEncryptorByLSBMethod, peamine erinevus seisneb sõnumi baitide lugemise ja salvestamise meetodis paleti kaudu.

Programmi töö

Meetod LBS

Oletame, et on selline pilt:

Steganograafia GIF-is

Antud pildis on paneel koostatud 256 värvist (nagu Paint säilitab). Esimesed neli värvi: valge, must, punane, roheline. Ülejäänud värvid on mustad. Globaalsete paneelide bittide järjekord on järgmine:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000

Steganograafia GIF-is

Pärast sõnumi sisestamist asendatakse allajoonitud bitid sõnumist pärit bittidega. Saadud pilt ei eristu originaalist peaaegu üldse.

Originaal
Sõnumit sisaldav pilt

Steganograafia GIF-is
Steganograafia GIF-is

Paleti laiendamise meetod

Avades pildi, kuhu see meetodiga sõnum paigutatud, võib avastada sellise pildi:

Steganograafia GIF-is

Mõistetavalt ei sobi selline meetod täiemahuliseks spioneerimiseks ja vajab võib-olla täiendavat sõnumi krüptimist.

Krüptimine/dekrüptimine animeeritud piltides töötab nagu tavalistes staatilistes piltides, samas ei häirita animatsiooni.

Kasutatud allikad:

Laadi alla:

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster