Steganography trong GIF

Giới thiệu

Hoan nghênh.
Cách đây không lâu, khi tôi đang học đại học có môn học “Phương pháp phần mềm bảo mật thông tin”. Bài tập yêu cầu chúng tôi tạo một chương trình nhúng tin nhắn vào tệp GIF. Tôi quyết định làm điều đó bằng Java.

Trong bài viết này tôi sẽ mô tả một số điểm lý thuyết cũng như cách tạo ra chương trình nhỏ này.

Phần lý thuyết

định dạng GIF

GIF (Graphics Interchange Format - định dạng trao đổi hình ảnh) là định dạng lưu trữ hình ảnh đồ họa, có khả năng lưu trữ dữ liệu nén mà không làm giảm chất lượng ở định dạng lên tới 256 màu. Định dạng này được CompuServe phát triển vào năm 1987 (GIF87a) để truyền hình ảnh raster qua mạng. Năm 1989, định dạng đã được sửa đổi (GIF89a), hỗ trợ độ trong suốt và hoạt ảnh được thêm vào.

Tệp GIF có cấu trúc khối. Các khối này luôn có độ dài cố định (hoặc phụ thuộc vào một số cờ), do đó gần như không thể nhầm lẫn về vị trí của từng khối. Cấu trúc của ảnh GIF không động đơn giản nhất ở định dạng GIF89a:

Steganography trong GIF

Trong tất cả các khối của cấu trúc, trong trường hợp này chúng ta sẽ quan tâm đến khối bảng màu chung và các tham số chịu trách nhiệm về bảng màu:

  • CT - sự hiện diện của một bảng màu toàn cầu. Nếu cờ này được đặt, bảng màu chung phải bắt đầu ngay sau bộ điều khiển màn hình logic.
  • Size - kích thước bảng màu và số lượng màu trong hình. Các giá trị cho tham số này:

Kích thước máy
Số lượng màu sắc
Kích thước bảng màu, byte

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Phương pháp mã hóa

Các phương pháp sau sẽ được sử dụng để mã hóa tin nhắn trong tệp hình ảnh:

  • Phương pháp LSB (Bit quan trọng nhất)
  • Phương pháp bổ sung bảng màu

phương pháp LSB - một phương pháp giấu tin phổ biến. Nó bao gồm việc thay thế các bit quan trọng cuối cùng trong vùng chứa (trong trường hợp của chúng tôi là các byte bảng màu chung) bằng các bit của thông điệp ẩn.

Chương trình sẽ sử dụng hai bit cuối cùng trong byte bảng màu chung như một phần của phương pháp này. Điều này có nghĩa là đối với hình ảnh 24 bit, trong đó bảng màu gồm ba byte cho màu đỏ, xanh lam và xanh lục, sau khi nhúng thông báo vào đó, mỗi thành phần màu sẽ thay đổi tối đa 3/255 mức tăng dần. Sự thay đổi như vậy, thứ nhất, sẽ vô hình hoặc khó nhận thấy bằng mắt người, thứ hai, nó sẽ không nhìn thấy được trên các thiết bị đầu ra thông tin chất lượng thấp.

Lượng thông tin sẽ phụ thuộc trực tiếp vào kích thước của bảng hình ảnh. Vì kích thước tối đa của bảng màu là 256 màu và nếu hai bit thông báo được ghi vào thành phần của mỗi màu thì độ dài thông báo tối đa (với bảng màu tối đa trong ảnh) là 192 byte. Khi tin nhắn được nhúng vào hình ảnh, kích thước tệp sẽ không thay đổi.

Phương pháp mở rộng bảng màu, chỉ hoạt động với cấu trúc GIF. Nó sẽ hiệu quả nhất trên những hình ảnh có bảng màu nhỏ. Bản chất của nó là tăng kích thước của bảng màu, từ đó cung cấp thêm không gian để ghi các byte cần thiết thay cho các byte màu. Nếu chúng ta coi kích thước tối thiểu của bảng màu là 2 màu (6 byte), thì kích thước tối đa của thông báo được nhúng có thể là 256 × 3–6 = 762 byte. Nhược điểm là độ bảo mật bằng mật mã thấp; tin nhắn được nhúng có thể được đọc bằng bất kỳ trình soạn thảo văn bản nào nếu tin nhắn chưa được mã hóa bổ sung.

Phần thực hành

Thiết kế chương trình

Tất cả các công cụ cần thiết để thực hiện các thuật toán mã hóa và giải mã sẽ được bao gồm trong gói com.tsarik.steganography. Gói này bao gồm giao diện Encryptor với các phương pháp encrypt и decrypt, Lớp học Binary, cung cấp khả năng làm việc với mảng bit, cũng như các lớp ngoại lệ UnableToEncryptException и UnableToDecryptException, nên được sử dụng trong các phương thức giao diện Encryptor trong trường hợp có lỗi mã hóa và giải mã tương ứng.

