GIF дээрх стеганографи

Танилцуулга

Сайн байцгаана уу.
Саяхан намайг их сургуульд сурч байхад “Мэдээллийн аюулгүй байдлын программ хангамжийн арга” гэсэн хичээл ордог байсан. Даалгаврын дагуу бид GIF файлд мессеж оруулах программ бүтээхийг шаардсан. Би үүнийг Java хэл дээр хийхээр шийдсэн.

Энэ нийтлэлд би энэ жижиг програмыг хэрхэн бүтээсэн талаар онолын зарим зүйлийг тайлбарлах болно.

Онолын хэсэг

GIF формат

GIF (Graphics Interchange Format - зураг солилцох формат) нь график дүрсийг хадгалах формат бөгөөд шахсан өгөгдлийг чанараа алдалгүйгээр 256 хүртэлх өнгөт форматаар хадгалах боломжтой. Энэ форматыг 1987 онд (GIF87a) CompuServe сүлжээгээр растер дүрс дамжуулах зорилгоор боловсруулсан. 1989 онд форматыг өөрчилсөн (GIF89a), ил тод байдал, хөдөлгөөнт дүрсийг дэмждэг.

GIF файлууд нь блок бүтэцтэй байдаг. Эдгээр блокууд нь үргэлж тогтмол урттай байдаг (эсвэл энэ нь зарим тугнаас хамаардаг) тул блок бүр хаана байрладаг талаар алдаа гаргах нь бараг боломжгүй юм. GIF89a форматтай хамгийн энгийн хөдөлгөөнгүй GIF зургийн бүтэц:

GIF дээрх стеганографи

Бүтцийн бүх блокуудаас энэ тохиолдолд бид дэлхийн палитр блок болон палитрыг хариуцах параметрүүдийг сонирхох болно.

  • CT - дэлхийн палитр байгаа эсэх. Хэрэв энэ тугийг тохируулсан бол глобал палитр нь логик дэлгэцийн бариулын дараа шууд эхлэх ёстой.
  • Size — палитрын хэмжээ, зураг дээрх өнгөний тоо. Энэ параметрийн утгууд:

Хэмжээ
Өнгөний тоо
Палетт хэмжээ, байт

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Шифрлэлтийн аргууд

Зургийн файл дахь мессежийг шифрлэхийн тулд дараах аргуудыг ашиглана.

  • LSB (Least Significant Bit) арга
  • Палетт нэмэх арга

LSB арга - стеганографийн нийтлэг арга. Энэ нь чингэлэг дэх хамгийн сүүлийн чухал битүүдийг (манай тохиолдолд дэлхийн палитрын байт) далд мессежийн битүүдээр солихоос бүрдэнэ.

Програм нь энэ аргын нэг хэсэг болгон дэлхийн палитрын байт дахь сүүлийн хоёр битийг ашиглах болно. Энэ нь өнгөт палитр нь улаан, хөх, ногоон гэсэн гурван байт хэмжээтэй 24 битийн зургийн хувьд мессеж оруулсны дараа өнгөний бүрэлдэхүүн хэсэг бүр дээд тал нь 3/255 градусаар өөрчлөгдөнө гэсэн үг юм. Ийм өөрчлөлт нь нэгдүгээрт, хүний ​​нүдэнд үл үзэгдэх эсвэл анзаарагдахад хэцүү байх, хоёрдугаарт, мэдээлэл дамжуулах чанар муутай төхөөрөмж дээр харагдахгүй болно.

Мэдээллийн хэмжээ нь зургийн палитрын хэмжээнээс шууд хамаарна. Палеттын хамгийн дээд хэмжээ нь 256 өнгө бөгөөд хэрэв өнгө бүрийн бүрэлдэхүүн хэсэгт хоёр мессежийн бит бичигдсэн бол мессежийн хамгийн их урт (зураг дээрх хамгийн их палитртай) 192 байт байна. Зурганд мессежийг оруулсны дараа файлын хэмжээ өөрчлөгдөхгүй.

Палитрыг өргөтгөх арга, энэ нь зөвхөн GIF бүтцэд л ажилладаг. Энэ нь жижиг палитр бүхий зураг дээр хамгийн үр дүнтэй байх болно. Үүний мөн чанар нь палитрын хэмжээг нэмэгдүүлж, улмаар өнгөт байтуудын оронд шаардлагатай байтыг бичих нэмэлт зай гаргаж өгдөгт оршино. Хэрэв бид палитрын хамгийн бага хэмжээ нь 2 өнгө (6 байт) гэж үзвэл суулгагдсан мессежийн хамгийн их хэмжээ нь 256 × 3-6 = 762 байт байж болно. Сул тал нь криптографийн хамгаалалт багатай, хэрэв зурваст нэмэлт шифрлэлт хийгдээгүй бол суулгагдсан мессежийг дурын текст засварлагч ашиглан уншиж болно.

Практик хэсэг

Хөтөлбөрийн дизайн

Шифрлэлт болон код тайлах алгоритмыг хэрэгжүүлэхэд шаардлагатай бүх хэрэгслийг багцад багтаасан болно com.tsarik.steganography. Энэ багц нь интерфейсийг агуулдаг Encryptor аргуудтай encrypt и decrypt, Анги Binary, энэ нь бит массив, мөн онцгой байдлын ангиудтай ажиллах боломжийг олгодог UnableToEncryptException и UnableToDecryptException, үүнийг интерфейсийн аргуудад ашиглах ёстой Encryptor кодлох болон тайлах алдаа тус тус гарсан тохиолдолд.

