GIF formatidagi steganografiya

kirish

Salomlar.
Yaqinda men universitetda o'qiyotganimda "Axborot xavfsizligini dasturiy ta'minot usullari" fanidan kurs ishi bor edi. Topshiriq bizdan xabarni GIF fayllariga joylashtiradigan dastur yaratishni talab qildi. Men buni Java-da qilishga qaror qildim.

Ushbu maqolada men ba'zi nazariy fikrlarni tasvirlab beraman, shuningdek, bu kichik dastur qanday yaratilgan.

Nazariy qism

GIF formati

GIF (Graphics Interchange Format - tasvirlarni almashish formati) - bu grafik tasvirlarni saqlash uchun format bo'lib, siqilgan ma'lumotlarni sifatini yo'qotmasdan 256 ranggacha formatda saqlashga qodir. Ushbu format 1987 yilda (GIF87a) CompuServe tomonidan rastrli tasvirlarni tarmoqlar orqali uzatish uchun ishlab chiqilgan. 1989 yilda format o'zgartirildi (GIF89a), shaffoflik va animatsiyani qo'llab-quvvatlash qo'shildi.

GIF fayllari blokli tuzilishga ega. Ushbu bloklar har doim qattiq uzunlikka ega (yoki bu ba'zi bayroqlarga bog'liq), shuning uchun har bir blokning qaerda joylashganligi haqida xato qilish deyarli mumkin emas. GIF89a formatidagi eng oddiy animatsiyasiz GIF tasvirining tuzilishi:

GIF formatidagi steganografiya

Strukturaning barcha bloklaridan, bu holda biz global palitra bloki va palitra uchun mas'ul bo'lgan parametrlarga qiziqamiz:

  • CT β€” global palitraning mavjudligi. Agar bu bayroq o'rnatilgan bo'lsa, global palitra mantiqiy ekran tutqichidan keyin darhol boshlanishi kerak.
  • Size β€” palitraning o'lchami va rasmdagi ranglar soni. Ushbu parametr uchun qiymatlar:

O'lcham
Ranglar soni
Palitra hajmi, baytlar

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Shifrlash usullari

Rasm fayllaridagi xabarlarni shifrlash uchun quyidagi usullar qo'llaniladi:

  • LSB (Least Significant Bit) usuli
  • Palitrani qo'shish usuli

LSB usuli - steganografiyaning keng tarqalgan usuli. U konteynerdagi oxirgi muhim bitlarni (bizning holimizda global palitra baytlari) yashirin xabarning bitlari bilan almashtirishdan iborat.

Dastur ushbu usulning bir qismi sifatida global palitradagi baytlardagi oxirgi ikki bitdan foydalanadi. Bu shuni anglatadiki, ranglar palitrasi qizil, ko'k va yashil ranglar uchun uch baytdan iborat bo'lgan 24 bitli tasvir uchun unga xabar kiritilgandan so'ng, har bir rang komponenti maksimal 3/255 gradatsiyaga o'zgaradi. Bunday o'zgarish, birinchidan, inson ko'ziga ko'rinmas yoki sezilishi qiyin bo'ladi, ikkinchidan, u past sifatli axborot chiqarish qurilmalarida ko'rinmaydi.

Ma'lumot miqdori to'g'ridan-to'g'ri tasvirlar palitrasining o'lchamiga bog'liq bo'ladi. Palitraning maksimal hajmi 256 rang bo'lganligi sababli va har bir rangning komponentiga ikkita xabar biti yozilsa, u holda maksimal xabar uzunligi (tasvirdagi maksimal palitra bilan) 192 baytni tashkil qiladi. Xabar rasmga kiritilgandan so'ng, fayl hajmi o'zgarmaydi.

Palitrani kengaytirish usuli, bu faqat GIF tuzilishi uchun ishlaydi. Kichkina palitrali tasvirlarda eng samarali bo'ladi. Uning mohiyati shundaki, u palitraning hajmini oshiradi va shu bilan rangli baytlar o'rniga kerakli baytlarni yozish uchun qo'shimcha joy beradi. Agar palitraning minimal hajmi 2 rang (6 bayt) ekanligini hisobga olsak, u holda o'rnatilgan xabarning maksimal hajmi 256 Γ— 3-6 = 762 bayt bo'lishi mumkin. Kamchilik - kriptografik xavfsizlikning pastligi; agar xabar qo'shimcha shifrlashdan o'tkazilmagan bo'lsa, o'rnatilgan xabarni istalgan matn muharriri yordamida o'qish mumkin.

Amaliy qism

Dastur dizayni

Paketga shifrlash va shifrni ochish algoritmlarini amalga oshirish uchun barcha zarur vositalar kiritiladi com.tsarik.steganography. Ushbu paket interfeysni o'z ichiga oladi Encryptor usullari bilan encrypt ΠΈ decrypt, Sinf Binary, bu bit massivlari, shuningdek istisno sinflari bilan ishlash qobiliyatini ta'minlaydi UnableToEncryptException ΠΈ UnableToDecryptException, bu interfeys usullarida qo'llanilishi kerak Encryptor mos ravishda kodlash va dekodlash xatolari bo'lsa.