Gói chương trình chính com.tsarik.programs.gifed sẽ bao gồm một lớp chương trình có thể chạy được với một phương thức tĩnh main, cho phép bạn chạy chương trình; một lớp lưu trữ các tham số của chương trình; và các gói với các lớp khác.

Việc thực hiện các thuật toán sẽ được trình bày trong gói com.tsarik.programs.gifed.gif các lớp học GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Cả hai lớp này sẽ thực hiện giao diện Encryptor.

Dựa trên cấu trúc của định dạng GIF, bạn có thể tạo một thuật toán chung để đưa thông điệp vào bảng hình ảnh:

Steganography trong GIF

Để xác định sự hiện diện của thông báo trong hình ảnh, cần phải thêm một chuỗi bit nhất định vào đầu thông báo, bộ giải mã sẽ đọc đầu tiên và kiểm tra tính chính xác. Nếu không khớp thì coi như không có thông điệp ẩn trong ảnh. Tiếp theo bạn cần chỉ định độ dài của tin nhắn. Sau đó là nội dung của tin nhắn.

Sơ đồ lớp của toàn bộ ứng dụng:

Steganography trong GIF

Thực hiện chương trình

Việc thực hiện toàn bộ chương trình có thể được chia thành hai thành phần: thực hiện các phương pháp mã hóa và giải mã giao diện Encryptor, Trong các lớp học GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethodvà việc triển khai giao diện người dùng.

Hãy xem xét lớp học GIFEncryptorByLSBMethod.

Steganography trong GIF

Lĩnh vực firstLSBit и secondLSBit chứa số bit của mỗi byte của hình ảnh mà tin nhắn sẽ được nhập vào và từ đó tin nhắn sẽ được đọc. Cánh đồng checkSequence lưu trữ chuỗi bit kiểm tra để đảm bảo nhận dạng tin nhắn được nhúng. Phương pháp tĩnh getEncryptingFileParameters trả về các tham số của tệp đã chỉ định và các đặc điểm của thông báo tiềm năng.

Thuật toán phương pháp encrypt lớp GIFEncryptorByLSBMethod:

Steganography trong GIF

Và mã của anh ấy:

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

Thuật toán và mã nguồn của phương pháp decrypt lớp GIFEncryptorByLSBMethod:

Steganography trong 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);
}

Thực hiện lớp học GIFEncryptorByPaletteExtensionMethod sẽ giống nhau, chỉ khác cách thức lưu/đọc thông tin.

Trong lớp MainFrame phương pháp bao bọc được mô tả: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), xử lý kết quả của các phương pháp giao diện Encryptor và tương tác với người dùng, tức là mở hộp thoại chọn tệp, hiển thị thông báo lỗi, v.v.; cũng như các phương pháp khác: openImage(), cho phép người dùng chọn một hình ảnh, exit(), thoát khỏi ứng dụng. Các phương thức này được gọi từ Actioncác mục menu tương ứng. Lớp này thực hiện thêm các phương thức phụ trợ: createComponents() - tạo ra các thành phần biểu mẫu, loadImageFile(File f) — tải hình ảnh vào một thành phần đặc biệt từ một tập tin. Thực hiện lớp học GIFEncryptorByPaletteExtensionMethod tương tự như việc thực hiện lớp GIFEncryptorByLSBMethod, sự khác biệt chính là ở cách viết và đọc byte thông báo từ bảng màu.

Vận hành chương trình

phương pháp LBS

Giả sử có một hình ảnh như thế này:

Steganography trong GIF

Trong hình ảnh này, bảng màu bao gồm 256 màu (khi Paint lưu). Bốn màu đầu tiên là: trắng, đen, đỏ, xanh lá cây. Các màu khác là màu đen. Chuỗi bit bảng màu chung sẽ như sau:

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

Steganography trong GIF

Sau khi tin nhắn được nhúng, các bit được gạch chân sẽ được thay thế bằng các bit trong tin nhắn. Hình ảnh thu được gần như không khác biệt gì so với ảnh gốc.

Nguyên
Hình ảnh có tin nhắn được nhúng

Steganography trong GIF
Steganography trong GIF

Phương pháp mở rộng bảng màu

Khi mở một hình ảnh chứa tin nhắn bằng phương pháp này, bạn sẽ thấy hình ảnh sau:

Steganography trong GIF

Rõ ràng là phương pháp này sẽ không hiệu quả đối với các hoạt động gián điệp chính thức và có thể yêu cầu mã hóa bổ sung tin nhắn.

Mã hóa/giải mã trong hình ảnh động hoạt động giống như trong hình ảnh tĩnh thông thường, nhưng hình ảnh động không bị hỏng.

Các nguồn đã sử dụng:

Tải xuống:

Nguồn: www.habr.com

Thêm một lời nhận xét