Програмын үндсэн багц com.tsarik.programs.gifed статик арга бүхий ажиллуулж болох програмын анги орно main, програмыг ажиллуулах боломжийг танд олгоно; програмын параметрүүдийг хадгалдаг анги; болон бусад ангиудтай багц.

Алгоритмуудын хэрэгжилтийг багцад танилцуулах болно com.tsarik.programs.gifed.gif ангиуд GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Эдгээр хоёр анги нь интерфэйсийг хэрэгжүүлэх болно Encryptor.

GIF форматын бүтцэд үндэслэн та зургийн палитрт мессеж оруулах ерөнхий алгоритмыг үүсгэж болно.

GIF дээрх стеганографи

Зурганд мессеж байгаа эсэхийг тодорхойлохын тулд мессежийн эхэнд битүүдийн тодорхой дарааллыг нэмэх шаардлагатай бөгөөд үүнийг декодлогч эхлээд уншиж, зөв ​​эсэхийг шалгана. Хэрэв энэ нь таарахгүй бол зурган дээр далд зурвас байхгүй гэж үзнэ. Дараа нь та мессежийн уртыг зааж өгөх хэрэгтэй. Дараа нь мессежийн текст өөрөө.

Бүх хэрэглээний ангийн диаграмм:

GIF дээрх стеганографи

Хөтөлбөрийн хэрэгжилт

Бүхэл бүтэн программын хэрэгжилтийг хоёр бүрэлдэхүүн хэсэгт хувааж болно: интерфейсийн шифрлэлт ба шифрийг тайлах аргуудыг хэрэгжүүлэх Encryptor, ангиудад GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod, мөн хэрэглэгчийн интерфэйсийн хэрэгжилт.

Ангиа бодоорой GIFEncryptorByLSBMethod.

GIF дээрх стеганографи

талбарууд firstLSBit и secondLSBit Зургийн байт бүрийн битийн тоог оруулах, мессежийг хаанаас уншихыг агуулна. Талбай checkSequence суулгагдсан мессежийг танихын тулд хяналтын битийн дарааллыг хадгалдаг. Статик арга getEncryptingFileParameters заасан файлын параметрүүд болон боломжит мессежийн шинж чанарыг буцаана.

Аргын алгоритм encrypt анги GIFEncryptorByLSBMethod:

GIF дээрх стеганографи

Мөн түүний код:

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

Аргын алгоритм ба эх код decrypt анги GIFEncryptorByLSBMethod:

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

Ангийн хэрэгжилт GIFEncryptorByPaletteExtensionMethod ижил төстэй байх болно, зөвхөн мэдээллийг хадгалах/унших арга нь өөр.

Ангид MainFrame боодлын аргуудыг тайлбарлав: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), интерфэйсийн аргуудын үр дүнг боловсруулах Encryptor мөн хэрэглэгчтэй харилцах, өөрөөр хэлбэл файл сонгох харилцах цонхыг нээх, алдааны мэдэгдлийг харуулах гэх мэт; түүнчлэн бусад аргууд: openImage(), хэрэглэгчдэд зураг сонгох боломжийг олгох, exit(), энэ нь програмаас гарах болно. Эдгээр аргуудыг -аас дууддаг Action-ийн харгалзах цэсийн зүйл. Энэ анги нь нэмэлт аргуудыг хэрэгжүүлдэг: createComponents() - маягтын бүрэлдэхүүн хэсгүүдийг бий болгох, loadImageFile(File f) — файлаас тусгай бүрэлдэхүүн хэсэг рүү зургийг ачаалах. Ангийн хэрэгжилт GIFEncryptorByPaletteExtensionMethod ангийн хэрэгжилттэй төстэй GIFEncryptorByLSBMethod, гол ялгаа нь палитраас мессежийн байт бичих, унших арга юм.

Програмын ажиллагаа

LBS арга

Ийм зураг байна гэж бодъё:

GIF дээрх стеганографи

Энэ зураг дээр палитр нь 256 өнгөнөөс бүрддэг (Paint хадгалдаг). Эхний дөрвөн өнгө нь: цагаан, хар, улаан, ногоон. Бусад өнгө нь хар өнгөтэй. Глобал палитрын битийн дараалал дараах байдалтай байна.

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

GIF дээрх стеганографи

Мессежийг суулгасны дараа доогуур зурсан битүүд нь мессежийн битүүдээр солигдоно. Үүссэн зураг нь анхныхаас бараг ялгаатай биш юм.

Эх
Суулгасан зурвас бүхий зураг

GIF дээрх стеганографи
GIF дээрх стеганографи

Палитрыг өргөтгөх арга

Та энэ аргыг ашиглан мессеж агуулсан зургийг нээхэд дараах зургийг харах болно.

GIF дээрх стеганографи

Энэ арга нь бүрэн хэмжээний тагнуулын үйл ажиллагаанд тохирохгүй нь тодорхой бөгөөд мессежийг нэмэлт шифрлэх шаардлагатай байж магадгүй юм.

Хөдөлгөөнт зураг дээрх шифрлэлт/шифр тайлах нь ердийн статик зурагтай адил ажилладаг боловч хөдөлгөөнт дүрс эвдэрсэнгүй.

Ашигласан эх сурвалжууд:

Татаж авах:

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх