Steganographie im GIF

Einführung

Willkommen.
Vor nicht allzu langer Zeit, als ich an der Universität studierte, gab es eine Lehrveranstaltung im Fach „Softwaremethoden der Informationssicherheit“. Für den Auftrag mussten wir ein Programm erstellen, das eine Nachricht in GIF-Dateien einbettet. Ich habe beschlossen, es in Java zu machen.

In diesem Artikel beschreibe ich einige theoretische Punkte sowie die Entstehung dieses kleinen Programms.

Theoretischer Teil

GIF-Format

GIF (Graphics Interchange Format – ein Format zum Austausch von Bildern) ist ein Format zum Speichern von Grafikbildern, das komprimierte Daten ohne Qualitätsverlust in einem Format mit bis zu 256 Farben speichern kann. Dieses Format wurde 1987 (GIF87a) von CompuServe zur Übertragung von Rasterbildern über Netzwerke entwickelt. 1989 wurde das Format geändert (GIF89a) und Unterstützung für Transparenz und Animation hinzugefügt.

GIF-Dateien haben eine Blockstruktur. Diese Blöcke haben immer eine feste Länge (oder sie hängt von einigen Flags ab), sodass es fast unmöglich ist, einen Fehler bei der Position jedes Blocks zu machen. Die Struktur des einfachsten nicht animierten GIF-Bildes im GIF89a-Format:

Steganographie im GIF

Von allen Blöcken der Struktur interessieren uns in diesem Fall der globale Palettenblock und die für die Palette verantwortlichen Parameter:

  • CT — Vorhandensein einer globalen Palette. Wenn dieses Flag gesetzt ist, muss die globale Palette unmittelbar nach dem logischen Bildschirmhandle beginnen.
  • Size — Palettengröße und Anzahl der Farben im Bild. Werte für diesen Parameter:

Größe
Anzahl der Farben
Palettengröße, Bytes

7
256
768

6
128
384

5
64
192

4
32
96

3
16
48

2
8
24

1
4
12

0
2
6

Verschlüsselungsmethoden

Die folgenden Methoden werden zum Verschlüsseln von Nachrichten in Bilddateien verwendet:

  • LSB-Methode (Least Significant Bit).
  • Methode zum Hinzufügen einer Palette

LSB-Methode - eine gängige Methode der Steganographie. Dabei werden die letzten signifikanten Bits im Container (in unserem Fall die globalen Palettenbytes) durch die Bits der versteckten Nachricht ersetzt.

Das Programm verwendet im Rahmen dieser Methode die letzten beiden Bits in den globalen Palettenbytes. Das bedeutet, dass sich bei einem 24-Bit-Bild, bei dem die Farbpalette drei Bytes für Rot, Blau und Grün umfasst, nach dem Einbetten einer Nachricht jede Farbkomponente um maximal 3/255 Abstufungen ändert. Eine solche Änderung wird erstens für das menschliche Auge unsichtbar oder schwer wahrnehmbar sein und zweitens wird sie auf Informationsausgabegeräten geringer Qualität nicht sichtbar sein.

Die Menge an Informationen hängt direkt von der Größe der Bildpalette ab. Da die maximale Größe der Palette 256 Farben beträgt und wenn zwei Nachrichtenbits in die Komponente jeder Farbe geschrieben werden, beträgt die maximale Nachrichtenlänge (mit der maximalen Palette im Bild) 192 Bytes. Sobald die Nachricht in das Bild eingebettet ist, ändert sich die Dateigröße nicht.

Methode zur Palettenerweiterung, was nur für die GIF-Struktur funktioniert. Am effektivsten ist es bei Bildern mit einer kleinen Palette. Sein Wesen besteht darin, dass die Palette vergrößert wird und dadurch zusätzlicher Platz zum Schreiben der erforderlichen Bytes anstelle der Farbbytes bereitgestellt wird. Wenn wir davon ausgehen, dass die Mindestgröße der Palette 2 Farben (6 Bytes) beträgt, kann die maximale Größe der eingebetteten Nachricht 256 × 3–6 = 762 Bytes betragen. Der Nachteil ist die geringe kryptografische Sicherheit; die eingebettete Nachricht kann mit jedem Texteditor gelesen werden, wenn die Nachricht keiner zusätzlichen Verschlüsselung unterzogen wurde.

Praktischer Teil

Programmdesign

Alle notwendigen Tools zur Implementierung von Verschlüsselungs- und Entschlüsselungsalgorithmen sind im Paket enthalten com.tsarik.steganography. Dieses Paket beinhaltet die Schnittstelle Encryptor mit Methoden encrypt и decrypt, Klasse Binary, was die Möglichkeit bietet, mit Bit-Arrays sowie Ausnahmeklassen zu arbeiten UnableToEncryptException и UnableToDecryptException, die in Schnittstellenmethoden verwendet werden sollte Encryptor im Falle von Codierungs- bzw. Decodierungsfehlern.

Hauptprogrammpaket com.tsarik.programs.gifed enthält eine ausführbare Programmklasse mit einer statischen Methode main, sodass Sie das Programm ausführen können; eine Klasse, die Programmparameter speichert; und Pakete mit anderen Klassen.

Die Implementierung der Algorithmen selbst wird im Paket vorgestellt com.tsarik.programs.gifed.gif Klassen GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethod. Beide Klassen implementieren die Schnittstelle Encryptor.

Basierend auf der Struktur des GIF-Formats können Sie einen allgemeinen Algorithmus zum Einfügen einer Nachricht in die Bildpalette erstellen:

Steganographie im GIF

Um das Vorhandensein einer Nachricht in einem Bild festzustellen, ist es notwendig, am Anfang der Nachricht eine bestimmte Bitfolge hinzuzufügen, die der Decoder zuerst liest und auf Richtigkeit überprüft. Wenn es nicht übereinstimmt, wird davon ausgegangen, dass das Bild keine versteckte Nachricht enthält. Als nächstes müssen Sie die Länge der Nachricht angeben. Dann der Text der Nachricht selbst.

Klassendiagramm der gesamten Anwendung:

Steganographie im GIF

Umsetzung des Programms

Die Implementierung des gesamten Programms kann in zwei Komponenten unterteilt werden: Implementierung von Schnittstellenverschlüsselungs- und -entschlüsselungsmethoden Encryptor, im Unterricht GIFEncryptorByLSBMethod и GIFEncryptorByPaletteExtensionMethodund die Implementierung der Benutzeroberfläche.

Betrachten Sie die Klasse GIFEncryptorByLSBMethod.

Steganographie im GIF

Felder firstLSBit и secondLSBit enthalten die Anzahl der Bits jedes Bytes des Bildes, in das die Nachricht eingegeben werden soll und von wo aus die Nachricht gelesen werden soll. Feld checkSequence speichert eine Prüfbitfolge, um die Erkennung der eingebetteten Nachricht sicherzustellen. Statische Methode getEncryptingFileParameters gibt die Parameter der angegebenen Datei und die Eigenschaften der potenziellen Nachricht zurück.

Methodenalgorithmus encrypt Klasse GIFEncryptorByLSBMethod:

Steganographie im GIF

Und sein Code:

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

Algorithmus und Quellcode der Methode decrypt Klasse GIFEncryptorByLSBMethod:

Steganographie im 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);
}

Implementierung der Klasse GIFEncryptorByPaletteExtensionMethod wird ähnlich sein, nur die Methode zum Speichern/Lesen von Informationen ist unterschiedlich.

In der Klasse MainFrame Wrapper-Methoden werden beschrieben: encryptImage(Encryptor encryptor) и decryptImage(Encryptor encryptor), Verarbeitung der Ergebnisse von Schnittstellenmethoden Encryptor und Interaktion mit dem Benutzer, d. h. Öffnen eines Dateiauswahldialogs, Anzeigen von Fehlermeldungen usw.; sowie andere Methoden: openImage(), sodass der Benutzer ein Bild auswählen kann, exit(), wodurch die Anwendung beendet wird. Diese Methoden werden aufgerufen von Actiondie entsprechenden Menüpunkte. Diese Klasse implementiert zusätzlich Hilfsmethoden: createComponents() - Erstellung von Formularkomponenten, loadImageFile(File f) – Laden eines Bildes aus einer Datei in eine spezielle Komponente. Implementierung der Klasse GIFEncryptorByPaletteExtensionMethod ähnlich der Klassenimplementierung GIFEncryptorByLSBMethodDer Hauptunterschied besteht in der Art und Weise, wie Nachrichtenbytes aus der Palette geschrieben und gelesen werden.

Programmbetrieb

LBS-Methode

Nehmen wir an, es gibt ein Bild wie dieses:

Steganographie im GIF

In diesem Bild besteht die Palette aus 256 Farben (wie Paint speichert). Die ersten vier Farben sind: Weiß, Schwarz, Rot, Grün. Andere Farben sind Schwarz. Die globale Palettenbitfolge sieht wie folgt aus:

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

Steganographie im GIF

Sobald die Nachricht eingebettet ist, werden die unterstrichenen Bits durch die Bits aus der Nachricht ersetzt. Das resultierende Bild unterscheidet sich nahezu nicht vom Original.

Original
Bild mit eingebetteter Nachricht

Steganographie im GIF
Steganographie im GIF

Methode zur Palettenerweiterung

Wenn Sie mit dieser Methode ein Bild öffnen, das eine Nachricht enthält, sehen Sie das folgende Bild:

Steganographie im GIF

Es ist klar, dass diese Methode für umfassende Spionageaktivitäten nicht funktioniert und möglicherweise eine zusätzliche Verschlüsselung der Nachricht erfordert.

Die Verschlüsselung/Entschlüsselung in animierten Bildern funktioniert genauso wie in normalen statischen Bildern, die Animation wird jedoch nicht unterbrochen.

Verwendete Quellen:

Herunterladen:

Source: habr.com

Kommentar hinzufügen