
Bilder werden normalerweise als Binärdateien gespeichert, und die Javascript-Datei besteht im Wesentlichen aus einfachem Text. Beide Dateitypen müssen ihren eigenen Regeln folgen: Bilder haben ein bestimmtes Dateiformat, das Daten auf eine bestimmte Weise codiert. Damit Javascript-Dateien ausführbar sind, müssen sie einer bestimmten Syntax folgen. Ich habe mich gefragt: Ist es möglich, eine Bilddatei zu erstellen, die auch eine gültige Javascript-Syntax hat, damit sie ausgeführt werden kann?
Bevor Sie weiterlesen, empfehle ich dringend, diese Code-Sandbox mit den Ergebnissen meiner Experimente zu erkunden :
https://codesandbox.io/s/executable-gif-8yq0j?file=/index.html
Wenn Sie das Bild sehen und selbst untersuchen möchten, Sie können es hier herunterladen:
https://executable-gif.glitch.me/image.gif
Auswahl des richtigen Bildtyps
Leider enthalten Bilder viele Binärdaten, die bei Interpretation als Javascript einen Fehler auslösen. Mein erster Gedanke war also: Was wäre, wenn ich nur alle Bilddaten in einen großen Kommentar einfügen würde?
/*ALL OF THE BINARY IMAGE DATA*/
Dies ist eine gültige Javascript-Datei. Bilddateien müssen jedoch mit einer bestimmten Bytesequenz beginnen, einem für das Bildformat spezifischen Dateikopf. Beispielsweise müssen PNG-Dateien immer mit der Bytefolge 89 50 4E 47 0D 0A 1A 0A beginnen . Wenn das Bild mit beginnt
/*, ist die Datei keine Bilddatei mehr.
Dieser Dateikopf führte mich zu folgender Idee: Was ist, wenn wir diese Folge von Bytes als Variablennamen verwenden und ihr den Wert einer langen Zeichenfolge zuweisen:
PNG=`ALL OF THE BINARY IMAGE DATA`;
Wir verwenden Vorlagenzeichenfolgen anstelle von regulären Zeichenfolgen
"oder 'weil Binärdaten Zeilenumbrüche enthalten können und Vorlagenzeichenfolgen besser mit ihnen umgehen können.
Leider enthalten die meisten Byte-Sequenzen in Bilddatei-Headern nicht druckbare Zeichen, die nicht in Variablennamen verwendet werden können. Es gibt jedoch ein Format, das wir verwenden können: GIF. Der GIF-Headerblock sieht aus wie 47 49 46 38 39 61 , der bequem in die ASCII-Zeichenfolge GIF89a konvertiert wird - absolut gültiger Variablenname!
Auswahl der richtigen Bildgröße
Nachdem wir ein Bildformat gefunden haben, das mit einem gültigen Variablennamen beginnt, müssen wir das Gleichheitszeichen und den Backtick hinzufügen. Daher lauten die nächsten vier Bytes der Datei: 3D 09 60 04

Erste Bytes des Bildes
Im GIF-Format definieren die vier Bytes nach dem Header die Abmessungen des Bildes. Wir müssen sie in 3D (Gleichheitszeichen) und 60 (Backtick, Öffnen einer Linie) einpassen. GIF verwendet die Little-Endian-Reihenfolge, sodass das zweite und vierte Zeichen einen großen Einfluss auf die Bildgröße haben. Sie sollten so klein wie möglich sein, damit das Bild nicht zehntausende Pixel breit und hoch ist. Daher müssen wir große 3D- Bytes und 60 in niedrigstwertigen Bytes speichern .
Das zweite Byte der Bildbreite muss ein gültiges Leerzeichen sein, da es ein Leerzeichen zwischen dem Gleichheitszeichen und dem Zeilenanfang ist
GIF89a= `...... Es ist auch zu beachten, dass der hexadezimale Zeichencode so klein wie möglich sein sollte, da sonst das Bild sehr groß wird.
Das kleinste Leerzeichen ist 09 (horizontales Tabulatorzeichen). Es gibt uns die Breite des 3D 09- Bildes , die 2365 in Little Endian ist; etwas breiter als ich möchte, aber immer noch vollkommen akzeptabel.
Für das zweite Byte der Höhe können Sie einen Wert auswählen, der ein gutes Seitenverhältnis ergibt. Ich habe 04 gewählt , was eine Höhe von 60 04 oder 1120 Pixel ergibt .
Einfügen des Skripts in die Datei
Bisher macht unser ausführbares GIF fast nichts. Es weist der globalen Variablen einfach eine
GIF89alange Zeichenfolge zu . Wir möchten, dass etwas Interessantes passiert! Die meisten Daten im GIF werden zum Codieren des Bildes verwendet. Wenn wir also versuchen, dort Javascript einzufügen, ist das Bild wahrscheinlich stark verzerrt. Aus irgendeinem Grund enthält das GIF-Format jedoch die sogenannte Kommentarerweiterung . Hier können Metadaten gespeichert werden, die vom GIF-Decoder nicht interpretiert werden - der perfekte Ort für unsere Javascript-Logik.
Diese Kommentarerweiterung befindet sich direkt nach der GIF-Farbkarte. Da wir dort jeden Inhalt ablegen können, können wir die GIF89a- Zeile problemlos schließenFügen Sie das gesamte Javascript hinzu und starten Sie dann einen mehrzeiligen Kommentarblock, damit der Rest des Bildes den Javascript-Parser nicht beeinflusst.
Letztendlich könnte unsere Datei so aussehen:
GIF89a= ` BINARY COLOR TABLE DATA ... COMMENT BLOCK:
`;alert("Javascript!");/*
REST OF THE IMAGE */
Es gibt jedoch eine kleine Einschränkung: Obwohl der Kommentarblock selbst eine beliebige Größe haben kann, besteht er aus mehreren Unterblöcken, und die maximale Größe von jedem von ihnen beträgt 255. Zwischen den Unterblöcken befindet sich ein Byte, das die Länge des nächsten Unterblocks bestimmt. Um ein großes Skript dort unterzubringen, muss es daher in kleine Fragmente unterteilt werden, etwa wie folgt:
alert('Javascript');/*0x4A*/console.log('another subblock');/*0x1F*/...
Die hexadezimalen Codes in den Kommentaren sind Bytes, die die Größe des nächsten Unterblocks bestimmen. Sie sind nicht spezifisch für Javascript, werden jedoch für das GIF-Dateiformat benötigt. Um den Rest des Codes nicht zu stören, müssen sie in Kommentare eingefügt werden. Ich habe ein kleines Skript geschrieben, das Skriptfragmente verarbeitet und zur Bilddatei hinzufügt:
https://gist.github.com/SebastianStamm/c2433819cb9e2e5af84df0904aa43cb8
Bereinigen von Binärdaten
Nachdem wir nun die Grundstruktur haben, müssen wir sicherstellen, dass die binären Bilddaten die Syntax des Codes nicht durcheinander bringen. Wie im vorherigen Abschnitt erwähnt, ist die Datei in drei Abschnitte unterteilt: Der erste ist die Zuordnung zur Variablen GIF89a , der zweite ist der Javascript-Code und der dritte ist ein mehrzeiliger Kommentar.
Schauen wir uns den ersten Teil über das Zuweisen eines Werts zu einer Variablen an:
GIF89a= ` BINARY DATA `;
Wenn die Binärdaten ein Zeichen
`oder eine Kombination von Zeichen enthalten ${, liegt ein Problem vor, da sie entweder die Musterzeichenfolge beenden oder einen ungültigen Ausdruck erstellen. Das Update ist ziemlich einfach: Ändern Sie einfach die Binärdaten! Beispielsweise können Sie anstelle von Zeichen `(Hex 60 ) Zeichen a(Hex 61 ) verwenden. Da dieser Teil der Datei eine Farbpalette enthält, führt dies zu geringfügigen Änderungen bei einigen Farben, z. B. um eine Farbe #286148anstelle von zu verwenden #286048. Es ist unwahrscheinlich, dass jemand den Unterschied bemerkt.
Umgang mit Verzerrungen
Am Ende des Javascript-Codes haben wir einen mehrzeiligen Kommentar geöffnet, damit die binären Bilddaten die Javascript-Analyse nicht beeinflussen:
alert("Script done");/*BINARY IMAGE DATA ...
Wenn die Bilddaten eine Folge von Zeichen enthalten
*/, wird der Kommentar vorzeitig beendet, wodurch die Javascript-Datei ungültig wird. Auch hier können wir eines der beiden Zeichen manuell ändern, damit der Kommentar nicht beendet wird. Da wir uns jetzt jedoch im Abschnitt des codierten Bildes befinden, wird das Ergebnis ein beschädigtes Bild sein, zum Beispiel Folgendes:

Beschädigtes Bild
Im schlimmsten Fall wird das Bild möglicherweise überhaupt nicht angezeigt. Durch sorgfältige Auswahl des zu invertierenden Bits konnte ich die Verzerrung minimieren. Glücklicherweise gab es nur wenige Fälle von schädlichen Kombinationen
*/. Es gibt immer noch leichte Verzerrungen im fertigen Bild, zum Beispiel am Ende der Zeile "Valid Javascript File", aber insgesamt bin ich mit dem Ergebnis ziemlich zufrieden.
Beenden der Datei
Wir bleiben bei der letzten Operation - der Fertigstellung der Datei. Die Datei muss mit 00 3B Bytes enden , daher müssen wir den Kommentar vorzeitig beenden. Da dies das Ende der Datei ist und potenzielle Schäden subtil wären, habe ich nur den Blockkommentar beendet und einen einzeiligen Kommentar hinzugefügt, damit das Ende der Datei keine Analyseprobleme verursacht:
/* BINARY DATA*/// 00 3B
Überzeugen Sie den Browser, das Bild auszuführen
Nach all dem haben wir endlich eine Datei, die sowohl ein Bild als auch eine gültige Javascript-Datei ist. Wir müssen jedoch das letzte Hindernis überwinden: Wenn wir ein Bild auf den Server hochladen und versuchen, es in einem Tag zu verwenden
script, wird höchstwahrscheinlich ein ähnlicher Fehler angezeigt:
Die Ausführung des Skripts von ' http: // localhost: 8080 / image.gif ' wurde abgelehnt , da der MIME-Typ ('image / gif') nicht ausführbar ist. [Weigerung, ein Skript von ' http: // localhost: 8080 / image.gif ' auszuführen , da sein MIME-Typ nicht ausführbar ist.]
Das heißt, der Browser sagt zu Recht: "Dies ist ein Bild, ich werde es nicht ausführen!" In den meisten Fällen ist dies durchaus angemessen. Aber wir wollen es trotzdem erfüllen. Die Lösung besteht darin, dem Browser einfach nicht mitzuteilen, dass es sich um ein Bild handelt. Zu diesem Zweck habe ich einen kleinen Server geschrieben, der ein Bild ohne Header-Informationen bereitstellt.
Ohne die Informationen vom Typ MIME aus dem Header weiß der Browser nicht, dass es sich um ein Bild handelt, und macht genau das, was im Kontext am besten ist: Rendern Sie es als Bild in einem Tag
<img>oder führen Sie es als Javascript in einem Tag aus <script>.
Aber ... warum ist das alles?
Ich habe es selbst noch nicht herausgefunden. Eine solche Aufgabe ist eine gute Aufwärmübung für den Geist, aber wenn Sie sich eine Situation vorstellen können, in der sie wirklich nützlich sein kann, dann lassen Sie es mich wissen!
Werbung
Bei Servern für Entwickler geht es um virtuelle Server aus unserem Unternehmen.
Seit langem verwenden wir ausschließlich schnelle Server-Laufwerke von Intel und sparen keine Hardware - nur Markengeräte und die modernsten Lösungen auf dem Markt für die Bereitstellung von Diensten.
