0

Update: Cloud Downloader 3.0

Wenn’s einmal läuft, dann läuft’s! Somit gibt es heute wieder ein Update für den Cloud Downloader. Diesmal lautet die neue Versionsnummer 3.0 – und was fällt dabei auf? Wir haben eine runde Versionsnummer.

Heute gibt es also nicht nur Bugfixes, sondern auch neue Features. Welche Neuerungen das sind, möchte ich nachfolgend kurz zusammenfassen.

Cloud Downloader 3.0 - better search Cloud Downloader 3.0 - file size prediction Cloud Downloader 3.0 - new features

Neue Features

  • In der Suchmaske wird nun angezeigt, wie groß (in MB) alle Titel zusammen sind und wie groß die zum Download ausgewählten Titel sind. Somit kann man vor dem Download schon sehen, ob z.B. alles auf den Lieblings-USB-Stick passt.
  • Die aktuelle Übertragungsgeschwindigkeit wird während des Downloads angezeigt
  • Die verbleibende Zeit sowie die noch herunterzuladene Dateimenge wird während des Downloads angezeigt
  • Um die Wartezeit während des Download zu verkürzen, werde zufällig ausgewählte Katzen-GIFs abgespielt. Jeder mag Katzen, oder?

Bugfixes

  • Die Suchfunktion findet nun beliebig große Playlists und Profile. Zuvor war die Suche auf ~200 Songs pro Link begrenzt. Nun existiert kein Limit mehr.

Ich wünsche euch viel Spaß mit der neuen Version und freue mich wie immer über euer Feedback sowie Verbesserungsvorschläge. Den Download der neuen Version findet ihr wie gewohnt in folgendem Artikel.

Zum Download: Cloud Downloader 3.0

2

Update: Cloud Downloader 2.9.7

Cloud Downloader 2.9.7

Kaum ein paar Tage nach der Veröffentlichung der Version 2.9.6 gibt es nun auch schon wieder eine neue Cloud Downloader Version.

In Version 2.9.7 gibt es, wie in den letzten beiden Updates auch schon, keine neuen Features, sondern Bugfixes.

