Steganográfia GIF-ben

Bevezetés

Üdvözlöm.
Nem rég, amikor az egyetemen tanultam, írtam egy dolgozatot az "Információvédelmi szoftveres módszerek" tantárgyból. A feladat egy olyan program készítése volt, amely üzenetet ágyaz be GIF formátumú fájlokba. Java nyelven döntöttem a megvalósítás mellett.

A cikkben néhány elméleti szempontot ismertetek, valamint azt, hogyan készült el ez a kis program.

Elméleti rész

GIF formátum

A GIF (angolul Graphics Interchange Format — képek cseréjére szolgáló formátum) egy grafikus képek tárolására szolgáló formátum, amely képes tömörített adatokat tárolni, minőségromlás nélkül, legfeljebb 256 színig. Ezt a formátumot 1987-ben (GIF87a) a CompuServe fejlesztette ki, hogy raszteres képeket továbbíthasson a hálózatokon. 1989-ben a formátumot módosították (GIF89a), hozzáadták az átlátszóság és animáció támogatását.

A GIF formátumú fájlok blokkszerkezetet alkalmaznak. Ezek a blokkok mindig rögzített hosszúságúak (vagy a bizonyos flagoktól függnek), így szinte lehetetlen hibázni a blokkok elhelyezkedésével kapcsolatban. A legegyszerűbb nem animált GIF-kép struktúrája GIF89a formátumban:

Steganográfia GIF-ben

A szerkezet összes blokkjából minket most a globális paletta és a palettát irányító paraméterek érdekelnek:

  • CT — globális paletta megléte. Ha ez a flag be van állítva, akkor a logikai képernyő leíróját követően azonnal a globális palettának kell kezdődnie.
  • Méret — a paletta mérete és a kép színeinek száma. E paraméter értékei:

Méret
Színek száma
A paletta mérete, 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

Titkosítási módszerek

A képfájlokban lévő üzenetek titkosításához a következő módszereket fogjuk használni:

  • LSB módszer (Legkisebb Jelentős Bit)
  • Paletta kiegészítési módszer

LSB módszer — a legelterjedtebb steganográfiai módszer. Ez abban rejlik, hogy a konténer utolsó jelentős biteit (esetünkben a globális paletta byte-jait) a rejtett üzenet biteivel helyettesítjük.

A program will use the last two bits in the bytes of the global palette within this method. This means that for a 24-bit image, where the color palette consists of three bytes for red, blue, and green colors, after the message is embedded, each color component will change by a maximum of 3/255 gradations. Such a change will, firstly, be unnoticed or hard to notice by the human eye, and secondly, will not be distinguishable on low-quality output devices.

The amount of information will directly depend on the size of the image palette. Since the maximum size of the palette is 256 colors, and if two message bits are recorded in each color component, then the maximum length of the message (with the maximum palette in the image) is 192 bytes. After embedding the message in the image, the file size does not change.

Palette Expansion Method, which works only for the GIF structure. It will be most effective in images with small palette sizes. Its essence is that it increases the palette size, thus providing additional space for writing the necessary bytes in place of the color bytes. Considering that the minimum palette size is 2 colors (6 bytes), the maximum size of the embedded message can be 256×3–6=762 bytes. A drawback is low cryptographic protection; the embedded message can be read using any text editor if it has not undergone additional encryption.

Practical Part

Program Design

All necessary tools for implementing the encryption and decryption algorithms will be located in the package com.tsarik.steganography. This package includes the interface Encryptor with the methods encrypt és decrypt, the class Binary, providing the ability to work with arrays of bits, as well as exception classes UnableToEncryptException és UnableToDecryptException, which should be used in the interface methods Encryptor in case of encoding and decoding errors, respectively.

The main package of the program com.tsarik.programs.gifed will include the executable class of the program with a static method main, allowing the program to be launched; a class that stores the program parameters; and packages with other classes.

A végrehajtás magukat az algoritmusokat a csomagban fogja bemutatni com.tsarik.programs.gifed.gif osztályokban GIFEncryptorByLSBMethod és GIFEncryptorByPaletteExtensionMethod. Mindkét osztály meg fogja valósítani a felületet Encryptor.

A GIF formátum szerkezete alapján általános algoritmust lehet kidolgozni az üzenet képként való elrejtésére:

Steganográfia GIF-ben

Az üzenet meglétének meghatározásához az üzenet elejére egy bit sorozatot kell hozzáadni, amelyet a dekódoló elsőként olvas el, és ellenőrzi a helyességet. Ha ez nem egyezik, akkor azt feltételezzük, hogy a képben nincs elrejtett üzenet. Ezután meg kell adni az üzenet hosszát. Majd az üzenet szövegét.

Az alkalmazás teljes osztálydiagramja:

Steganográfia GIF-ben

A program végrehajtása

A teljes program végrehajtását két összetevőre lehet bontani: a titkosítási és dekódolási módszerek megvalósítására Encryptor, az osztályokban GIFEncryptorByLSBMethod és GIFEncryptorByPaletteExtensionMethod, és a felhasználói felület megvalósítására.

Nézzük meg az osztályt GIFEncryptorByLSBMethod.

