Home » Tutorials » Sonstiges » Steganographie

Steganographie

Programmierung eines Steganographie Codierers

Zur Verdeutlichung der Möglichkeiten der „Least Significant Bit“ Methode habe ich ein Programm entwickelt, das auf diese Art Daten verstecken kann. Ich habe dazu einen einfach aufgebauten Datentyp verwendet, das Bitmap. Ein Bitmap eignet sich besonders gut für ein kurzes Programm, da es relativ einfach aufgebaut ist und in Delphi gut gekapselt ist. Wie schon gesagt, habe ich als Sprache Objekt Pascal gewählt und das Programm mit Delphi geschrieben. Es ist zumindest unter Delphi 2, 3 und 5 lauffähig und benutzt keine Fremdkomponenten.
Ziel des Programms sollte es sein, einen Text, der sowohl innerhalb des Programms eingegeben als auch aus einer Textdatei gelesen werden kann, innerhalb eines Bitmaps zu verstecken. Das Bitmap kann hierzu in das Programm geladen und auch wieder gespeichert werden.
Zuerst habe ich eine Unit mit Hilfsmethoden für die weitere Codierung geschrieben: UBits. Hierin befinden sich Methoden, einzelne Bits eines Bytes auf „1“ zu setzen (Setze), auf „0“ zu setzen (Entferne) und auszulesen (Erhalte). Weiterhin befinden sich hier Methoden zum Aufteilen eines Pixels in einzelne Farbbestandteile (GetRed, GetBlue, GetGreen) und um diese wieder zusammenzusetzen (RGB). Außerdem enthält es eine Methode, um, wenn die Breite eines Bitmaps gegeben ist (zum Beispiel 11), die Koordinate eines (zum Beispiel des 35.) Pixels zu berechnen ((4;3), Bitmaps beginnen mit (0;0)): GetPixel. Dann habe ich ein einfaches Statusformular in der Unit UStatus geschrieben, um den Fortschritt der Codierung ausgeben zu können. Auf diesen Units aufbauend habe ich dann die eigentlichen Methoden zum Codieren in der Unit UCodiere geschrieben. Diese 3 Methoden sind Codiere, Decodiere und XorCode.
Diese 3 Methoden sind der Hauptbestandteil des Gesamtprogramms. Sie benutzen die Funktionen und Prozeduren der Unit UBits. Der Rest ist eher eine grafische Oberfläche für die Codierung in der Unit UCodiere. Deswegen gehe ich hier im Folgenden überwiegend auf die Prozeduren Codiere und Decodiere ein.

Codieren

Anmerkung: Aus Platzgründen kann der Quelltext hier nur auszugsweise behandelt werden. Der komplette Quelltext befindet sich zum Download am Ende des Dokuments.
Als Parameter für die Codieren-Prozedur wurden nur der zu codierende String und das Bitmap bestimmt, in das der String eingefügt werden soll.

procedure Codiere(aBitmap: TBitmap; Text: string);

Auf die Variabeln werde ich später eingehen, wenn sie gebraucht werden. Zuerst prüft die Methode, ob das Bitmap überhaupt vorhanden ist und der Text Zeichen hat, da ansonsten eine Codierung unmöglich wäre.

if (not aBitmap.empty) and (length(Text)>0) then begin

Nach einer Initialisierung des Statusformulars wird nun begonnen, jedes Zeichen des Textes zu verschlüsseln.

for i:=1 to length(Text) do begin
  aChar:=Text[i];

Zu diesem Zeichen des Ursprungtextes werden nun nacheinander 3 Pixel benutzt, um in ihnen das Zeichen zu speichern. Im ersten und zweiten Pixel wird in jeder Farbinformation das letzte Bit für ein Bit des Buchstabens verwendet. Im dritten Pixel werden nur noch 2 Bit geändert, womit dann alle 8 Bit des Ursprungszeichens umgesetzt wurden.
Das erste der benötigten Pixel wird passend zu dem Buchstaben mit Hilfe der Funktion GetPixel aus der Unit Ubits ermittelt.

Pixel := GetPixel(aBitmap.Width, i*3-2);

Auf dieses Pixel wird nun zugegriffen und es wird in einzelne Farbanteile zerlegt.

Farbe := aBitmap.Canvas.Pixels[Pixel.x, Pixel.y];
FarbCode := GetBlue(Farbe);

Zuerst wird im Blauanteil des Pixels ein Bit versteckt. Dazu wird zuerst das letzte Bit auf „0“ gesetzt, und wenn im Zeichen am entsprechenden Bit (in diesem Fall das erste) eine „1“ steht, wird das Bit auf „1“ gesetzt, ansonsten bleibt es auf „0“ stehen.

Farbcode := Entferne(Farbcode,8);
if Erhalte(byte(aChar), 1) then Farbcode := Setze(FarbCode, 8);

Anschließend wird die Farbe wieder zusammengesetzt.

Farbe := rgb(FarbCode, GetGreen(Farbe), GetRed(Farbe));

Nun wird mit dem nächsten Farbanteil (Grün) und dem nächsten Bit des Zeichens (jetzt das zweite) das selbe gemacht, anschließend mit dem Rotanteil und dem 3. Bit:

FarbCode := GetGreen(Farbe);
Farbcode := Entferne(Farbcode, 8);
if Erhalte(byte(aChar), 2) then Farbcode := Setze(FarbCode, 8);
Farbe := rgb(GetBlue(Farbe), FarbCode, GetRed(Farbe));

FarbCode := GetRed(Farbe);
Farbcode := Entferne(Farbcode,8);
if Erhalte(byte(aChar), 3) then Farbcode := Setze(FarbCode, 8);
Farbe := rgb(GetBlue(Farbe), GetGreen(Farbe), FarbCode);

Nun ist die Arbeit mit diesem Pixel abgeschlossen und es wird zurück in das Bitmap geschrieben.

aBitmap.Canvas.Pixels[Pixel.x, Pixel.y] := Farbe;

Mit den anderen Pixeln wird genauso verfahren, nur dass die Pixel sich anders aus der Laufvariable i berechnen.

Pixel := GetPixel(aBitmap.Width, i*3-1); //für das 2. Pixel
Pixel := GetPixel(aBitmap.Width, i*3);  //für das 3. Pixel

Abschließend wird bei jedem Schleifendurchlauf die Fortschrittsanzeige weitergeschaltet und Nachrichten abgearbeitet. Außerdem ist eine Abbruchbedingung eingebaut.

StatusForm.Progress;
Application.ProcessMessages;
if StatusForm.Abort then break;

Decodieren

Die Decodiere-Methode ist eine Funktion, der nur ein Bitmap übergeben werden muss und der decodierte Text wird dann zurückgegeben.

function DeCodiere(aBitmap: TBitmap): string;

Zuerst folgt eine interne Funktion, die aus drei Farben (drei hintereinanderliegende Farbpixel) wieder ein Zeichen zusammensetzt.

function MakeChar(var Color1, Color2, Color3: TColor): Char;

In einer vorher auf 0 gesetzten lokalen Variablen, die später als Ergebnis übergeben wird, werden, nur falls die entsprechenden letzten Bits der Farbanteile auf „1“ stehen, dann diese Bits auf „1“ gesetzt, so dass der Buchstabe aus den drei Farbanteilen herausgeholt wird.

bResult := 0;

if Erhalte(GetBlue(Color1), 8) then bResult := Setze(bResult, 1);
if Erhalte(GetGreen(Color1), 8) then bResult := Setze(bResult, 2);
if Erhalte(GetRed(Color1), 8) then bResult := Setze(bResult, 3);
if Erhalte(GetBlue(Color2), 8) then bResult := Setze(bResult, 4);
if Erhalte(GetGreen(Color2), 8) then bResult := Setze(bResult, 5);
if Erhalte(GetRed(Color2), 8) then bResult := Setze(bResult, 6);
if Erhalte(GetBlue(Color3), 8) then bResult := Setze(bResult, 7);
if Erhalte(GetGreen(Color3), 8) then bResult := Setze(bResult, 8);
Result := Char(bResult);

In der Hauptmethode wird nach dem Initialisieren des Statusformulars und der lokalen Variablen abgefragt, ob das übergebene Bitmap überhaupt einen Inhalt hat, da ansonsten kein Text herausgeholt werden kann.

if aBitmap<>nil

Nun wird jedes Pixel des Bildes durchlaufen, mit 2 ineinander verschachtelten For-Schleifen.

for i:=0 to aBitmap.Height-1 do begin
    for j:=0 to aBitmap.Width-1

