استگانوگرافی در GIF

معرفی

Приветствую
چندی پیش، زمانی که در دانشگاه تحصیل می کردم، یک دوره آموزشی در رشته "روش های نرم افزاری امنیت اطلاعات" وجود داشت. این تکلیف ما را ملزم به ایجاد برنامه ای می کرد که پیامی را در فایل های GIF جاسازی می کند. تصمیم گرفتم این کار را در جاوا انجام دهم.

در این مقاله برخی از نکات نظری و همچنین نحوه ایجاد این برنامه کوچک را شرح خواهم داد.

قسمت نظری

فرمت GIF

GIF (فرمت تبادل گرافیکی - فرمتی برای تبادل تصاویر) فرمتی برای ذخیره تصاویر گرافیکی است که می تواند داده های فشرده شده را بدون افت کیفیت در فرمت تا 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

اضافه کردن نظر