Steganográfia GIF-ben

Mezők firstLSBit és secondLSBit tartalmazza a bit számokat minden képkocka byte-jában, ahová az üzenetet be kell írni és ahonnan olvasni kell. A mező checkSequence tárolja a beágyazott üzenet észleléséhez szükséges bit ellenőrző sorozatát. A statikus módszer getEncryptingFileParameters visszaadja a megadott fájl paramétereit és a potenciális üzenet jellemzőit.

A módszer algoritmusa encrypt kapcsolat és csatornák GIFEncryptorByLSBMethod:

Steganográfia GIF-ben

És annak kódja:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("A bemeneti fájl null");
	}
	if (out == null) {
		throw new NullPointerException("A kimeneti fájl null");
	}
	if (text == null) {
		throw new NullPointerException("A szöveg null");
	}
	
	// olvassuk be a bájtokat a bemeneti fájlból
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// ellenőrizzük a formátumot
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("A bemeneti fájl hibás GIF formátumú");
	}
	
	// olvassuk be a paletta méret tulajdonságát a 10. bájt első három bitjéből
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// számítsuk ki a színszámot és a lehetséges üzenet hosszát
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // egy bájt a ellenőrzésre és egy bájt az üzenet hosszára
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("A szöveg túl hosszú");
	}
	
	int n = 13;
	
	// írjuk be az ellenőrző sorozatot
	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++;
	}
	
	// írjuk be a szöveg hosszát
	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++;
	}
	
	// írjuk be az üzenetet
	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++;
		}
	}
	
	// írjuk ki a kimeneti fájlt
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Algoritmus és a módszer forráskódja decrypt kapcsolat és csatornák GIFEncryptorByLSBMethod:

Steganográfia GIF-ben

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("A bemeneti fájl null");
	}
	
	// bájtok olvasása a bemeneti fájlból
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// formátum ellenőrzése
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("A bemeneti fájl formátuma hibás");
	}
	
	// a paletta méretének tulajdonságának olvasása a 10. bájt első három bitjéből
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// a színek számának és a lehetséges üzenethossz kiszámítása
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // egy bájt a ellenőrzésnek és egy bájt az üzenet hosszának
	
	int n = 13;
	
	// ellenőrző sorozat olvasása
	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("Nincs titkosított üzenet a képen (Az ellenőrző sorozat hibás)");
	}
	
	// szöveghossz olvasása
	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("A dekódolt szöveghossz kisebb, mint 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Nincsenek üzenetek (A dekódolt üzenet hossza (" + textLength + ") kisebb, mint a lehetséges üzenethossz (" + possibleTextLength + "))");
	}
	
	// szövegbitek olvasása és szövegbájtok létrehozása
	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);
}

A osztály megvalósítása GIFEncryptorByPaletteExtensionMethod hasonló lesz, csak a mentési/kiolvasási módszer eltér.

Az osztályban MainFrame a "wrapper" módszereket írja le: encryptImage(Encryptor encryptor) és decryptImage(Encryptor encryptor), amelyek feldolgozzák az interfész módszereinek eredményeit, Encryptor és lehetővé teszik a felhasználói interakciót, pl. fájlok kiválasztására nyit diákot, hibaüzeneteket mutat be stb.; valamint más módszereket is tartalmaz: openImage(), amely lehetőséget ad a felhasználónak egy kép kiválasztására, exit(), amely lehetővé teszi a programból való kilépést. Ezeket a módszereket a Akciómenüpontok megfelelő elemeiből hívják meg. Ebben az osztályban további segédmódszerek is vannak: createComponents() — a form komponenseinek létrehozása, loadImageFile(File f) — kép betöltése egy speciális komponensbe fájlból. Az osztály megvalósítása GIFEncryptorByPaletteExtensionMethod hasonló az osztály megvalósításához GIFEncryptorByLSBMethod, a fő különbség a üzenet bájtjainak a palettából való írása és olvasása módjában rejlik.

A program működése

LBS metódus

Tegyük fel, hogy van egy ilyen kép:

Steganográfia GIF-ben

A képen 256 szín látható (így tárolja a Paint). Az első négy szín: fehér, fekete, piros, zöld. A többi szín fekete. A globális paletta bitjeinek sorrendje a következő lesz:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000…

Steganográfia GIF-ben

Az üzenet beillesztése után a kiemelt bitek az üzenet bitjeivel lesznek helyettesítve. A kapott kép szinte alig különbözik az eredetitől.

Eredeti
Üzenetet tartalmazó kép

Steganográfia GIF-ben
Steganográfia GIF-ben

Palette Expansion Method

Ha megnyitunk egy olyan képet, amelybe üzenetet helyeztek el e módszer szerint, egy ilyen látvány fogad:

Steganográfia GIF-ben

Egyértelmű, hogy ilyen módszerrel a teljes értékű kémkedés nem lehetséges, és esetleg további üzenet titkosítást igényel.

A titkosítás/dekódolás animált képekben ugyanúgy működik, mint a normális statikus képekben, és az animáció nem zavartatik meg.

Használt források:

Letöltés:

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster