Steganography í GIF

Inngangur

Kveðjur.
Fyrir ekki svo löngu síðan, þegar ég var í háskólanámi, var námskeið í greininni „Hugbúnaðaraðferðir upplýsingaöryggis“. Verkefnið krafðist þess að við bjuggum til forrit sem fellir skilaboð í GIF skrár. Ég ákvað að gera það í Java.

Í þessari grein mun ég lýsa nokkrum fræðilegum atriðum, sem og hvernig þetta litla forrit varð til.

Bóklegur hluti

GIF snið

GIF (Graphics Interchange Format - snið til að skiptast á myndum) er snið til að geyma grafískar myndir, sem getur geymt þjöppuð gögn án þess að tapa gæðum á sniði sem er allt að 256 litir. Þetta snið var þróað árið 1987 (GIF87a) af CompuServe til að senda rastermyndir yfir netkerfi. Árið 1989 var sniðinu breytt (GIF89a), stuðningi við gagnsæi og hreyfimyndir bætt við.

GIF skrár hafa blokkbyggingu. Þessir kubbar hafa alltaf fasta lengd (eða það fer eftir einhverjum fánum), þannig að það er nánast ómögulegt að gera mistök um hvar hver kubbur er staðsettur. Uppbygging einföldustu GIF myndarinnar sem ekki er hreyfimynd á GIF89a sniði:

Steganography í GIF

Af öllum blokkum uppbyggingarinnar, í þessu tilfelli munum við hafa áhuga á alþjóðlegu litatöflublokkinni og breytunum sem bera ábyrgð á stikunni:

  • CT - tilvist alþjóðlegrar litatöflu. Ef þetta flagg er stillt verður alþjóðlega litatöfluna að byrja strax á eftir rökréttu skjáhandfanginu.
  • Size — litastærð og fjöldi lita á myndinni. Gildi fyrir þessa færibreytu:

Size
Fjöldi lita
Litatöflustærð, bæti

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Dulkóðunaraðferðir

Eftirfarandi aðferðir verða notaðar til að dulkóða skilaboð í myndskrám:

  • LSB (Least Significant Bit) aðferð
  • Aðferð til að bæta við litatöflu

LSB aðferð - algeng aðferð við stiganography. Það samanstendur af því að skipta út síðustu mikilvægu bitunum í ílátinu (í okkar tilfelli, alþjóðlegu litatöflubætin) fyrir bita falinna skilaboðanna.

Forritið mun nota síðustu tvo bitana í alþjóðlegu litatöflubætunum sem hluta af þessari aðferð. Þetta þýðir að fyrir 24-bita mynd, þar sem litapallettan er þrjú bæti fyrir rautt, blátt og grænt, eftir að skilaboð hafa verið felld inn í hana, mun hver litahluti að hámarki breytast um 3/255 stigbreytingar. Slík breyting verður í fyrsta lagi ósýnileg eða erfitt að sjá fyrir mannsauga og í öðru lagi verður hún ekki sýnileg á lággæða upplýsingaúttakstækjum.

Magn upplýsinga fer beint eftir stærð myndspjaldsins. Þar sem hámarksstærð litatöflunnar er 256 litir, og ef tveir skilaboðabitar eru skrifaðir inn í hluti hvers litar, þá er hámarkslengd skilaboða (með hámarksspjaldinu á myndinni) 192 bæti. Þegar skilaboðin eru felld inn í myndina breytist skráarstærðin ekki.

Palett stækkun aðferð, sem virkar aðeins fyrir GIF uppbyggingu. Það mun vera áhrifaríkast á myndum með lítilli litatöflu. Kjarni þess er að hún eykur stærð litatöflunnar og veitir þar með meira pláss til að skrifa nauðsynleg bæti í stað litabætisins. Ef við lítum svo á að lágmarksstærð litatöflunnar sé 2 litir (6 bæti), þá getur hámarksstærð innbyggðu skilaboðanna verið 256 × 3–6 = 762 bæti. Ókosturinn er lágt dulritunaröryggi; innfelldu skilaboðin er hægt að lesa með hvaða textaritli sem er ef skilaboðin hafa ekki verið háð viðbótar dulkóðun.

Hagnýt hluti

Hönnun dagskrár

Öll nauðsynleg verkfæri til að innleiða dulkóðunar- og afkóðunaralgrím verða innifalin í pakkanum com.tsarik.steganography. Þessi pakki inniheldur viðmótið Encryptor með aðferðum encrypt и decrypt, flokkur Binary, sem veitir möguleika á að vinna með bitafylki, sem og undantekningarflokka UnableToEncryptException и UnableToDecryptException, sem ætti að nota í viðmótsaðferðum Encryptor ef um er að ræða kóðun og umskráningarvillur í sömu röð.

