การอำพรางใน 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 (บิตที่มีนัยสำคัญน้อยที่สุด)
  • วิธีการเพิ่มจานสี

วิธีแอลเอสบี - วิธีการทั่วไปในการสุริยคติ ประกอบด้วยการแทนที่บิตสำคัญสุดท้ายในคอนเทนเนอร์ (ในกรณีของเราคือไบต์ของจานสีส่วนกลาง) ด้วยบิตของข้อความที่ซ่อนอยู่

โปรแกรมจะใช้สองบิตสุดท้ายในไบต์ของจานสีส่วนกลางซึ่งเป็นส่วนหนึ่งของวิธีนี้ ซึ่งหมายความว่าสำหรับรูปภาพ 24 บิต โดยที่ชุดสีคือ 3 ไบต์สำหรับสีแดง น้ำเงิน และเขียว หลังจากฝังข้อความลงไปแล้ว ส่วนประกอบแต่ละสีจะเปลี่ยนไปตามการไล่ระดับสูงสุด 255/XNUMX การเปลี่ยนแปลงดังกล่าว ประการแรกจะมองไม่เห็นหรือสังเกตเห็นได้ยากด้วยตามนุษย์ และประการที่สอง จะไม่สามารถมองเห็นได้บนอุปกรณ์ส่งออกข้อมูลคุณภาพต่ำ

จำนวนข้อมูลจะขึ้นอยู่กับขนาดของจานสีโดยตรง เนื่องจากขนาดสูงสุดของพาเล็ตคือ 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ความแตกต่างหลักอยู่ที่วิธีการเขียนและอ่านไบต์ของข้อความจากพาเล็ต

การทำงานของโปรแกรม

วิธีปอนด์

สมมติว่ามีภาพเช่นนี้:

การอำพรางใน GIF

ในภาพนี้ จานสีประกอบด้วย 256 สี (ในขณะที่ Paint บันทึก) สี่สีแรกคือ: ขาว, ดำ, แดง, เขียว สีอื่นๆเป็นสีดำ ลำดับบิตของจานสีส่วนกลางจะเป็นดังนี้:

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

การอำพรางใน GIF

เมื่อข้อความถูกฝัง บิตที่ขีดเส้นใต้จะถูกแทนที่ด้วยบิตจากข้อความ ภาพที่ได้แทบไม่แตกต่างจากต้นฉบับเลย

ดั้งเดิม
รูปภาพพร้อมข้อความที่ฝังไว้

การอำพรางใน GIF
การอำพรางใน GIF

วิธีการขยายจานสี

เมื่อคุณเปิดรูปภาพที่มีข้อความโดยใช้วิธีนี้ คุณจะเห็นรูปภาพต่อไปนี้:

การอำพรางใน GIF

เห็นได้ชัดว่าวิธีนี้ใช้ไม่ได้กับกิจกรรมการจารกรรมเต็มรูปแบบ และอาจต้องมีการเข้ารหัสข้อความเพิ่มเติม

การเข้ารหัส/ถอดรหัสในภาพเคลื่อนไหวทำงานเหมือนกับภาพนิ่งทั่วไป แต่ภาพเคลื่อนไหวจะไม่เสียหาย

แหล่งที่มาที่ใช้:

ดาวน์โหลด:

ที่มา: will.com

เพิ่มความคิดเห็น