Steganogrāfija GIF formātā

Ievads

Sveicieni.
Ne tik sen, kad mācÄ«jos universitātē, bija kursa darbs disciplÄ«nā ā€œInformācijas droŔības programmatÅ«ras metodesā€. Lai veiktu uzdevumu, mums bija jāizveido programma, kas iegultu ziņojumu GIF failos. Es nolēmu to darÄ«t Java.

Å ajā rakstā es aprakstÄ«Å”u dažus teorētiskos punktus, kā arÄ« to, kā Ŕī mazā programma tika izveidota.

Teorētiskā daļa

GIF formātā

GIF (Graphics Interchange Format ā€” attēlu apmaiņas formāts) ir grafisko attēlu glabāŔanas formāts, kas spēj saglabāt saspiestus datus, nezaudējot kvalitāti, formātā lÄ«dz 256 krāsām. Å o formātu 1987. gadā (GIF87a) izstrādāja CompuServe rastra attēlu pārsÅ«tÄ«Å”anai pa tÄ«kliem. 1989. gadā formāts tika modificēts (GIF89a), tika pievienots caurspÄ«dÄ«guma un animācijas atbalsts.

GIF failiem ir bloku struktÅ«ra. Å iem blokiem vienmēr ir noteikts garums (vai tas ir atkarÄ«gs no dažiem karogiem), tāpēc ir gandrÄ«z neiespējami kļūdÄ«ties, kur katrs bloks atrodas. VienkārŔākā neanimētā GIF attēla struktÅ«ra GIF89a formātā:

Steganogrāfija GIF formātā

No visiem struktÅ«ras blokiem Å”ajā gadÄ«jumā mÅ«s interesēs globālais paletes bloks un parametri, kas ir atbildÄ«gi par paleti:

  • CT ā€” globālās paletes klātbÅ«tne. Ja Å”is karodziņŔ ir iestatÄ«ts, globālajai paletei jāsākas tÅ«lÄ«t aiz loÄ£iskā ekrāna roktura.
  • Size ā€” paletes izmērs un krāsu skaits attēlā. Å Ä« parametra vērtÄ«bas:

Izmēri
Krāsu skaits
Paletes izmērs, baiti

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

ŠifrēŔanas metodes

Lai Å”ifrētu ziņojumus attēlu failos, tiks izmantotas Ŕādas metodes:

  • LSB (least Significant Bit) metode
  • Paletes pievienoÅ”anas metode

LSB metode - izplatÄ«ta steganogrāfijas metode. Tas sastāv no pēdējo nozÄ«mÄ«go bitu aizstāŔanas konteinerā (mÅ«su gadÄ«jumā globālo paletes baitu) ar slēptā ziņojuma bitiem.

Programma izmantos pēdējos divus bitus globālās paletes baitos kā daļu no Ŕīs metodes. Tas nozÄ«mē, ka 24 bitu attēlam, kura krāsu palete ir trÄ«s baiti sarkanai, zilai un zaļai krāsai, pēc ziņojuma iegulÅ”anas tajā katra krāsu sastāvdaļa mainÄ«sies ne vairāk kā par 3/255 gradācijām. Šādas izmaiņas, pirmkārt, bÅ«s neredzamas vai grÅ«ti pamanāmas cilvēka acij, otrkārt, tās nebÅ«s redzamas nekvalitatÄ«vās informācijas izvades ierÄ«cēs.

Informācijas apjoms bÅ«s tieÅ”i atkarÄ«gs no attēla paletes izmēra. Tā kā paletes maksimālais izmērs ir 256 krāsas un, ja katras krāsas komponentā ir ierakstÄ«ti divi ziņojuma biti, tad maksimālais ziņojuma garums (ar maksimālo paleti attēlā) ir 192 baiti. Kad ziņojums ir iegults attēlā, faila lielums nemainās.

Paletes paplaÅ”ināŔanas metode, kas darbojas tikai GIF struktÅ«rai. Tas bÅ«s visefektÄ«vākais attēliem ar nelielu paleti. Tās bÅ«tÄ«ba ir tāda, ka tas palielina paletes izmēru, tādējādi nodroÅ”inot papildu vietu nepiecieÅ”amo baitu rakstÄ«Å”anai krāsu baitu vietā. Ja ņemam vērā, ka minimālais paletes izmērs ir 2 krāsas (6 baiti), tad iegultā ziņojuma maksimālais izmērs var bÅ«t 256 Ɨ 3ā€“6 = 762 baiti. TrÅ«kums ir zemā kriptogrāfiskā droŔība; iegulto ziņojumu var lasÄ«t, izmantojot jebkuru teksta redaktoru, ja ziņojums nav pakļauts papildu Å”ifrÄ“Å”anai.

Praktiskā daļa

Programmas dizains

Visi nepiecieÅ”amie rÄ«ki Å”ifrÄ“Å”anas un atÅ”ifrÄ“Å”anas algoritmu ievieÅ”anai tiks iekļauti komplektā com.tsarik.steganography. Å ajā pakotnē ir iekļauts interfeiss Encryptor ar metodēm encrypt Šø decrypt, Klase Binary, kas nodroÅ”ina iespēju strādāt ar bitu masÄ«viem, kā arÄ« izņēmumu klasēm UnableToEncryptException Šø UnableToDecryptException, kas jāizmanto saskarnes metodēs Encryptor attiecÄ«gi kodÄ“Å”anas un dekodÄ“Å”anas kļūdu gadÄ«jumā.

