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