Une image qui est aussi un code Javascript



Les images sont généralement stockées sous forme de fichiers binaires et le fichier Javascript est essentiellement du texte brut. Les deux types de fichiers doivent suivre leurs propres règles: les images ont un format de fichier spécifique qui encode les données d'une manière spécifique. Pour que les fichiers Javascript soient exécutables, ils doivent suivre une syntaxe spécifique. Je me suis demandé: est-il possible de créer un fichier image qui est à la fois une syntaxe Javascript valide afin qu'il puisse être exécuté?



Avant de continuer à lire, je vous recommande vivement d'explorer ce bac à sable de code avec les résultats de mes expériences:



https://codesandbox.io/s/executable-gif-8yq0j?file=/index.html



Si vous souhaitez afficher l'image et l'explorer vous-même, alors Vous pouvez le télécharger à partir d'ici:



https://executable-gif.glitch.me/image.gif



Choisir le bon type d'image



Malheureusement, les images contiennent beaucoup de données binaires qui, lorsqu'elles sont interprétées comme Javascript, génèrent une erreur. Ma première pensée a donc été la suivante: et si je mettais simplement toutes les données d'image dans un gros commentaire, quelque chose comme ceci:



/*ALL OF THE BINARY IMAGE DATA*/


Ce sera un fichier Javascript valide. Cependant, les fichiers image doivent commencer par une séquence spécifique d'octets, un en-tête de fichier spécifique au format d'image. Par exemple, les fichiers PNG doivent toujours commencer par la séquence d'octets 89 50 4E 47 0D 0A 1A 0A . Si l'image commence par /*, le fichier n'est plus un fichier image.



Cet en-tête de fichier m'a conduit à l'idée suivante: et si nous utilisons cette séquence d'octets comme nom de variable et lui attribuons la valeur d'une longue chaîne:



PNG=`ALL OF THE BINARY IMAGE DATA`;


Nous utilisons des chaînes de modèle au lieu de chaînes régulières, "ou 'parce que les données binaires peuvent contenir des sauts de ligne et que les chaînes de modèle s'en tirent mieux.



Malheureusement, la plupart des séquences d'octets dans les en-têtes de fichiers image contiennent des caractères non imprimables qui ne peuvent pas être utilisés dans les noms de variables. Mais il existe un format que nous pouvons utiliser: GIF. Le bloc d'en-tête GIF ressemble à 47 49 46 38 39 61 , qui est commodément converti en chaîne ASCII GIF89a - un nom de variable absolument valide!



Choisir la bonne taille d'image



Maintenant que nous avons trouvé un format d'image qui commence par un nom de variable valide, nous devons ajouter le signe égal et le backtick. Par conséquent, les quatre octets suivants du fichier seront: 3D 09 60 04





Premiers octets de l'image



Au format GIF, les quatre octets après l'en-tête définissent les dimensions de l'image. Nous devons y intégrer 3D (signe égal) et 60 (backtick, ouverture d'une ligne). GIF utilise un ordre peu endian, de sorte que les deuxième et quatrième caractères ont un impact énorme sur la taille des images. Ils doivent être aussi petits que possible pour que l'image ne soit pas large et haute de dizaines de milliers de pixels. Par conséquent, nous devons stocker de gros octets 3D et 60 dans les octets les moins significatifs.



Le deuxième octet de la largeur de l'image doit être un espace blanc valide, car ce sera un espace entre le signe égal et le début de la ligneGIF89a= `...... Il convient également de rappeler que le code de caractère hexadécimal doit être aussi petit que possible, sinon l'image sera énorme.



Le plus petit caractère d'espace blanc est 09 (caractère de tabulation horizontale). Cela nous donne la largeur de l'image 3D 09 , qui est de 2365 en petit boutien; un peu plus large que je ne le souhaiterais, mais toujours parfaitement acceptable.



Pour le deuxième octet de la hauteur, vous pouvez choisir une valeur qui donne un bon rapport hauteur / largeur. J'ai choisi 04 , ce qui nous donne une hauteur de 60 04 , soit 1120 pixels.



Mettre le script dans le fichier



Jusqu'à présent, notre GIF exécutable ne fait presque rien. Il affecte simplement une GIF89alongue chaîne à la variable globale . Nous voulons que quelque chose d'intéressant se produise! La plupart des données contenues dans GIF sont utilisées pour encoder l'image, donc si nous essayons d'y insérer du Javascript, l'image sera probablement fortement déformée. Mais pour une raison quelconque, le format GIF contient quelque chose appelé l' extension de commentaire . C'est un endroit pour stocker des métadonnées qui ne sont pas interprétées par le décodeur GIF - l'endroit idéal pour notre logique Javascript.



Cette extension de commentaire se trouve juste après le nuancier GIF. Puisque nous pouvons y mettre n'importe quel contenu, nous pouvons facilement fermer la ligne GIF89a, ajoutez tout le Javascript, puis démarrez un bloc de commentaires sur plusieurs lignes afin que le reste de l'image n'affecte pas l'analyseur Javascript.



En fin de compte, notre fichier peut ressembler à ceci:



GIF89a= ` BINARY COLOR TABLE DATA ... COMMENT BLOCK:

`;alert("Javascript!");/*

REST OF THE IMAGE */