Aðaldagskrárpakki com.tsarik.programs.gifed mun innihalda keyranlegan forritaflokk með kyrrstöðuaðferð main, sem gerir þér kleift að keyra forritið; flokkur sem geymir forritsfæribreytur; og pakka með öðrum flokkum.

Innleiðing reikniritanna sjálfra verður kynnt í pakkanum com.tsarik.programs.gifed.gif Flokkar GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Báðir þessir flokkar munu útfæra viðmótið Encryptor.

Byggt á uppbyggingu GIF sniðsins geturðu búið til almennt reiknirit til að koma skilaboðum inn í myndatöfluna:

Steganography í GIF

Til að ákvarða tilvist skilaboða í mynd er nauðsynlegt að bæta ákveðinni röð bita við upphaf skilaboðanna sem afkóðarinn les fyrst og athugar hvort rétt sé. Ef það passar ekki, þá er litið svo á að engin falin skilaboð séu í myndinni. Næst þarftu að tilgreina lengd skilaboðanna. Síðan texti skilaboðanna sjálfs.

Bekkjarmynd af öllu forritinu:

Steganography í GIF

Framkvæmd áætlunarinnar

Innleiðingu alls forritsins má skipta í tvo þætti: innleiðingu á dulkóðun viðmóts og afkóðunaraðferðir Encryptor, í bekkjum GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, og útfærslu notendaviðmótsins.

Hugleiddu bekkinn GIFEncryptorByLSBMethod.

Steganography í GIF

sviðum firstLSBit и secondLSBit innihalda fjölda bita af hverju bæti myndarinnar sem skilaboðin á að færa inn í og ​​þaðan sem skilaboðin á að lesa. Field checkSequence geymir athugunarbitaröð til að tryggja viðurkenningu á innbyggðu skilaboðunum. Statísk aðferð getEncryptingFileParameters skilar breytum tilgreindrar skráar og eiginleikum hugsanlegra skilaboða.

Aðferðaralgrím encrypt bekknum GIFEncryptorByLSBMethod:

Steganography í GIF

Og kóðinn þess:

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

Reiknirit og frumkóði aðferðarinnar decrypt bekknum GIFEncryptorByLSBMethod:

Steganography í GIF

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

Framkvæmd bekkjarins GIFEncryptorByPaletteExtensionMethod verður svipað, aðeins aðferðin við að vista/lesa upplýsingar er önnur.

Í tíma MainFrame Umbúðaaðferðum er lýst: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), vinna úr niðurstöðum viðmótsaðferða Encryptor og samskipti við notandann, þ.e. opna valglugga fyrir skrár, sýna villuboð o.s.frv.; auk annarra aðferða: openImage(), sem gerir notandanum kleift að velja mynd, exit(), sem fer úr forritinu. Þessar aðferðir eru kallaðar frá Actionsamsvarandi valmyndaratriði. Þessi flokkur útfærir að auki viðbótaraðferðir: createComponents() - að búa til formhluta, loadImageFile(File f) — hlaða mynd í sérstakan hluta úr skrá. Framkvæmd bekkjarins GIFEncryptorByPaletteExtensionMethod svipað og bekkjarútfærslan GIFEncryptorByLSBMethod, aðalmunurinn er í því hvernig skilaboðabæti eru skrifuð og lesin af stikunni.

Rekstur forrita

LBS aðferð

Segjum að það sé mynd eins og þessi:

Steganography í GIF

Á þessari mynd samanstendur pallettan af 256 litum (eins og Paint vistar). Fyrstu fjórir litirnir eru: hvítur, svartur, rauður, grænn. Aðrir litir eru svartir. Alþjóðlega litatöflubitaröðin verður sem hér segir:

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

Steganography í GIF

Þegar skilaboðin hafa verið felld inn verður undirstrikuðu bitunum skipt út fyrir bitana úr skilaboðunum. Myndin sem myndast er nánast ekkert frábrugðin upprunalegu myndinni.

Original
Mynd með innfelldum skilaboðum

Steganography í GIF
Steganography í GIF

Palett stækkun aðferð

Þegar þú opnar mynd sem inniheldur skilaboð með þessari aðferð muntu sjá eftirfarandi mynd:

Steganography í GIF

Það er ljóst að þessi aðferð mun ekki virka fyrir fullgilda njósnastarfsemi og gæti þurft viðbótar dulkóðun skilaboðanna.

Dulkóðun/afkóðun í hreyfimyndum virkar alveg eins og í venjulegum kyrrstæðum myndum, en hreyfimyndin er ekki biluð.

Heimildir notaðar:

Hlaða niður:

Heimild: www.habr.com

Bæta við athugasemd