Steganografia në GIF

Paraqitje

Përshëndetje.
Jo shumë kohë më parë, kur studioja në universitet, kishte një kurs në disiplinën "Metodat softuerike të sigurisë së informacionit". Detyra na kërkonte të krijonim një program që fut një mesazh në skedarët GIF. Vendosa ta bëj në Java.

Në këtë artikull do të përshkruaj disa pika teorike, si dhe se si u krijua ky program i vogël.

Pjesa teorike

Formati GIF

GIF (Graphics Interchange Format - një format për shkëmbimin e imazheve) është një format për ruajtjen e imazheve grafike, i aftë për të ruajtur të dhënat e kompresuara pa humbje të cilësisë në një format deri në 256 ngjyra. Ky format u zhvillua në vitin 1987 (GIF87a) nga CompuServe për transmetimin e imazheve raster përmes rrjeteve. Në 1989, formati u modifikua (GIF89a), u shtua mbështetje për transparencën dhe animacionin.

Skedarët GIF kanë një strukturë blloku. Këto blloqe kanë gjithmonë një gjatësi fikse (ose kjo varet nga disa flamuj), kështu që është pothuajse e pamundur të gabosh se ku ndodhet secili bllok. Struktura e imazhit më të thjeshtë GIF jo të animuar në formatin GIF89a:

Steganografia në GIF

Nga të gjitha blloqet e strukturës, në këtë rast do të jemi të interesuar për bllokun global të paletës dhe parametrat përgjegjës për paletën:

  • CT - prania e një palete globale. Nëse ky flamur është vendosur, paleta globale duhet të fillojë menjëherë pas dorezës logjike të ekranit.
  • Size — Madhësia e paletës dhe numri i ngjyrave në foto. Vlerat për këtë parametër:

Masat
Numri i ngjyrave
Madhësia e paletës, bajt

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Metodat e kriptimit

Metodat e mëposhtme do të përdoren për të enkriptuar mesazhet në skedarët e imazhit:

  • Metoda LSB (Bit më pak i rëndësishëm).
  • Metoda e shtimit të paletës

Metoda LSB - një metodë e zakonshme e steganografisë. Ai konsiston në zëvendësimin e pjesëve të fundit të rëndësishme në kontejner (në rastin tonë, bajtët e paletës globale) me pjesët e mesazhit të fshehur.

Programi do të përdorë dy bitet e fundit në bajtet e paletës globale si pjesë e kësaj metode. Kjo do të thotë që për një imazh 24-bitësh, ku ngjyra e paletës është tre bajt për të kuqe, blu dhe jeshile, pas futjes së një mesazhi në të, çdo komponent i ngjyrës do të ndryshojë me një maksimum prej 3/255 shkallëzime. Një ndryshim i tillë, së pari, do të jetë i padukshëm ose i vështirë për t'u vërejtur për syrin e njeriut, dhe së dyti, ai nuk do të jetë i dukshëm në pajisjet e daljes së informacionit me cilësi të ulët.

Sasia e informacionit do të varet drejtpërdrejt nga madhësia e paletës së imazhit. Meqenëse madhësia maksimale e paletës është 256 ngjyra, dhe nëse dy bit mesazhi janë shkruar në përbërësin e secilës ngjyrë, atëherë gjatësia maksimale e mesazhit (me paletën maksimale në imazh) është 192 bajt. Pasi mesazhi futet në imazh, madhësia e skedarit nuk ndryshon.

Metoda e zgjerimit të paletës, e cila funksionon vetëm për strukturën GIF. Do të jetë më efektive në imazhet me një gamë të vogël. Thelbi i tij është se rrit madhësinë e paletës, duke siguruar kështu hapësirë ​​shtesë për të shkruar bajtet e nevojshme në vend të bajteve të ngjyrave. Nëse marrim parasysh se madhësia minimale e paletës është 2 ngjyra (6 bajt), atëherë madhësia maksimale e mesazhit të ngulitur mund të jetë 256 × 3–6 = 762 bajt. Disavantazhi është siguria e ulët kriptografike; mesazhi i integruar mund të lexohet duke përdorur çdo redaktues teksti nëse mesazhi nuk i është nënshtruar enkriptimit shtesë.

Pjesë praktike

Dizajni i programit

Të gjitha mjetet e nevojshme për zbatimin e algoritmeve të kriptimit dhe deshifrimit do të përfshihen në paketë com.tsarik.steganography. Kjo paketë përfshin ndërfaqen Encryptor me metoda encrypt и decrypt, Klasa Binary, i cili ofron mundësinë për të punuar me vargje bit, si dhe me klasa përjashtimi UnableToEncryptException и UnableToDecryptException, e cila duhet të përdoret në metodat e ndërfaqes Encryptor në rast të gabimeve përkatësisht në kodim dhe dekodim.

