C# Quellcode - 9.43 Kb

Worum geht es?

Dieser Artikel versucht zu erklären, wie beliebige Daten im Rauschen von Bildern versteckt werden können.

Es gibt nur einen sicheren Ort für private Daten: Den Ort, an dem niemand danach sucht.
Eine mit Algorithmen wie PGP verschlüsselte Datei ist nicht lesbar, aber jeder sieht sofort, dass etwas versteckt wird. Wäre es nicht gut, wenn jeder Deine verschlüsselten Dateien öffnen könnte, und anstelle der Geheimnisse unscharfe Fotos vom letzten Urlaub sehen würde? Kaum jemand würde nach Teilen verschlüsselter Nachrichten in den Pixel-Werten suchen.

Wie es funktioniert

Um zu sehen, wie die Anwendung arbeitet, solltest Du den Quellcode anschauen.
Allgemein dargestellt werden Nachrichten so versteckt:

Und so wieder extrahiert:

Zuerst schreibt das Programm die Länge der Nachricht ins erste Pixel. Wir brauchen diesen Wert später, um die Nachricht wieder zu extrahieren.

messageLength = (Int32)messageStream.Length;

//hier werden eventuelle Konflikte abgefangen
//...
//Länge der Nachricht ins erste Pixel schreiben
int colorValue = messageLength;
int red = colorValue >> 2;
colorValue -= red << 2;
int green = colorValue >> 1;
int blue = colorValue - (green << 1);
pixelColor = Color.FromArgb(red, green, blue);
bitmap.SetPixel(0,0, pixelColor);

Anschließend liest es ein Byte aus dem Schlüssel-Stream, um die Position des nächsten zu verwendenden Pixels zu berechnen:

//mit dem zweiten Pixel anfangen
Point pixelPosition = new Point(1,0);

//Für jedes Byte der Nachricht
for(int messageIndex=0; messageIndex<messageLength; messageIndex++){
        //repeat the key, if it is shorter than the message
        if(keyStream.Position == keyStream.Length){
        keyStream.Seek(0, SeekOrigin.Begin);
}
//Nächste Pixel-Anzahl aus dem Schlüssel lesen, "1" für 0 verwenden
currentStepWidth = keyStream.ReadByte() + 1;

//Zeile wechseln, falls aktuelle Schrittweite den Rand überschreitet
while(currentStepWidth > bitmapWidth){
        currentStepWidth -= bitmapWidth;
        pixelPosition.Y++;
}

//Position horizontal versetzen
if((bitmapWidth - pixelPosition.X) < currentStepWidth){
        pixelPosition.X = currentStepWidth - (bitmapWidth - pixelPosition.X);
        pixelPosition.Y++;
}else{
        pixelPosition.X += currentStepWidth;
}

Jetzt lesen wir das Pixel und ersetzen eine Farb-Komponente mit dem Nachrichten-Byte (oder alle Komponenten, wenn schwarz/weisses Rauschen entstehen soll):

 //Farbe des "sauberen" Pixels lesen
 pixelColor = bitmap.GetPixel(pixelPosition.X, pixelPosition.Y);

 //Um etwas Verwirrung hinzuzufügen, wird das Byte mit der aktuellen Schrittweite kombiniert
 int currentByte = messageStream.ReadByte() ^ currentKeyByte;

 if(useGrayscale){
  pixelColor = Color.FromArgb(currentByte, currentByte, currentByte);
 }else{
  //Eine Farb-Komponente ersetzen
  SetColorComponent(ref pixelColor, currentColorComponent, currentByte);
  //Für das nächste Byte eine andere Komponente verwenden
  currentColorComponent = (currentColorComponent==2) ? 0 : (currentColorComponent+1);
 }
} //Ende "for" - weiter zum nächsten Byte

Wenn das Programm eine versteckte Nachricht ausliest, liest es die Länge der Nachricht und die Farb-Komponenten, anstatt sie in die Pixel zu schreiben. So wird die Länge aus dem ersten Pixel gelesen:

pixelColor = bitmap.GetPixel(0,0);
messageLength = (pixelColor.R << 2) + (pixelColor.G << 1) + pixelColor.B;
messageStream = new MemoryStream(messageLength);

Die Pixel-Koordinaten werden genauso bestimmt wie bereits beschrieben. Anschließend wird das versteckte Byte aus dem Farbwert gelesen:

 //Farbe des geänderten Pixels lesen
 pixelColor = bitmap.GetPixel(pixelPosition.X, pixelPosition.Y);
 //Das ersteckte Nachrichten-Byte aus der Farbe extrahieren
 byte foundByte = (byte)(currentKeyByte ^ GetColorComponent(pixelColor, currentColorComponent));
 messageStream.WriteByte(foundByte);
 //Für das nächste Byte eine andere Komponente verwenden
  currentColorComponent = (currentColorComponent==2) ? 0 : (currentColorComponent+1);
} //Ende "for" - weiter zum nächsten Byte