Steganografie in GIF

Einführung

Hallo.
Vor nicht allzu langer Zeit, als ich an der Universität studierte, hatte ich eine Seminararbeit im Fach "Programmmethoden zum Schutz von Informationen". Die Aufgabe bestand darin, ein Programm zu erstellen, das eine Nachricht in Dateien des GIF-Formats einfügt. Ich entschied mich, dies in Java zu tun.

In diesem Artikel werde ich einige theoretische Aspekte beschreiben und erläutern, wie dieses kleine Programm entstanden ist.

Theoretischer Teil

GIF-Format

GIF (engl. Graphics Interchange Format – Format zum Austausch von Bildern) ist ein Format zur Speicherung grafischer Bilder, das komprimierte Daten ohne Qualitätsverlust im Format von bis zu 256 Farben speichern kann. Dieses Format wurde 1987 (GIF87a) von der Firma CompuServe entwickelt, um Rasterbilder über Netzwerke zu übertragen. 1989 wurde es modifiziert (GIF89a), wobei Unterstützung für Transparenz und Animation hinzugefügt wurde.

GIF-Dateien haben eine blockartige Struktur. Die Datenblöcke haben immer eine feste Länge (oder sie hängt von bestimmten Flags ab), so dass es nahezu unmöglich ist, Fehler darüber zu machen, wo sich welcher Block befindet. Die Struktur eines einfachsten nicht animierten GIF-Bildes im GIF89a-Format lautet:

Steganografie in GIF

Von allen Blöcken der Struktur interessieren uns in diesem Fall der globale Palette-Block und die Parameter, die für die Palette verantwortlich sind:

  • CT — Vorhandensein einer globalen Palette. Wenn dieses Flag gesetzt ist, sollte die globale Palette unmittelbar nach dem Deskriptor des logischen Bildschirms beginnen.
  • Size — Größe der Palette und Anzahl der Farben des Bildes. Die Werte dieses Parameters:

Size
Anzahl der Farben
Größe der Palette, 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

Verschlüsselungsmethoden

Als Methoden zur Verschlüsselung von Nachrichten in Bilddateien werden verwendet:

  • LSB-Methode (Least Significant Bit, kleinster signifikante Bit)
  • Palette-Ergänzungsmethode

LSB-Methode — eine verbreitete Methode der Steganographie. Sie besteht darin, die letzten signifikanten Bits im Container (in unserem Fall die Bytes der globalen Palette) durch die Bits der versteckten Nachricht zu ersetzen.

Im Programm werden im Rahmen dieser Methode die letzten beiden Bits der Bytes der globalen Palette verwendet. Das bedeutet, dass bei einem 24-Bit-Bild, in dem die Farbpalerte aus drei Bytes für Rot, Blau und Grün besteht, nach dem Einfügen der Nachricht jede Farbkomponente maximal um 3/255 Stufen verändert wird. Eine solche Veränderung wird erstens vom menschlichen Auge nicht wahrnehmbar oder nur schwer wahrnehmbar sein und zweitens auf niedrigauflösenden Ausgabegeräten nicht erkennbar sein.

Die Menge an Informationen wird direkt von der Größe der Bildpalette abhängen. Da die maximale Größe der Palette 256 Farben beträgt und, wenn man zwei Bits der Nachricht in jede Farbkomponente einfügt, die maximale Länge der Nachricht (bei maximaler Palette im Bild) 192 Bytes beträgt. Nach dem Einfügen der Nachricht in das Bild ändert sich die Dateigröße nicht.

Palette Enlargement Method, die nur für die GIF-Struktur funktioniert. Sie ist am effektivsten bei Bildern mit kleinen Paletten. Der Kern besteht darin, die Größe der Palette zu erhöhen, wodurch zusätzlicher Platz zum Einfügen der erforderlichen Bytes anstelle der Farbbytes geschaffen wird. Wenn man bedenkt, dass die minimale Palette 2 Farben (6 Bytes) hat, kann die maximale Größe der eingefügten Nachricht 256×3–6=762 Bytes betragen. Ein Nachteil ist die geringe kryptografische Sicherheit; die eingefügte Nachricht kann mit jedem Texteditor gelesen werden, wenn die Nachricht keiner zusätzlichen Verschlüsselung unterzogen wurde.

Praktischer Teil

Programmentwicklung