Paketa kryesore e programit com.tsarik.programs.gifed do të përfshijë një klasë programi të ekzekutueshme me një metodë statike main, duke ju lejuar të ekzekutoni programin; një klasë që ruan parametrat e programit; dhe paketat me klasat e tjera.

Zbatimi i vetë algoritmeve do të paraqitet në paketë com.tsarik.programs.gifed.gif klasat GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Të dyja këto klasa do të zbatojnë ndërfaqen Encryptor.

Bazuar në strukturën e formatit GIF, mund të krijoni një algoritëm të përgjithshëm për futjen e një mesazhi në paletën e imazhit:

Steganografia në GIF

Për të përcaktuar praninë e një mesazhi në një imazh, është e nevojshme të shtoni një sekuencë të caktuar bitash në fillim të mesazhit, të cilat dekoderi i lexon së pari dhe kontrollon për korrektësinë. Nëse nuk përputhet, atëherë konsiderohet se nuk ka asnjë mesazh të fshehur në imazh. Më pas duhet të specifikoni gjatësinë e mesazhit. Pastaj vetë teksti i mesazhit.

Diagrami i klasës së të gjithë aplikacionit:

Steganografia në GIF

Zbatimi i programit

Zbatimi i të gjithë programit mund të ndahet në dy komponentë: zbatimi i metodave të kriptimit të ndërfaqes dhe deshifrimit Encryptor, në klasa GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, dhe zbatimin e ndërfaqes së përdoruesit.

Merrni parasysh klasën GIFEncryptorByLSBMethod.

Steganografia në GIF

fushat firstLSBit и secondLSBit përmbajnë numrat e biteve të secilit bajt të imazhit në të cilin duhet të futet mesazhi dhe nga ku duhet lexuar mesazhi. Fusha checkSequence ruan një sekuencë bit kontrolli për të siguruar njohjen e mesazhit të ngulitur. Metoda statike getEncryptingFileParameters kthen parametrat e skedarit të specifikuar dhe karakteristikat e mesazhit të mundshëm.

Algoritmi i metodës encrypt klasë GIFEncryptorByLSBMethod:

Steganografia në GIF

Dhe kodi i tij:

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

Algoritmi dhe kodi burim i metodës decrypt klasë GIFEncryptorByLSBMethod:

Steganografia në 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);
}

Zbatimi i klasës GIFEncryptorByPaletteExtensionMethod do të jenë të ngjashme, vetëm mënyra e ruajtjes/leximit të informacionit është e ndryshme.

Në klasë MainFrame Metodat e mbështjelljes janë përshkruar: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), duke përpunuar rezultatet e metodave të ndërfaqes Encryptor dhe ndërveprimi me përdoruesin, p.sh. hapja e një dialogu të përzgjedhjes së skedarit, shfaqja e mesazheve të gabimit, etj.; si dhe metoda të tjera: openImage(), duke i lejuar përdoruesit të zgjedhë një imazh, exit(), e cila del nga aplikacioni. Këto metoda thirren nga Actionartikujt përkatës të menusë. Kjo klasë zbaton gjithashtu metoda ndihmëse: createComponents() - krijimi i komponentëve të formës, loadImageFile(File f) — ngarkimi i një imazhi në një komponent të veçantë nga një skedar. Zbatimi i klasës GIFEncryptorByPaletteExtensionMethod ngjashëm me zbatimin e klasës GIFEncryptorByLSBMethod, ndryshimi kryesor është në mënyrën se si shkruhen dhe lexohen bytet e mesazheve nga paleta.

Funksionimi i programit

Metoda LBS

Le të themi se ka një imazh si ky:

Steganografia në GIF

Në këtë imazh, paleta përbëhet nga 256 ngjyra (siç ruan Paint). Katër ngjyrat e para janë: e bardhë, e zezë, e kuqe, jeshile. Ngjyrat e tjera janë të zeza. Sekuenca e biteve të paletës globale do të jetë si më poshtë:

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

Steganografia në GIF

Pasi mesazhi të jetë i integruar, bitet e nënvizuara do të zëvendësohen me bitet nga mesazhi. Imazhi që rezulton pothuajse nuk është i ndryshëm nga origjinali.

Origjinal
Imazhi me mesazh të ngulitur

Steganografia në GIF
Steganografia në GIF

Metoda e zgjerimit të paletës

Kur hapni një imazh që përmban një mesazh duke përdorur këtë metodë, do të shihni foton e mëposhtme:

Steganografia në GIF

Është e qartë se kjo metodë nuk do të funksionojë për aktivitete të plota spiunazhi dhe mund të kërkojë kriptim shtesë të mesazhit.

Kriptimi/deshifrimi në imazhet e animuara funksionon njësoj si në imazhet e zakonshme statike, por animacioni nuk është i prishur.

Burimet e përdorura:

Shkarko:

Burimi: www.habr.com

Shto një koment