Hierbei ist es notwendig, dass die drei Farbwerte (die später an die Methode MakeChar übergeben werden) mit -1 initialisiert werden. Dies geschieht am Anfang der Funktion. Nun wird überprüft, ob die erste Farbe -1 ist. Wenn ja, dann wird hierin die erste Farbe gespeichert. Ansonsten steht hier schon eine Farbe drin und man wechselt zur Variable für die zweite Farbe. Wenn diese noch nicht gesetzt ist, wird sie mit einer Farbe gefüllt, ansonsten muss zwangsweise die dritte Farbe noch frei sein und kann gefüllt werden.

if color1=-1 then color1 := aBitmap.Canvas.Pixels[j, i]
else if color2=-1 then color2 := aBitmap.Canvas.Pixels[j, i]
else begin
  color3 := aBitmap.Canvas.Pixels[j, i];

Diese drei Farben werden nun an MakeChar übergeben und das Ergebnis von MakeChar, ein neuer Buchstabe, wird nun an das Ergebnis von Decodiere angehängt.

Result := result+MakeChar(Color1, Color2, Color3);

Anschließend werden die drei Farben wieder auf -1 zurückgesetzt, um die nächsten 3 Pixel in den ineinander verschachtelten For-Schleifen aufzunehmen.

Color1 := -1;
Color2 := -1;
Color3 := -1;

Am Ende wird noch das Übliche wie Abbruchbedingung, Nachrichtenverarbeitung und Fortschrittsanzeige durchlaufen.

Oberfläche

Über die Oberfläche und deren Aufbau will ich nicht viel Zeit verlieren, da dies nicht das Thema der Facharbeit ist.
Einfach mit „Bild öffnen“ ein Bild in das Programm laden. Sollte das Bild nicht den Vorgaben entsprechen, wird eine Fehlermeldung angezeigt. Mit „Text öffnen“ kann man eine Textdatei in das Programm laden oder auch selber in das rechte Feld eingeben. Mit dem Button „Text zu Bild“ wird der Text ins Bild hinein verschlüsselt und mit „Bild zu Text“ wird der Text aus dem Bild in das Textfeld decodiert. „Bild speichern“ und „Text speichern“ erklären sich von selber. Im Hauptmenu finden sich diese Punkte mit ein paar kleinen Ergänzungen wieder. Einziges Problem ist es, dass man Text- und Bildfeld nicht verschieben kann, da ich aus Gründen der Kompatibilität mit Delphi2 auf die Splitterkomponente verzichten musste.

Fazit

Mich persönlich hat bei dieser Form der Codierung sehr stark überrascht, wie gut und unbemerkt die Steganographie wirkt. Sie funktioniert entscheidend unauffälliger als dies meine Detektivspiele in jüngeren Jahren taten, als ich mit Zitronensaft Briefe geschrieben habe. Vor allem Geschichten über die Arbeit von, z.B. den polnischen Decodierern der Enigma, haben mich stark fasziniert und ich hatte durch diese Facharbeit die Möglichkeit, wenn auch auf geringerem Niveau und nur aus der Sicht der Codierer, solch einen Wettstreit unter Wissenschaftlern nachzuvollziehen.
Nun, ich hoffe, ich konnte zeigen, welche Mittel die Steganographie anwendet, um Daten sicher zu verstecken. Dass so etwas in einigen Ländern als „Waffe“ verstanden wird, kann ich in einer demokratischen Gesellschaft nicht nachvollziehen. Solch ein Gesetz passt eher in ein totalitäres System. Weiterhin ist solch ein Verbot sinnlos, da man besonders Steganographie versteckt ausführen kann. Es ist sogar der Sinn von Steganographie, nicht auffindbar zu sein. Da man die Leute, die einen großen Aufwand betreiben nicht entdeckt zu werden, selten bis gar nicht ergreifen kann, trifft so etwas eher den normalen Bürger. Organisationen wie Amnesty International oder Greenpeace könnten in Ländern mit einem Kryptographieverbot nicht legal arbeiten, da sie intern PGP benutzen, um nicht vom Staat belauscht werden zu können. Deshalb möchte ich mich hier gegen solch ein Verbot in Deutschland aussprechen. Vielleicht wäre sogar eine ausdrückliche europaweite Kryptographie-Erlaubnis angebracht.