Alle notwendigen Werkzeuge zur Implementierung der Verschlüsselungs- und Entschlüsselungsalgorithmen befinden sich im Paket com.tsarik.steganography. Dieses Paket enthält das Interface Encryptor mit den Methoden encrypt und decrypt, die Klasse Binary, die die Arbeit mit Bitarrays ermöglicht, sowie die Ausnahmeklassen UnableToEncryptException und UnableToDecryptException, die in den Methoden des Interfaces im Falle von Codierungs- und Decodierungsfehlern verwendet werden sollten. Encryptor Das Hauptpaket des Programms

com.tsarik.programs.gifed wird die ausführbare Klasse des Programms mit einer statischen Methode enthalten , die das Programm startet; eine Klasse, die die Programmparameter speichert; und Pakete mit anderen Klassen. maindie das Programm startet; eine Klasse, die die Programmparameter speichert; und Pakete mit anderen Klassen.

Die Umsetzung der Algorithmen selbst wird im Paket dargestellt com.tsarik.programs.gifed.gif Klassen GIFEncryptorByLSBMethod und GIFEncryptorByPaletteExtensionMethod. Beide Klassen werden das Interface implementieren Encryptor.

Basierend auf der Struktur des GIF-Formats kann ein allgemeiner Algorithmus zur Einbettung einer Nachricht in die Bildpalette erstellt werden:

Steganografie in GIF

Um die Präsenz einer Nachricht im Bild zu bestimmen, muss am Anfang der Nachricht eine bestimmte Bitfolge hinzugefügt werden, die der Dekodierer zuerst liest und auf Korrektheit überprüft. Wenn sie nicht übereinstimmt, wird angenommen, dass im Bild keine versteckte Nachricht vorhanden ist. Danach muss die Länge der Nachricht angegeben werden. Dann der eigentliche Text der Nachricht.

Das Klassendiagramm der gesamten Anwendung:

Steganografie in GIF

Implementierung des Programms

Die Implementierung des gesamten Programms kann in zwei Teile untergliedert werden: die Implementierung der Verschlüsselungs- und Entschlüsselungsmethoden des Interfaces Encryptor, in den Klassen GIFEncryptorByLSBMethod und GIFEncryptorByPaletteExtensionMethod, und die Implementierung des Benutzerinterfaces.

Betrachten wir die Klasse GIFEncryptorByLSBMethod.

Steganografie in GIF

Felder firstLSBit und secondLSBit enthalten die Bitnummern jedes Bytes des Bildes, in die die Nachricht geschrieben werden soll und aus denen sie gelesen wird. Das Feld checkSequence speichert die Kontrollbitfolge zur Sicherstellung der Erkennung der eingebetteten Nachricht. Die statische Methode getEncryptingFileParameters gibt die Parameter der angegebenen Datei und die Eigenschaften der potenziellen Nachricht zurück.

Der Algorithmus der Methode encrypt Klasse GIFEncryptorByLSBMethod:

Steganografie in GIF

Und der dazugehörige Code:

@Override
public void encrypt(File in, File out, String text) throws UnableToEncodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Eingabedatei ist null");
	}
	if (out == null) {
		throw new NullPointerException("Ausgabedatei ist null");
	}
	if (text == null) {
		throw new NullPointerException("Text ist null");
	}
	
	// Bytes aus der Eingabedatei lesen
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// Format überprüfen
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToEncodeException("Eingabedatei hat falsches GIF-Format");
	}
	
	// Palette-Größe-Eigenschaft aus den ersten drei Bits im zehnten Byte der Datei lesen
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// Farbanzahl und mögliche Nachrichtenlänge berechnen
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2; // ein Byte für die Überprüfung und ein Byte für die Nachrichtenlänge
	
	if (possibleTextLength < text.length()) {
		throw new UnableToEncodeException("Text ist zu groß");
	}
	
	int n = 13;
	
	// Prüfreihenfolge schreiben
	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++;
	}
	
	// Textlänge schreiben
	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++;
	}
	
	// Nachricht schreiben
	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++;
		}
	}
	
	// Ausgabedatei schreiben
	OutputStream os = new FileOutputStream(out);
	os.write(bytes);
	os.close();
}

Algorithmus und Quellcode des Verfahrens decrypt Klasse GIFEncryptorByLSBMethod:

Steganografie in GIF

