GIF'te steganografi

Giriş

Hoşgeldiniz.
Çok uzun zaman önce, üniversitede okurken "Bilgi güvenliğinin yazılım yöntemleri" disiplininde bir ders vardı. Görev, GIF dosyalarına mesaj yerleştiren bir program oluşturmamızı gerektiriyordu. Bunu Java'da yapmaya karar verdim.

Bu yazıda bazı teorik noktaların yanı sıra bu küçük programın nasıl oluşturulduğunu anlatacağım.

Teorik kısım

GIF formatı

GIF (Grafik Değişim Formatı - görüntü alışverişi için bir format), sıkıştırılmış verileri kalite kaybı olmadan 256 renge kadar formatta depolayabilen grafik görüntüleri depolamak için bir formattır. Bu format, 1987'de (GIF87a) CompuServe tarafından taramalı görüntülerin ağlar üzerinden iletilmesi için geliştirildi. 1989'da format değiştirildi (GIF89a), şeffaflık ve animasyon desteği eklendi.

GIF dosyaları blok yapıya sahiptir. Bu blokların her zaman sabit bir uzunluğu vardır (ya da bazı bayraklara bağlıdır), dolayısıyla her bloğun nerede olduğu konusunda hata yapmak neredeyse imkansızdır. GIF89a formatındaki en basit animasyonsuz GIF görüntüsünün yapısı:

GIF'te steganografi

Bu durumda yapının tüm blokları arasında global palet bloğu ve paletten sorumlu parametrelerle ilgileneceğiz:

  • CT - küresel bir paletin varlığı. Bu bayrak ayarlanırsa genel palet mantıksal ekran tanıtıcısından hemen sonra başlamalıdır.
  • Size — resimdeki palet boyutu ve renk sayısı. Bu parametreye ait değerler:

beden
Renk sayısı
Palet boyutu, bayt

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Şifreleme yöntemleri

Görüntü dosyalarındaki mesajları şifrelemek için aşağıdaki yöntemler kullanılacaktır:

  • LSB (En Az Önemli Bit) yöntemi
  • Palet ekleme yöntemi

LSB yöntemi - yaygın bir steganografi yöntemi. Kaptaki son önemli bitlerin (bizim durumumuzda global palet baytları) gizli mesajın bitleriyle değiştirilmesinden oluşur.

Program bu yöntemin bir parçası olarak global palet baytlarındaki son iki biti kullanacaktır. Bu, renk paletinin kırmızı, mavi ve yeşil için üç bayt olduğu 24 bitlik bir görüntü için, içine bir mesaj yerleştirildikten sonra her renk bileşeninin maksimum 3/255 derece değişeceği anlamına gelir. Böyle bir değişiklik öncelikle insan gözüyle görülmeyecek veya fark edilmesi zor olacak, ikincisi ise düşük kaliteli bilgi çıkış cihazlarında görülmeyecek.

Bilgi miktarı doğrudan görüntü paletinin boyutuna bağlı olacaktır. Paletin maksimum boyutu 256 renk olduğundan ve her rengin bileşenine iki mesaj biti yazılırsa maksimum mesaj uzunluğu (görüntüdeki maksimum paletle birlikte) 192 bayt olur. Mesaj görsele gömüldükten sonra dosya boyutu değişmez.

Palet genişletme yöntemi, yalnızca GIF yapısı için çalışır. Küçük paletli görüntülerde en etkili olacaktır. Özü, paletin boyutunu arttırması, böylece gerekli baytların renk baytları yerine yazılması için ek alan sağlamasıdır. Paletin minimum boyutunun 2 renk (6 byte) olduğunu düşünürsek, gömülü mesajın maksimum boyutu 256 × 3–6 = 762 byte olabilir. Dezavantajı düşük kriptografik güvenliktir; eğer mesaj ek şifrelemeye tabi tutulmamışsa, gömülü mesaj herhangi bir metin düzenleyici kullanılarak okunabilir.

Pratik kısmı

Program tasarımı

Şifreleme ve şifre çözme algoritmalarını uygulamak için gerekli tüm araçlar pakete dahil edilecektir com.tsarik.steganography. Bu paket arayüzü içerir Encryptor yöntemlerle encrypt и decrypt, Sınıf Binarybit dizileriyle ve istisna sınıflarıyla çalışma yeteneği sağlar UnableToEncryptException и UnableToDecryptExceptionarayüz yöntemlerinde kullanılması gereken Encryptor sırasıyla kodlama ve kod çözme hataları durumunda.

Ana program paketi com.tsarik.programs.gifed statik yöntemle çalıştırılabilir bir program sınıfı içerecektir mainprogramı çalıştırmanıza izin verir; program parametrelerini saklayan bir sınıf; ve diğer sınıflarla paketler.

Algoritmaların uygulanması pakette sunulacaktır com.tsarik.programs.gifed.gif sınıflar GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Bu sınıfların her ikisi de arayüzü uygulayacak Encryptor.

GIF formatının yapısına dayanarak, görüntü paletine bir mesaj eklemek için genel bir algoritma oluşturabilirsiniz:

GIF'te steganografi

Bir görüntüde bir mesajın varlığını belirlemek için, mesajın başlangıcına, kod çözücünün ilk önce okuduğu ve doğruluğunu kontrol ettiği belirli bir bit dizisinin eklenmesi gerekir. Eşleşmiyorsa görselde gizli bir mesaj olmadığı kabul edilir. Daha sonra mesajın uzunluğunu belirtmeniz gerekir. Daha sonra mesajın metni.

Tüm uygulamanın sınıf diyagramı:

GIF'te steganografi

Programın uygulanması

Programın tamamının uygulanması iki bileşene ayrılabilir: arayüz şifreleme ve şifre çözme yöntemlerinin uygulanması Encryptor, sınıflarda GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethodve kullanıcı arayüzünün uygulanması.

Sınıfı düşünün GIFEncryptorByLSBMethod.

GIF'te steganografi

Tarla firstLSBit и secondLSBit mesajın girilmesi gereken ve mesajın nereden okunması gerektiği görüntünün her baytının bit sayısını içerir. Alan checkSequence gömülü mesajın tanınmasını sağlamak için bir kontrol biti dizisi saklar. Statik yöntem getEncryptingFileParameters belirtilen dosyanın parametrelerini ve potansiyel mesajın özelliklerini döndürür.

Yöntem algoritması encrypt sınıf GIFEncryptorByLSBMethod:

GIF'te steganografi

Ve onun kodu:

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

Yöntemin algoritması ve kaynak kodu decrypt sınıf GIFEncryptorByLSBMethod:

GIF'te steganografi

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

Sınıfın uygulanması GIFEncryptorByPaletteExtensionMethod benzer olacaktır, yalnızca bilgiyi kaydetme/okuma yöntemi farklıdır.

Sınıfta MainFrame sarma yöntemleri açıklanmıştır: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), arayüz yöntemlerinin sonuçlarının işlenmesi Encryptor ve kullanıcıyla etkileşimde bulunmak, yani bir dosya seçimi iletişim kutusunu açmak, hata mesajlarını göstermek vb.; diğer yöntemlerin yanı sıra: openImage()kullanıcının bir resim seçmesine olanak tanır, exit(), uygulamadan çıkar. Bu yöntemler çağrılır Actionilgili menü öğeleri. Bu sınıf ayrıca yardımcı yöntemleri de uygular: createComponents() - form bileşenlerinin oluşturulması, loadImageFile(File f) — bir görüntünün bir dosyadan özel bir bileşene yüklenmesi. Sınıfın uygulanması GIFEncryptorByPaletteExtensionMethod sınıf uygulamasına benzer GIFEncryptorByLSBMethodtemel fark, mesaj baytlarının paletten yazılma ve okunma şeklidir.

Programın çalışması

LBS yöntemi

Diyelim ki şöyle bir görüntü var:

GIF'te steganografi

Bu görüntüde palet 256 renkten oluşuyor (Paint'in kaydettiği gibi). İlk dört renk: beyaz, siyah, kırmızı, yeşil. Diğer renkler siyahtır. Global palet bit dizisi aşağıdaki gibi olacaktır:

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

GIF'te steganografi

Mesaj gömüldükten sonra, altı çizili bitler mesajdaki bitlerle değiştirilecektir. Ortaya çıkan görüntü orijinalinden neredeyse hiç farklı değildir.

Orijinal
Gömülü mesaj içeren resim

GIF'te steganografi
GIF'te steganografi

Palet genişletme yöntemi

Bu yöntemi kullanarak mesaj içeren bir görseli açtığınızda aşağıdaki resmi göreceksiniz:

GIF'te steganografi

Bu yöntemin tam kapsamlı casusluk faaliyetlerinde işe yaramayacağı ve mesajın ek olarak şifrelenmesini gerektirebileceği açıktır.

Animasyonlu görüntülerde şifreleme/şifre çözme, normal statik görüntülerde olduğu gibi çalışır ancak animasyon bozulmaz.

Kullanılan kaynaklar:

İndir:

Kaynak: habr.com

Yorum ekle