إخفاء المعلومات في 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 بايت. العيب هو انخفاض أمان التشفير ؛ يمكنك قراءة الرسالة المضمنة باستخدام أي محرر نصوص إذا لم تخضع الرسالة لتشفير إضافي.

الجزء العملي

تصميم البرنامج

ستكون جميع الأدوات اللازمة لتنفيذ خوارزميات التشفير وفك التشفير في الحزمة 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 لونًا (كما يحفظ الرسام). الألوان الأربعة الأولى هي الأبيض والأسود والأحمر والأخضر. باقي الألوان سوداء. سيكون تسلسل البت للوحة العمومية كما يلي:

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

إخفاء المعلومات في GIF

بعد تضمين الرسالة ، سيتم استبدال البتات التي تحتها خط بالبتات من الرسالة. يكاد لا يمكن تمييز الصورة الناتجة عن الصورة الأصلية.

أصلي
صورة مع رسالة مضمنة

إخفاء المعلومات في GIF
إخفاء المعلومات في GIF

طريقة تمديد لوحة

بعد فتح الصورة التي توضع فيها الرسالة وفق هذه الطريقة ، يمكنك أن تجد الصورة التالية:

إخفاء المعلومات في GIF

من الواضح أن هذه الطريقة لن تعمل مع أنشطة التجسس الكاملة ، وقد تتطلب تشفيرًا إضافيًا للرسالة.

يعمل التشفير / فك التشفير في الصور المتحركة تمامًا مثل الصور الثابتة العادية ، ولا يتم كسر الرسوم المتحركة.

المصادر المستخدمة:

تحميل:

المصدر: www.habr.com

إضافة تعليق