LSB Steganographie

Vor langer Zeit schrieb ich meinen ersten Beitrag auf Habré. Dieser Beitrag widmete sich einem sehr interessanten Problem, nämlich der Steganografie. Natürlich kann die in diesem alten Thema vorgeschlagene Lösung nicht als echte Steganografie bezeichnet werden. Es ist nur ein Spiel mit Dateiformaten, aber dennoch ein ziemlich interessantes Spiel.

Heute werden wir versuchen, ein wenig tiefer zu graben und den LSB-Algorithmus zu betrachten. Wenn Sie daran interessiert sind, sind Sie herzlich eingeladen, weiterzulesen. (Im Folgenden beträgt der Traffic etwa ein Megabyte.)

Zunächst ist es notwendig, eine kleine Einführung zu geben. Allen ist bekannt, dass das Ziel der Kryptografie darin besteht, das Lesen geheimer Informationen unmöglich zu machen. Natürlich hat die Kryptografie ihre Anwendungsbereiche, aber es gibt auch einen anderen Ansatz zum Schutz von Daten. Man kann Informationen nicht verschlüsseln, sondern so tun, als hätten wir sie nicht. Genau dafür wurde die Steganografie erfunden. Wikipedia versichert uns, dass "Steganografie (vom griechischen στεγανοσ — verborgen und γραφω — ich schreibe, wörtlich 'Geheimschrift') die Wissenschaft der geheimen Übertragung von Informationen ist, indem man das Vorhandensein der Übertragung selbst geheim hält.

Natürlich verbietet niemand die Kombination von kryptografischen und steganografischen Methoden. Im Gegenteil, in der Praxis wird dies so gemacht, aber unsere Aufgabe ist es, die Grundlagen zu verstehen. Wenn man den Wikipedia-Artikel genau studiert, kann man herausfinden, dass in den Algorithmen der Steganografie von einem sogenannten Container und einer Nachricht die Rede ist. Der Container ist jede Information, die hilft, unsere geheime Nachricht zu verbergen.

In unserem Fall wird das Containerformat ein Bild im BMP-Format sein. Zuerst betrachten wir die Struktur dieser Datei. Die Datei kann grob in 4 Teile unterteilt werden: Dateikopf, Bildkopf, Palette und das eigentliche Bild. Für unsere Zwecke müssen wir nur wissen, was im Header geschrieben ist.

Die ersten beiden Bytes des Headers sind die Signatur BM, danach wird in einem DWORD die Dateigröße in Bytes angegeben, die nächsten 4 Bytes sind reserviert und sollten Nullen enthalten, und schließlich wird in einem weiteren DWORD der Offset vom Beginn der Datei bis zu den eigentlichen Bildbytes angegeben. In einer 24-Bit-BMP-Datei wird jeder Pixel durch drei Bytes BGR kodiert.

Jetzt wissen wir, wie wir auf das Bild zugreifen können, und müssen nur noch herausfinden, wie wir die benötigten Informationen dort speichern können. Dazu nutzen wir die LSB-Methode. Der Kern der Methode besteht darin, dass wir die letzten Bits in den Bytes, die für die Farbcodierung zuständig sind, ersetzen. Angenommen, wenn das nächste Byte unserer geheimen Nachricht – 11001011 ist und die Bytes im Bild –…11101100 01001110 01111100 0101100111… sind, würde die Kodierung so aussehen. Wir zerlegen das Byte der geheimen Nachricht in 4 zwei-Bit-Teile: 11, 00, 10, 11, und ersetzen die letzten Bits des Bildes mit den gewonnenen Fragmenten: …11101111 01001100 01111110 0101100111…. Solch eine Änderung ist für das menschliche Auge in der Regel nicht wahrnehmbar. Darüber hinaus können viele alte Ausgabegeräte solche unbedeutenden Veränderungen nicht einmal darstellen.

Es ist klar, dass wir nicht nur 2 letzte Bits ändern können, sondern auch beliebig viele. Hier gibt es eine folgende Regelmäßigkeit: Je mehr Bits wir ändern, desto mehr Informationen können wir verstecken, und desto größere Störungen verursacht dies im ursprünglichen Bild. Zur Veranschaulichung hier zwei Bilder:

LSB Steganographie
LSB Steganographie

So sehr ich es mir auch wünsche, ich konnte den Unterschied zwischen ihnen nicht erkennen, dennoch wurde im zweiten Bild durch die beschriebene Methode das Gedicht von Lewis Carroll „Die Jagd nach dem Snark“ verborgen. Wenn Sie bis hierher gelesen haben, sind Sie wahrscheinlich auch an der Implementierung interessiert. Diese ist ziemlich einfach, aber ich warne Sie sofort, dass alles in Delphi gemacht wurde. Dafür gibt es zwei Gründe: 1. Ich halte Delphi für eine gute Programmiersprache; 2. Dieses Programm entstand im Zuge der Vorbereitung eines Kurses über die Grundlagen des maschinellen Sehens, und die Teilnehmer, für die ich diesen Kurs halte, können bisher nichts anderes als Delphi. Für diejenigen, die mit der Syntax nicht vertraut sind, sollte ich eine Sache erklären: shl x – bitweises Verschieben nach links um x, shr x – bitweises Verschieben nach rechts um x.

Angenommen, wir speichern einen Text in der Container-Variable, der in einer Zeichenkette gespeichert ist, und ersetzen die letzten zwei Bytes:
Code zum Schreiben:

for i:=1 to length(str) do
    begin
      l1:=byte(str[i]) shr 6;
      l2:=byte(str[i]) shl 2; l2:=l2 shr 6;
      l3:=byte(str[i]) shl 4; l3:=l3 shr 6;
      l4:=byte(str[i]) shl 6; l4:=l4 shr 6;
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l1;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l2;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l3;
      f.WriteBuffer(tmp,1);
 
      f.ReadBuffer(tmp,1);
      f.Position:=f.Position-1;
      tmp:=((tmp shr 2) shl 2)+l4;
      f.WriteBuffer(tmp,1);
 
    end;

Code zum Auslesen:

for i:=1 to MsgSize do
    begin
      f.ReadBuffer(tmp,1);
      l1:=tmp shl 6;
      f.ReadBuffer(tmp,1);
      l2:=tmp shl 6; l2:=l2 shr 2;
      f.ReadBuffer(tmp,1);
      l3:=tmp shl 6; l3:=l3 shr 4;
      f.ReadBuffer(tmp,1);
      l4:=tmp shl 6; l4:=l4 shr 6;
      str:=str+char(l1+l2+l3+l4);
    end;

Und für die ganz Faulen – Link zur Software und ihrem Quellcode.

Danke.

Quelle: habr.com

60GB SSD 8Gb DDR4