Стеганография в GIF

въведение

Поздрави.
Не толкова отдавна, когато учех в университета, имаше курсова работа по дисциплината „Софтуерни методи за информационна сигурност“. Заданието изискваше да създадем програма, която вгражда съобщение в GIF файлове. Реших да го направя в Java.

В тази статия ще опиша някои теоретични точки, както и как е създадена тази малка програма.

Теоретична част

GIF формат

GIF (Graphics Interchange Format - формат за обмен на изображения) е формат за съхранение на графични изображения, способен да съхранява компресирани данни без загуба на качество във формат до 256 цвята. Този формат е разработен през 1987 г. (GIF87a) от CompuServe за предаване на растерни изображения по мрежи. През 1989 г. форматът е модифициран (GIF89a), добавена е поддръжка за прозрачност и анимация.

GIF файловете имат блокова структура. Тези блокове винаги имат фиксирана дължина (или това зависи от някои флагове), така че е почти невъзможно да се направи грешка къде се намира всеки блок. Структурата на най-простото неанимирано GIF изображение във формат GIF89a:

Стеганография в 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 (най-малък значим бит).
  • Метод за добавяне на палитри

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

Добавяне на нов коментар