@Override
public String decrypt(File in) throws UnableToDecodeException, NullPointerException, IOException {
	if (in == null) {
		throw new NullPointerException("Die Eingabedatei ist null");
	}
	
	// Bytes aus der Eingabedatei lesen
	byte[] bytes = new byte[(int)in.length()];
	InputStream is = new FileInputStream(in);
	is.read(bytes);
	is.close();
	
	// Format überprüfen
	if (!(new String(bytes, 0, 6)).equals("GIF89a")) {
		throw new UnableToDecodeException("Die Eingabedatei hat ein falsches GIF-Format");
	}
	
	// Farbeigenschaft der Palette aus den ersten drei Bits im 10. Byte aus der Datei lesen
	byte[] b10 = Binary.toBitArray(bytes[10]);
	byte bsize = Binary.toByte(new byte[] {b10[0], b10[1], b10[2]});
	
	// Farbanzahl und mögliche Nachrichtenlänge berechnen
	int bOrigColorCount = (int)Math.pow(2, bsize+1);
	int possibleMessageLength = bOrigColorCount*3/4;
	int possibleTextLength = possibleMessageLength-2;	// ein Byte für die Prüfziffer und ein Byte für die Nachrichtenlänge
	
	int n = 13;
	
	// Prüfsequenz lesen
	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("Es gibt keine verschlüsselte Nachricht im Bild (Prüfziffer ist falsch)");
	}
	
	// Textlänge lesen
	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("Die decodierte Textlänge ist kleiner als 0");
	}
	if (possibleTextLength < textLength) {
		throw new UnableToDecodeException("Es gibt keine Nachrichten (Die decodierte Nachrichtenlänge (" + textLength + ") ist kleiner als die mögliche Nachrichtenlänge (" + possibleTextLength + "))");
	}
	
	// Textbits lesen und Textbytes erzeugen
	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, unterscheidet sich jedoch in der Methode zum Speichern/Lesen von Informationen.

In der Klasse MainFrame sind die Methoden-„Wrapper“ beschrieben: encryptImage(Encryptor encryptor) und decryptImage(Encryptor encryptor), die die Ergebnisse der Methoden des Interfaces verarbeiten Encryptor und das Benutzerinteraktion ermöglichen, d.h. Dialoge zum Dateiauswahl öffnen, Fehlermeldungen anzeigen usw.; sowie weitere Methoden: openImage(), die dem Benutzer die Auswahl eines Bildes ermöglicht, exit(), die den Ausstieg aus der Anwendung ermöglicht. Diese Methoden werden aus Aktionender entsprechenden Menüeinträge aufgerufen. In dieser Klasse sind zusätzlich Hilfsmethoden implementiert: createComponents() — Erstellung der Komponenten des Formulars, loadImageFile(File f) — lädt ein Bild in eine spezielle Komponente aus einer Datei. Die Implementierung der Klasse GIFEncryptorByPaletteExtensionMethod ist der Implementierung der Klasse GIFEncryptorByLSBMethod, der Hauptunterschied besteht in der Art und Weise, wie die Bytes der Nachricht aus der Palette geschrieben und gelesen werden.

Funktionsweise des Programms

Die Methode LBS

Angenommen, es gibt ein solches Bild:

Steganografie in GIF

Auf diesem Bild besteht die Palette aus 256 Farben (so speichert Paint). Die ersten vier Farben sind: Weiß, Schwarz, Rot, Grün. Die restlichen Farben sind Schwarz. Die Bitfolge der globalen Palette wird wie folgt aussehen:

11111111 11111111 11111111 00000000 00000000 00000000 11111111 00000000 00000000 00000000 11111111 00000000

Steganografie in GIF

Nach der Einspeisung der Nachricht werden die hervorgehobenen Bits durch die Bits der Nachricht ersetzt. Das erhaltene Bild unterscheidet sich kaum vom Original.

Original
Bild mit eingespeister Nachricht

Steganografie in GIF
Steganografie in GIF

Palette Enlargement Method

Öffnet man das Bild, in das die Nachricht nach dieser Methode eingefügt wurde, könnte man folgendes Bild entdecken:

Steganografie in GIF

Es versteht sich von selbst, dass diese Methode für eine umfassende Spionagetätigkeit nicht geeignet ist und möglicherweise zusätzliche Verschlüsselung der Nachricht erfordert.

Die Verschlüsselung/Dekodierung in animierten Bildern funktioniert wie in normalen statischen Bildern, wobei die Animation nicht beeinträchtigt wird.

Verwendete Quellen:

Herunterladen:

Quelle: habr.com

60GB SSD 8Gb DDR4