Asosiy dastur to'plami com.tsarik.programs.gifed statik usul bilan ishlaydigan dastur sinfini o'z ichiga oladi main, dasturni ishga tushirish imkonini beruvchi; dastur parametrlarini saqlaydigan sinf; va boshqa sinflar bilan paketlar.

Algoritmlarning o'zlari amalga oshirilishi paketda taqdim etiladi com.tsarik.programs.gifed.gif sinflar GIFEncryptorByLSBMethod ΠΈ GIFEncryptorByPaletteExtensionMethod. Ushbu sinflarning ikkalasi ham interfeysni amalga oshiradi Encryptor.

GIF formatining tuzilishiga asoslanib, siz tasvirlar palitrasiga xabar kiritishning umumiy algoritmini yaratishingiz mumkin:

GIF formatidagi steganografiya

Tasvirda xabar mavjudligini aniqlash uchun xabarning boshiga ma'lum bir ketma-ketlikni qo'shish kerak, bu dekoder birinchi bo'lib o'qiydi va to'g'riligini tekshiradi. Agar u mos kelmasa, rasmda yashirin xabar yo'q deb hisoblanadi. Keyin xabarning uzunligini belgilashingiz kerak. Keyin xabarning o'zi.

Butun ilovaning sinf diagrammasi:

GIF formatidagi steganografiya

Dasturni amalga oshirish

Butun dasturni amalga oshirishni ikkita komponentga bo'lish mumkin: interfeysni shifrlash va shifrni ochish usullarini amalga oshirish Encryptor, sinflarda GIFEncryptorByLSBMethod ΠΈ GIFEncryptorByPaletteExtensionMethod, va foydalanuvchi interfeysini amalga oshirish.

Sinfni ko'rib chiqing GIFEncryptorByLSBMethod.

GIF formatidagi steganografiya

sohalar firstLSBit ΠΈ secondLSBit xabar kiritilishi kerak bo'lgan va xabarni o'qilishi kerak bo'lgan tasvirning har bir baytining bit raqamlarini o'z ichiga oladi. Maydon checkSequence o'rnatilgan xabarning tan olinishini ta'minlash uchun chek bit ketma-ketligini saqlaydi. Statik usul getEncryptingFileParameters belgilangan fayl parametrlarini va potentsial xabarning xususiyatlarini qaytaradi.

Usul algoritmi encrypt daraja GIFEncryptorByLSBMethod:

GIF formatidagi steganografiya

Va uning kodi:

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

Usulning algoritmi va manba kodi decrypt daraja GIFEncryptorByLSBMethod:

GIF formatidagi steganografiya

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

Sinfni amalga oshirish GIFEncryptorByPaletteExtensionMethod o'xshash bo'ladi, faqat ma'lumotni saqlash/o'qish usuli boshqacha.

Sinfda MainFrame o'rash usullari tavsiflanadi: encryptImage(Encryptor encryptor) ΠΈ decryptImage(Encryptor encryptor), interfeys usullari natijalarini qayta ishlash Encryptor va foydalanuvchi bilan muloqot qilish, ya'ni fayl tanlash dialogini ochish, xato xabarlarini ko'rsatish va h.k.; shuningdek, boshqa usullar: openImage(), foydalanuvchiga rasm tanlashga ruxsat berish, exit(), bu ilovadan chiqadi. Ushbu usullar dan chaqiriladi Actionning tegishli menyu elementlari. Ushbu sinf qo'shimcha ravishda yordamchi usullarni amalga oshiradi: createComponents() - shakl komponentlarini yaratish; loadImageFile(File f) β€” rasmni fayldan maxsus komponentga yuklash. Sinfni amalga oshirish GIFEncryptorByPaletteExtensionMethod sinfni amalga oshirishga o'xshaydi GIFEncryptorByLSBMethod, asosiy farq xabar baytlarini yozish va palitradan o'qish usulida.

Dasturning ishlashi

LBS usuli

Aytaylik, shunday tasvir bor:

GIF formatidagi steganografiya

Ushbu rasmda palitra 256 rangdan iborat (Bo'yoq saqlaganidek). Birinchi to'rtta rang: oq, qora, qizil, yashil. Boshqa ranglar qora. Global palitraning bit ketma-ketligi quyidagicha bo'ladi:

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

GIF formatidagi steganografiya

Xabar o'rnatilgandan so'ng, tagiga chizilgan bitlar xabarning bitlari bilan almashtiriladi. Olingan tasvir asl nusxadan deyarli farq qilmaydi.

Original
O'rnatilgan xabarli rasm

GIF formatidagi steganografiya
GIF formatidagi steganografiya

Palitrani kengaytirish usuli

Ushbu usul yordamida xabarni o'z ichiga olgan rasmni ochganingizda, siz quyidagi rasmni ko'rasiz:

GIF formatidagi steganografiya

Bu usul toβ€˜laqonli josuslik faoliyati uchun ishlamasligi aniq va xabarni qoβ€˜shimcha shifrlashni talab qilishi mumkin.

Animatsiyalangan tasvirlardagi shifrlash/shifrni ochish oddiy statik tasvirlardagi kabi ishlaydi, lekin animatsiya buzilmaydi.

Foydalanilgan manbalar:

Yuklash:

Manba: www.habr.com

a Izoh qo'shish