Cependant, il y a une petite limitation: bien que le bloc de commentaires lui-même puisse être de n'importe quelle taille, il se compose de plusieurs sous-blocs, et la taille maximale de chacun d'eux est de 255. Il y a un octet entre les sous-blocs qui détermine la longueur du sous-bloc suivant. Par conséquent, pour y insérer un gros script, il doit être divisé en petits fragments, quelque chose comme ceci:



alert('Javascript');/*0x4A*/console.log('another subblock');/*0x1F*/...


Les codes hexadécimaux dans les commentaires sont des octets qui déterminent la taille du sous-bloc suivant. Ils ne sont pas spécifiques à Javascript, mais sont obligatoires pour le format de fichier GIF. Pour éviter d'interférer avec le reste du code, ils doivent être placés dans des commentaires. J'ai écrit un petit script qui traite les fragments de script et les ajoute au fichier image:



https://gist.github.com/SebastianStamm/c2433819cb9e2e5af84df0904aa43cb8



Nettoyage des données binaires



Maintenant que nous avons la structure de base, nous devons nous assurer que les données d'image binaire ne gâchent pas la syntaxe du code. Comme mentionné dans la section précédente, le fichier est divisé en trois sections: la première est l'affectation à la variable GIF89a , la seconde est le code Javascript et la troisième est un commentaire sur plusieurs lignes.



Jetons un coup d'œil à la première partie sur l'attribution d'une valeur à une variable:



GIF89a= ` BINARY DATA `;


Si les données binaires contiennent un caractère `ou une combinaison de caractères ${, nous avons un problème, car elles mettront fin à la chaîne de modèle ou créeront une expression non valide. Le correctif est assez simple: changez simplement les données binaires! Par exemple, au lieu du caractère `(hex 60 ), vous pouvez utiliser le caractère a(hex 61 ). Étant donné que cette partie du fichier contient une palette de couleurs, cela entraînera des modifications mineures dans certaines couleurs, par exemple, l'utilisation d'une couleur à la #286148place de #286048. Il est peu probable que quiconque remarque la différence.



Gérer la distorsion



À la fin du code Javascript, nous avons ouvert un commentaire multiligne afin que les données de l'image binaire n'affectent pas l'analyse Javascript:



alert("Script done");/*BINARY IMAGE DATA ...


Si les données d'image contiennent une séquence de caractères */, le commentaire se terminera prématurément, rendant le fichier Javascript invalide. Là encore, on peut modifier manuellement l'un des deux caractères pour qu'ils ne terminent pas le commentaire. Cependant, puisque nous sommes maintenant dans la section de l'image encodée, le résultat sera une image endommagée, par exemple ceci:





Image endommagée



Dans le pire des cas, l'image peut ne pas s'afficher du tout. En choisissant soigneusement le bit à inverser, j'ai pu minimiser la distorsion. Heureusement, il n'y a eu que quelques cas de combinaisons dommageables */. Il y a encore de légères distorsions dans l'image finale, par exemple, en bas de la ligne "Fichier Javascript valide", mais dans l'ensemble je suis assez satisfait du résultat.



Terminer le fichier



Il nous reste la dernière opération - l'achèvement du fichier. Le fichier doit se terminer par 00 3B octets , nous devons donc terminer le commentaire tôt. Comme il s'agit de la fin du fichier et que tout dommage potentiel ne sera pas particulièrement perceptible, je viens de terminer le commentaire de bloc et d'ajouter un commentaire d'une ligne afin que la fin du fichier ne pose pas de problèmes d'analyse:



/* BINARY DATA*/// 00 3B


Persuader le navigateur d'exécuter l'image



Maintenant, après tout cela, nous avons enfin un fichier qui est à la fois une image et un fichier Javascript valide. Cependant, nous devons surmonter le dernier obstacle: si nous téléchargeons une image sur le serveur et essayons de l'utiliser dans une balise script, nous obtiendrons très probablement une erreur similaire:



Refus d'exécuter le script depuis ' http: // localhost: 8080 / image.gif ' car son type MIME ('image / gif') n'est pas exécutable. [Refus d'exécuter le script à partir de ' http: // localhost: 8080 / image.gif ' car son type MIME n'est pas exécutable.]


Autrement dit, le navigateur dit à juste titre: "Ceci est une image, je ne l'exécuterai pas!" Et dans la plupart des cas, c'est tout à fait approprié. Mais nous voulons toujours y parvenir. La solution est simplement de ne pas dire au navigateur qu'il s'agit d'une image. Pour cela, j'ai écrit un petit serveur qui sert une image sans informations d'en-tête.



Sans les informations de type MIME de l'en-tête, le navigateur ne sait pas qu'il s'agit d'une image et fait exactement ce qu'il y a de mieux dans le contexte: la restituer sous forme d'image dans une balise, <img>ou l'exécuter en Javascript dans une balise <script>.



Mais ... pourquoi est-ce tout?



Je ne l'ai pas encore compris moi-même. Une telle tâche est un bon échauffement pour l'esprit, mais si vous pouvez penser à une situation dans laquelle cela peut vraiment être utile, faites-le moi savoir!






La publicité



Les serveurs pour les développeurs concernent les serveurs virtuels de notre société.

Pendant longtemps, nous utilisons exclusivement des lecteurs de serveur rapides d'Intel et n'économisons pas sur le matériel - uniquement des équipements de marque et les solutions les plus modernes du marché pour la fourniture de services.






All Articles