Da sich die Soundcloud-API im Hintergrund etwas geändert hat, wurden die ID3 Tags nicht mehr vollständig erzeugt. Besonders aufgefallen ist dies durch fehlende Coverbilder. (Vielen Dank an dieser Stelle an die fleißigen Kommentatoren, die mich auf diesen Bug aufmerksam gemacht haben.

Wie dem auch sei. Neue Version, neues Glück. In Version 2.9.7 nun auch wieder mit ID3-Tags und Coverbildern.

Die aktuelle Version findet ihr im Hauptartikel zum Cloud Downloader unter folgendem Link:

Zum Download: Cloud Downloader 2.9.7

8

Update: Cloud Downloader 2.9.6

Cloud Downloader 2.9.6Das letzte Update des Cloud Downloaders liegt nun gut zwei Wochen zurück. Seitdem habe ich wieder einiges an Feedback erhalten. So sind unter anderem ein, zwei Bugs aufgetaucht. Deshalb gibt es heute wieder eine neue Version.

In Version 2.9.6 des Cloud Downloaders gibt es folgende Änderungen:

  • Bisher wurden bei Angabe eines “/likes”-Url nur die einzelnen Songs beachtet. Wenn eine komplette Playlist geliked wurde, so wurden dessen Songs außer acht gelassen. Nun werden alle Songs, auch jene aus Playlists, beachtet
  • Die “/playlists”-Urls aus den Profilseiten konnten nicht ausgelesen werden. Dies klappt nun.
  • Der komplette Code wurde noch einmal überarbeitet, um Abtürze/Fehler besser vermeiden zu können.
  • Ein, zwei Schreibfehler in der Oberfläche wurden behoben

Die aktuelle Version findet ihr wie immer im Hauptartikel zum Cloud Downloader unter folgendem Link:

Zum Download: Cloud Downloader 2.9.6

1

Update: Cloud Downloader 2.9.5

Cloud Downloader 2.9.5Nur ein kurzes Status-Update. Seit heute gibt es den Cloud Downloader in der Version 2.9.5. Wie die Versionsnummer schon ahnen lässt, enthält der Sprung von 2.9 zu 2.9.5 nur kleinere Änderungen.

Neue Features sind in dem aktuellen Release nicht enthalten. Dafür habe ich an der Stabilität des gesamten Programms gearbeitet und den ein oder anderen Bug behoben.

Die aktuelle Version findet ihr wie immer im Hauptartikel zum Cloud Downloader über nachfolgenden Link.

Zum Download: Cloud Downloader 2.9.5

2

Steganographie mit PHP – Dateien in Bildern verstecken

Steganographie mit PHPIn diesem Artikel wollen wir uns mit dem Thema Steganographie beschäftigen und ein kleines Beispiel in PHP implementieren. Denn mit PHP lassen sich längst nicht “nur” Webseiten erstellen oder Formulare umsetzen, was mit Sicherheit auch jeder versierte PHP-Programmierer größerer Webagenturen bestätigen wird.

Neben Funktionen zur Textverarbeitung bringt PHP auch Methoden zur Bildbearbeitung sowie zur Manipulation auf Bit- und Byte-Ebene mit. Und eben diese wollen wir uns heute zunutze machen. Doch bevor es mit dem Coding losgeht, gibt es noch einen kurzen Einstieg in das Thema Steganographie.

Steganographie ist keine Kurzschrift

Wie der Titel bereits angekündigt hat, geht es heute um Steganographie, die Kunst Informationen oder Wissen in einem Trägermedium zu verstecken. Wikipedia definiert Steganographie wie folgt:

Die Steganographie (auch Steganografie) ist die Kunst oder Wissenschaft der verborgenen Speicherung oder Übermittlung von Informationen in einem Trägermedium (Container). Das Wort lässt sich auf die griechischen Bestandteile στεγανός steganós ‚bedeckt‘ und γράφειν gráphein ‚schreiben‘ zurückführen,[1] bedeutet also wörtlich „bedeckt schreiben“ bzw. „geheimes Schreiben“. Das modifizierte Medium wird als Steganogramm bezeichnet.
Quelle: https://de.wikipedia.org/wiki/Steganographie

Das mag auf den ersten Blick jetzt etwas abstrakt klingen, doch eigentlich ist Steganographie gar nicht so schwer zu verstehen. Nehmen wir an, wir sind ein verdeckter Ermittler und haben ein Foto von einem Verbrechen gemacht. Nun wollen wir dieses Foto unerkannt an unseren Feinden vorbeischleusen. Hierzu nehmen wir unser geheimes Foto und verstecken es innerhalb eines harmlosen Fotos. Zum Beispiel einer schönen Landschaftsaufnahme.

Nachdem wir unser geheimes Foto in die Landschaftsaufnahme (das Trägermedium) injiziert haben, wird daraus das sogenannte “Steganogramm”. Die Empfänger unseres Steganogramms nutzen wiederum ihr Wissen darüber, wie wir das Foto versteckt haben, und lesen es aus der Landschaftsaufnahme wieder aus. That’s it! So funktioniert Steganographie in seiner einfachsten Form.

In unserem Artikel wollen wir heute genau dieses Szenario nachbauen. Wir schreiben ein kleines PHP-Script, welches es uns ermöglicht, Dateien in einem Foto zu verstecken und somit unsere eigenen Steganogramme zu erstellen.

Ein Wort zum Ende der Einleitung – auch wenn es ähnlich klingt – Steganographie ist nicht Stenographie. Bei Stenographie handelt es sich um eine aus Symbolen bestehende Kurzschrift, die es ermöglicht, besonders schnell handschriftliche Notizen anzufertigen.

Der Programmablauf in der Theorie

Pixel - Funktionsweise von digitalen BildernDas Script, welches wir heute schreiben wollen, ermöglicht es Bilder in anderen Bildern zu verstecken. Wer den Artikel bis zum Ende durcharbeitet, sollte auch in der Lage sein, das Script so zu erweitern, dass beliebige Dateien in einem Foto versteckt werden können. Doch wie funktioniert dies technisch? Hierzu müssen wir zuerst betrachten, wie Bilder digital gespeichert werden.

Ein digitales Bild besteht aus einer Menge an Bildpunkten, den Pixeln. Jedes einzelne Pixel wiederum hat einen eigenen Farbwert. Diese Farbwerte/Farben setzen sich (in den meisten Fällen) aus den drei Grundfarben Rot, Grün und Blau zusammen.

Jede Grundfarbe wiederum wird mit einem Wert von 0-255 dargestellt und passt somit exakt in 1 Byte. Je höher der Wert der Grundfarbe, umso dominanter ist er in der Farbe des Pixels. Ein reines Rot besteht zum Beispiel aus: “Rot: 255, Grün: 0, Blau: 0”. Ein weißes Pixel wiederum würde als “Rot: 255, Grün: 255, Blau: 255” dargestellt werden.

Nachdem wir nun den grundlegenden Aufbau eines Bildes und die Funktionsweise eines Pixels kennengelernt haben, können wir uns dies für unser Vorhaben zunutze machen. Als Beispiel nehmen wir die Farbe Rot. Wie bereits beschrieben wird Rot als 255, 0, 0 (Rot, Grün, Blau) dargestellt. Ebenso haben wir festgestellt, dass der Wertebereich 0-255 entspricht und in 1 Byte passt. Somit könnten wir die Grundfarbwerte auch binär darstellen. (Wer Hilfe braucht, kann diesen Rechner hier nutzen.)

Farbunterschied 255 vs. 248In binärer Schreibweise würde Rot als 11111111, 00000000, 00000000 dargestellt werden. Eine Eigenschaft der binären Schreibweise ist, dass eine Änderung der Bits, je weiter rechts sie stattfindet, einen immer kleineren Einfluss auf den Gesamtwert hat. Ändert man z.B. die letzten 3 Bit im Rotwert von 111 auf 000, so ändert sich der Dezimalwert von 255 auf 248. Diese Änderung ist mit dem menschlichen Auge kaum wahrnehmbar, wie die nebenstehende Grafik zeigt, die Seite an Seite die beiden Farben zeigt.

Was lernen wir daraus? Wir können also mindestens die letzten 3 Bit eines Farbwertes anpassen, ohne dass dies groß auffällt. Überlegen wir nun weiter. Ein Pixel besteht aus drei Grundfarbwerten. Wenn wir je drei Bits anpassen, dann können wir in einem Pixel 9 Bit anpassen. Dies reicht also aus, um mehr als ein Byte in einem einzelnen Pixel zu codieren. Passen wir bei einer Farbe nur 2 anstelle von 3 Bits an, so kommen wir auf genau ein Byte.

Haben wir nun also ein Foto einer handelsüblichen Kamera, dass mit einer Auflösung von 12 Megapixel aufgenommen wurde, haben wir ein Bild mit 4048 × 3040 = 12.305.920 Pixeln. In jedes Pixel können wir 1 Byte ablegen. Bei 12.305.920 Pixeln können wir also auch 12.305.920 Byte abspeichern, was wiederum ~11,75 Mb entspricht.

Wie wir nun ein Byte der zu versteckenden Datei auf 3 Bytes (Rot-Byte, Grün-Byte, Blau-Byte) eines jeden Pixels aufteilen und warum wir in der Praxis nicht die volle Pixelanzahl in Speicher umwandeln können, folgt im nächsten Abschnitt, in welchem wir uns mit der Implementierung befassen.

Doch bevor es losgeht, noch ein Wort zum Trägermedium, dem Bild, in dem wir unser geheimes Foto verstecken wollen. Das Trägermedium muss zwingend in einem unkomprimierten oder verlustfrei komprimierten Dateiformat wie z.B. PNG, TIFF oder BMP vorliegen. Verlustbehaftete Formate funktionieren für dieses Steganographie-Verfahren nicht, denn wie der Name schon sagt, komprimieren solche Formate wie JPG mit Informationsverlusten. In der Praxis werden z.B. mehrere, farblich ähnliche Pixel zu Blöcken einer einzigen Farbe zusammenfassen. Hier würden uns also Informationen unserer geheimen Nachricht verloren gehen.

Doch nun genug der Theorie. Im folgenden Abschnitt beginnen wir, das nun kennengelernte Konzept in der Praxis umzusetzen.

Die Implementierung

Für die Implementierung werden wir das Script in kleinen Stücken aufbauen und diese einzeln besprechen. Wer den Überblick verliert, kann ans Ende des Beitrags scrollen. Dort befindet sich das komplette Script in einem Block.


//URLs des Trägermediums und der zu versteckenden Datei
$src_container = $_GET['img_container'];
$src_payload = $_GET['payload_file'];

//Bildgröße auslesen und maximale Bytegröße berechnen
$container_size = getimagesize($src_container);
$maxPayloadByte = $container_size[0]*$container_size[1]-4;

In den ersten beiden Code-Zeilen fragen wir die URLs des Trägermediums und der Payload ab. Die URL für das Trägermedium sollte auf eine verlustfrei komprimierte Bilddatei verweisen. (Wie z.B. eine PNG-Grafik.) Die URL für die Payload kann auf jede beliebige Datei verweisen. (Prinzipiell könnten die Dateien auch direkt von der Festplatte gelesen oder aus anderen Quellen bezogen werden. Der Einfachheit halber arbeiten wir jedoch mit URLs, die wir per GET-Parameter einlesen.)

In der dritten Zeile ermitteln wir mit der getimagesize-Funktion einige Werte zum Trägermedium. Besonders interessant sind hierbei die Maße (Breite und Höhe) in Pixeln des Trägermediums.

In der vierten Zeile berechnen wir nun die maximale Dateigröße in Byte, die wir in dem Trägermedium speichern können. Hierzu berechnen wir erst einmal die Anzahl der Pixel, in dem wir die Breite ($container_size[0]) mit der Höhe ($container_size[1]) multiplizieren. Da wir in jedem Pixel ein Byte ablegen können (siehe hierzu den Absatz “Programmablauf in der Theorie”), ergibt sich aus der Anzahl der Pixel also die maximale Speichermenge.

Von dieser Gesamtspeichermenge ziehen wir nun noch 4 Byte (respektive 32 Bit) ab. Diese Speichermenge “reservieren” wir uns, um dort die Dateigröße der zu versteckenden Datei abzulegen. Denn ist diese kleiner als der zur Verfügung stehende Speicher, müssen wir beim Auslesen ja wissen, wie viele Pixel einen Teil der geheimen Nachricht enthalten und wie viele nicht mehr Teil der Nachricht sind.

//Payload in Bytearray schreiben und Größe berechnen
$payloadByteArr = unpack("C*", file_get_contents($src_payload));
$payloadByteSize = count($payloadByteArr);

//Sicherheitsabfrage für Dateigrößen
if ($payloadByteSize > $maxPayloadByte)
{
die('Die Payload ist größer als der Cryptcontainer.');
}

Im nächsten Schritt nutzen wir die unpack-Funktion, um die zu versteckende Datei ($src_payload) in ein Array aus Bytes auszulesen/umzuwandeln. Danach lesen wir dann die Größe dieses Bytearrays ($payloadByteArr) mittels der count-Funktion aus.

Abschließend überprüfen wir, ob die maximale Dateigröße, die in dem Trägermedium versteckt werden kann ($maxPayloadByte), größer als die zu versteckende Datei ($payloadByteSize) ist. Wenn dem nicht so ist, also die geheime Datei nicht in das Trägermedium passt, brechen wir das Script mit einer Fehlermeldung und dem die-Befehl ab.

Ist das Trägermedium ausreichend groß, bereiten wir die Codierung vor. Hierzu legen wir noch ein paar Hilfsvariablen an.


//Trägermedium in Datei lesen und als Bild "öffnen"
$container = file_get_contents($src_container);
$img = imagecreatefromstring($container);
if (!$img) echo "error";

//Payload-Größe in Bytearray umschreiben
$payloadByteSizeArr = array((($payloadByteSize >> 24) & 0xFF),
     (($payloadByteSize >> 16) & 0xFF),
     (($payloadByteSize >> 8) & 0xFF),
     ($payloadByteSize & 0xFF) );

Zuerst lesen wir die geheime Datei in die Variable $container, um im nächsten Schritt mittels der imagecreatefromstring-Funktion ein Bild-Objekt ($img) daraus zu erstellen. Dies ist notwendig, um später die Farbwerte der einzelnen Pixel auslesen und ändern zu können. Sollte es hierbei einen Fehler geben, quittieren wir dies mit der Ausgabe eines Strings mit dem Wert “error”.

Als Nächstes nehmen wir uns die Größenangabe der Payload (=geheime Datei) vor. Diese müssen wir (zusammen mit den geheimen Daten selbst) mit in dem Trägermedium verstecken, um zu wissen, wie viele Pixel mit geheimen Daten versehen sind. Da die Größenangabe jedoch als 32-Bit Integer (Ganzzahl) in der Variable $payloadByteSize steht, wir jedoch in jedem Pixel nur 1 Byte unterbringen können, schieben wir je 8-Bit (=1 Byte) mittels des Bitshiftoperators (>>) und einer Bitmaske (&0xFF) aus dem Integer in ein Byte und legen dieses in dem Array $payloadByteSizeArr ab.

Wer diesem Abschnitt nicht ganz folgen konnte, liest sich am besten noch mal den Einstieg zum Thema Bit-Operatoren durch. Alternativ könnt ihr auch gerne einen Kommentar unter diesen Artikel mit eurer Frage posten. Ich werde dann mein Bestes geben, die Unklarheiten zu beseitigen.

Nun sind alle Vorarbeiten abgeschlossen. Wir haben Bildgrößen ermittelt, die Speichergrößen errechnet und geprüft und die Daten vorbereitet. Kommen wir also zum Codieren der geheimen Nachricht.

Um die Pixel des Trägermediums ($img) einzeln anzusprechen, beginnen wir mit zwei ineinander verschachtelten Schleifen.


//Für jeden Pixel auf der Horizontalen
for($x=0;$x<$container_size[0];$x++)
{
   //Für jeden Pixel auf der Vertikalen
   for($y=0;$y<$container_size[1];$y++)
   {
       //Die ersten 4 Pixel (=Byte) anders behandeln
      if ($y < 4 && $x == 0)
      {
         //Codeblock A
      }
      else
      {
         //Wenn Payload noch nicht vollständig versteckt
         if ((($x*$container_size[1])+$y-3) <= $payloadByteSize)
         {
            //Codeblock B
         }
      }
   }
}

Innerhalb der beiden Schleifen machen wir dann noch eine if-else-Abfrage, um zu ermitteln, ob es sich bei dem aktuellen x-y-Wert um einen der ersten 4 Pixel handelt. Dies machen wir, da wir die ersten vier Pixel nicht mit den Daten der geheimen Nachricht ($payloadByteArr), sondern mit den Größeninformationen der geheimen Nachricht ($payloadByteSizeArr) versehen möchten.

Im else-Block, also wenn es sich nicht um einen der ersten vier Pixel handelt, machen wir noch eine weitere if-Abfrage, die überprüft, ob das aktuelle Pixel, kleiner ist als die Dateigröße der zu versteckenden Datei. Hiermit stellen wir sicher, dass wir nur solange Pixel im Trägermedium manipulieren, wie auch Bytes der zu versteckenden Datei vorhanden sind.

Die eigentliche Manipulation des Bildes findet an den Stellen //Codeblock A und //Codeblock B statt, welche ich in oben stehenden Code-Snippet ausgespart habe, um die Schleifen übersichtlich zu halten. Mit den beiden Codeblöcken wollen wir uns aber nun beschäftigen. Beginnen wir mir Codeblock A…

//Payload größe codieren
$pixel=imagecolorat($img, $x, $y); 
$payloadSubBlock1 = ($payloadByteSizeArr[$y] & 0xE0) >> 5;
$payloadSubBlock2 = ($payloadByteSizeArr[$y] & 0x1C) >> 2;
$payloadSubBlock3 = ($payloadByteSizeArr[$y] & 0x3);
$payloadBlock = $payloadSubBlock1 << 16 | $payloadSubBlock2 << 8 | $payloadSubBlock3;
$pixel = ($pixel & 0xF8F8FC) | $payloadBlock;
imagesetpixel($img, $x, $y, $pixel);

In der ersten Zeile ermitteln wir mittels der imagecolorat-Funktion die Farbe des Pixels im Trägermedium ($img) an der Position x=$x und y=$y. Den Farbwert legen wir in der Variable $pixel ab. Hierbei gibt die imagecolorat-Funktion den Farbwert als 24-Bit Integer zurück. Je 8 der 24 Bit entsprechen einem der drei Farbkanäle Rot, Blau und Grün.

In den folgenden drei Zeilen nehmen wir uns ein Byte des $payloadByteSizeArr-Arrays, welches die Länge der zu speichernden Payload angibt. Dieses Byte zerlegen wir nun in drei Blöcke ($payloadSubBlock1..3). Hierzu nutzen wir sowohl Bit-Masken und den &-Operator als auch Bit-Shifting.

In der nächsten Zeile schieben wir unsere drei Teilblöcke der zu versteckenden Information in einen 24-Bit Integer, um diesen dann in der folgenden Zeile mit dem 24-Bit Integer aus $pixel zu verschmelzen können. In der letzten Zeile des Codeblocks wird die manipulierte Farbe dann mittels der imagesetpixel-Funktion wieder in das Trägermedium zurückgeschrieben.

Da diese Zeilen etwas verwirrend sein können, habe ich folgendes Beispiel für den “Codeblock A” verfasst, das mittels Testdaten den Vorgang noch einmal aufschlüsselt.


//Der Pixel an der Stelle x, y hat die Farbe Grün-Gelb (R=173,G=255,B=47)
$pixel=imagecolorat($img, $x, $y); 
//in Pixel steht nun: 10101101 11111111 00101111

//Das zu codierende Byte lautet:
//$payloadByteSizeArr[$y] -> 10111010
//Die Maske lautet:  0xE0 -> 11100000
$payloadSubBlock1 = ($payloadByteSizeArr[$y] & 0xE0);
//Durch & steht nun in $payloadSubBlock1: 10100000
$payloadSubBlock1 = $payloadSubBlock1 >> 5;
//Durch >> 5 steht in $payloadSubBlock1: 00000101

//0x1C -> 00011100
$payloadSubBlock2 = ($payloadByteSizeArr[$y] & 0x1C);
//Durch & steht nun in $payLoadSubBlock2: 00011000
$payloadSubBlock2 = $payloadSubBlock2 >> 2;
//Durch >> 2 steht in $payloadSubBlock2: 00000110

//0x3 -> 00000011
$payloadSubBlock3 = ($payloadByteSizeArr[$y] & 0x3);
//Durch & steht nun in $payloadSubBlock3: 00000010

//$payloadSubBlock1 << 16 entspricht: 00000101 00000000 00000000
//$payloadSubBlock2 << 8 entspricht:  00000000 00000110 00000000
//$payloadSubBlock3 entspricht:       00000000 00000000 00000010
$payloadBlock = $payloadSubBlock1 << 16 | $payloadSubBlock2 << 8 | $payloadSubBlock3;
//Nach Verknüpfung mit |-Operator: 00000101 00000110 00000010

//$pixel ist:   10101101 11111111 00101111
//0xF8F8FC ist: 11111000 11111000 11111100
//($pixel & 0xF8F8FC) ist: 10101000 11111000 00101100
//Maske löscht je 3 bzw. 2 unrelevanteste Bits des jeweiligen Farbkanal
$pixel = ($pixel & 0xF8F8FC) | $payloadBlock;
//Durch |-Operator werden gecleante Pixel mit $payloadBlock verbunden
//$pixel ist nun: 10101101 11111110 00101110

imagesetpixel($img, $x, $y, $pixel);
//neue Pixelfarbe wurde in Trägermedium gesetzt

Das ist auch schon der ganze Zauber. Kommen wir nun zu “Codeblock B”…


//Payload codieren
$pixel=imagecolorat($img, $x, $y);
$payloadSubBlock1 = ($payloadByteArr[($x*$container_size[1])+$y-3] & 0xE0) >> 5;
$payloadSubBlock2 = ($payloadByteArr[($x*$container_size[1])+$y-3] & 0x1C) >> 2;
$payloadSubBlock3 = ($payloadByteArr[($x*$container_size[1])+$y-3] & 0x3);
$payloadBlock = $payloadSubBlock1 << 16 | $payloadSubBlock2 << 8 | $payloadSubBlock3;
$pixel = ($pixel & 0xF8F8FC) | $payloadBlock;
imagesetpixel($img, $x, $y, $pixel);

Was fällt auf? CodeblockB gleicht CodeblockA bis auf ein kleines Detail. Die Funktionsweise ist bis auf den einen Unterschied komplett gleich, sodass ich an dieser Stelle auf eine weitere Erklärung verzichte.

Der einzige Unterschied liegt darin, wie der Index berechnet wird, an dem das zu versteckende Byte aus dem $payloadByteArr-Array entnommen wird.

In CodeblockA hatten wir an dieser Stelle die Variable $y genommen. Dies geht für die Payload nicht mehr, da wir zu den Spalten noch die Zeilen $x mit einbeziehen müssen. Schließlich wird $y für jede Spalte (jeden Schleifendurchlauf) ja wieder 0, weshalb wir $x in die Index-Berechnung hinzunehmen. Abschließend reduzieren wir den Index noch um 3 Positionen, da der Pixelindex ($x, $y) ja schon durch CodeblockA fortgeschritten ist, wir jedoch für das Auslesen aus $payloadByteArr an dessen Index 0 beginnen wollen.

Das komplette Script

Für die Erklärung war es sicherlich hilfreich das Script in einzelnen Blöcken bzw. zeilenweise zu analysieren. Wer nun jedoch den Überblick verloren hat, der möge folgenden Codeblock anschauen, welcher das komplette Script in einem Block anzeigt.

<?php //URLs des Trägermediums und der zu versteckenden Datei $src_container = $_GET['img_container']; $src_payload = $_GET['payload_file']; //Bildgröße auslesen und maximale Bytegröße berechnen $container_size = getimagesize($src_container); $maxPayloadByte = $container_size[0]*$container_size[1]-4; //Payload in Bytearray schreiben und Größe berechnen $payloadByteArr = unpack("C*", file_get_contents($src_payload)); $payloadByteSize = count($payloadByteArr); //Sicherheitsabfrage für Dateigrößen if ($payloadByteSize > $maxPayloadByte)
{
	die('Die Payload ist gr&ouml;&szlig;er als der Cryptcontainer.');
}

//Trägermedium in Datei lesen und als Bild "öffnen"
$container = file_get_contents($src_container);
$img = imagecreatefromstring($container);
if (!$img) echo "error"; 

//Payload-Größe in Bytearray umschreiben
$payloadByteSizeArr = array((($payloadByteSize >> 24) & 0xFF), 
                             (($payloadByteSize >> 16) & 0xFF), 
                             (($payloadByteSize >> 8) & 0xFF), 
                             ($payloadByteSize & 0xFF) );


//Für jeden Pixel auf der Horizontalen
for($x=0;$x<$container_size[0];$x++)
{
    //Für jeden Pixel auf der Vertikalen
    for($y=0;$y<$container_size[1];$y++)
    { 	
        //Die ersten 4 Pixel (=Byte) anders behandeln
        if ($y < 4 && $x == 0) { //Payload größe codieren $pixel=imagecolorat($img, $x, $y); $payloadSubBlock1 = ($payloadByteSizeArr[$y] & 0xE0) >> 5;
    		$payloadSubBlock2 = ($payloadByteSizeArr[$y] & 0x1C) >> 2;
    		$payloadSubBlock3 = ($payloadByteSizeArr[$y] & 0x3);
    		$payloadBlock = $payloadSubBlock1 << 16 | $payloadSubBlock2 << 8 | $payloadSubBlock3;
    		$pixel = ($pixel & 0xF8F8FC) | $payloadBlock;
    		imagesetpixel($img, $x, $y, $pixel);    	
    	}
    	else 
    	{
            //Wenn Payload noch nicht vollständig versteckt
    		if ((($x*$container_size[1])+$y-3) <= $payloadByteSize) { //Payload codieren $pixel=imagecolorat($img, $x, $y); $payloadSubBlock1 = ($payloadByteArr[($x*$container_size[1])+$y-3] & 0xE0) >> 5;
	    		$payloadSubBlock2 = ($payloadByteArr[($x*$container_size[1])+$y-3] & 0x1C) >> 2;
	    		$payloadSubBlock3 = ($payloadByteArr[($x*$container_size[1])+$y-3] & 0x3);
	    		$payloadBlock = $payloadSubBlock1 << 16 | $payloadSubBlock2 << 8 | $payloadSubBlock3; $pixel = ($pixel & 0xF8F8FC) | $payloadBlock; imagesetpixel($img, $x, $y, $pixel); } } } } header('Content-type: '.$container_size['mime']); imagepng($img); imagedestroy($img); ?>

Kommen wir zum Abschluss des Artikels…

Fazit

Erst mal: “Hut ab und herzlichen Glückwunsch.” Wer es bis zu dieser Stelle des Artikels geschafft hat, hat nicht nur ein funktionierendes Steganographie-Script geschrieben, sondern sicherlich auch etwas gelernt und die “Grauen Zellen” wieder einmal etwas in Schwung gebracht.

Das Script an und für sich ist eigentlich weder lang, noch sehr komplex. Dennoch fasziniert es (zumindest mich), was mit so wenig Zeilen Code möglich ist.

Dem aufmerksamen Leser mag aufgefallen sein, dass wir nur das Script zum Codieren, aber keines zum decodieren geschrieben haben. Dies hat jedoch auch einen Grund. Wer das Thema wirklich verstanden hat, der kann auch das “Umkehr-Script” schreiben. Und wer es nicht verstanden hat, darf mir gerne Kommentare schreiben, solange bis er es verstanden hat. Wer nur auf hastiges Copy’n’Paste aus ist, der guckt heute einfach mal in die Röhre.

Für die ganz Fleißigen noch ein paar Ideen zur Verbesserung des Scripts:

  • Analog zu der Payloadgröße könnte man noch den Dateinamen der Payload mit eincodieren. Somit müsste der Empfänger nicht zwingend das Dateiformat kennen, um die decodierte Nachricht anzuzeigen.
  • Wenn die Payload kleiner als das Trägermedium ist, könnte man entweder nur jedes x-te Pixel mit Payload-Daten versehen oder einfach nur das jeweils letzte Bit eines Farbkanals ändern. So ließe sich die Payload noch unauffälliger verstecken.
  • Die Payload-Bytes könnten vor dem Eincodieren noch verschlüsselt oder zumindest pseudo-zufällig angeordnet werden. (Dies macht es noch schwerer, durch Bildanalysen zu erkennen, ob in dem Trägermedium einer geheimen Datei versehen ist.)

Zum Abschluss noch eine kleine Motivation. Im Artikelbild (erstes Bild, oben links im Artikel) habe ich eine geheime Nachricht (.jpg-Datei) codiert. Wer ein Decoder-Script schreibt und mir als Erster den Inhalt der Nachricht in den Kommentaren nennt, bekommt eine Powerbank von mir geschenkt. (Davon habe ich aus einem anderen Projekt noch ein paar hier rumliegen…)

Seite 1 von 66