Galvenā programmu pakete com.tsarik.programs.gifed ietvers darbināmu programmu klasi ar statisku metodi main, kas ļauj palaist programmu; klase, kas glabā programmas parametrus; un paketes ar citām klasēm.

PaÅ”u algoritmu ievieÅ”ana tiks prezentēta komplektā com.tsarik.programs.gifed.gif klases GIFEncryptorByLSBMethod Šø GIFEncryptorByPaletteExtensionMethod. Abas Ŕīs klases ieviesÄ«s saskarni Encryptor.

Pamatojoties uz GIF formāta struktÅ«ru, varat izveidot vispārÄ«gu algoritmu ziņojuma ievadÄ«Å”anai attēlu paletē:

Steganogrāfija GIF formātā

Lai noteiktu ziņojuma esamību attēlā, ziņojuma sākumam jāpievieno noteikta bitu secība, kuru dekodētājs vispirms nolasa un pārbauda pareizību. Ja nesakrīt, tad tiek uzskatīts, ka attēlā nav slēpta ziņojuma. Tālāk jums jānorāda ziņojuma garums. Pēc tam pats ziņojuma teksts.

Visa lietojumprogrammas klases diagramma:

Steganogrāfija GIF formātā

Programmas īstenoŔana

Visas programmas ievieÅ”anu var iedalÄ«t divās daļās: saskarnes Å”ifrÄ“Å”anas un atÅ”ifrÄ“Å”anas metožu ievieÅ”ana Encryptor, nodarbÄ«bās GIFEncryptorByLSBMethod Šø GIFEncryptorByPaletteExtensionMethodun lietotāja interfeisa ievieÅ”anu.

Apsveriet klasi GIFEncryptorByLSBMethod.

Steganogrāfija GIF formātā

lauki firstLSBit Šø secondLSBit satur katra attēla baita bitu skaitu, kurā jāievada ziņojums un no kurienes ziņojums jālasa. Lauks checkSequence saglabā pārbaudes bitu secÄ«bu, lai nodroÅ”inātu iegultā ziņojuma atpazÄ«Å”anu. Statiskā metode getEncryptingFileParameters atgriež norādÄ«tā faila parametrus un potenciālā ziņojuma raksturlielumus.

Metodes algoritms encrypt klase GIFEncryptorByLSBMethod:

Steganogrāfija GIF formātā

Un viņa kods:

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

Metodes algoritms un pirmkods decrypt klase GIFEncryptorByLSBMethod:

Steganogrāfija GIF formātā

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

Klases īstenoŔana GIFEncryptorByPaletteExtensionMethod būs līdzīgi, atŔķiras tikai informācijas saglabāŔanas/nolasīŔanas metode.

Klasē MainFrame AprakstÄ«tas iesaiņoÅ”anas metodes: encryptImage(Encryptor encryptor) Šø decryptImage(Encryptor encryptor), apstrādājot interfeisa metožu rezultātus Encryptor un mijiedarbÄ«ba ar lietotāju, t.i., failu atlases dialoga atvērÅ”ana, kļūdu ziņojumu parādÄ«Å”ana utt.; kā arÄ« citas metodes: openImage(), ļaujot lietotājam izvēlēties attēlu, exit(), kas aizver lietojumprogrammu. Å Ä«s metodes tiek sauktas no ActionatbilstoÅ”os izvēlnes vienumus. Å Ä« klase papildus ievieÅ” palÄ«gmetodes: createComponents() - formas sastāvdaļu izveide, loadImageFile(File f) ā€” attēla ielāde Ä«paŔā komponentā no faila. Klases Ä«stenoÅ”ana GIFEncryptorByPaletteExtensionMethod lÄ«dzÄ«gi klases ievieÅ”anai GIFEncryptorByLSBMethod, galvenā atŔķirÄ«ba ir veidā, kādā ziņojuma baiti tiek rakstÄ«ti un nolasÄ«ti no paletes.

Programmas darbība

LBS metode

Pieņemsim, ka ir Ŕāds attēls:

Steganogrāfija GIF formātā

Å ajā attēlā palete sastāv no 256 krāsām (kā Paint saglabā). Pirmās četras krāsas ir: balta, melna, sarkana, zaļa. Citas krāsas ir melnas. Globālās paletes bitu secÄ«ba bÅ«s Ŕāda:

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

Steganogrāfija GIF formātā

Kad ziņojums ir iegults, pasvÄ«trotie biti tiks aizstāti ar ziņojuma bitiem. IegÅ«tais attēls gandrÄ«z neatŔķiras no oriÄ£ināla.

Oriģināls
Attēls ar iegultu ziņojumu

Steganogrāfija GIF formātā
Steganogrāfija GIF formātā

Paletes paplaŔināŔanas metode

Atverot attēlu, kurā ir ziņojums, izmantojot Å”o metodi, tiks parādÄ«ts Ŕāds attēls:

Steganogrāfija GIF formātā

Ir skaidrs, ka Ŕī metode nedarbosies pilnvērtÄ«gām spiegoÅ”anas darbÄ«bām, un tai var bÅ«t nepiecieÅ”ama papildu ziņojuma Å”ifrÄ“Å”ana.

Å ifrÄ“Å”ana/atÅ”ifrÄ“Å”ana animētos attēlos darbojas tāpat kā parastajos statiskajos attēlos, taču animācija nav bojāta.

Izmantotie avoti:

Lejupielādēt:

Avots: www.habr.com

Pievieno komentāru