Una imagen que también es un código Javascript



Las imágenes generalmente se almacenan como archivos binarios y el archivo Javascript es esencialmente texto sin formato. Ambos tipos de archivos deben seguir sus propias reglas: las imágenes tienen un formato de archivo específico que codifica los datos de una manera específica. Para que los archivos Javascript sean ejecutables, deben seguir una sintaxis específica. Me preguntaba: ¿es posible crear un archivo de imagen que también tenga una sintaxis JavaScript válida para que pueda ejecutarse?



Antes de continuar leyendo, le recomiendo que explore este entorno de pruebas de código con los resultados de mis experimentos:



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



Si desea ver la imagen y explorarla usted mismo, entonces Puedes descargarlo desde aquí:



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



Elegir el tipo de imagen correcto



Desafortunadamente, las imágenes contienen una gran cantidad de datos binarios que, cuando se interpretan como Javascript, arrojarán un error. Así que mi primer pensamiento fue esto: ¿y si pongo todos los datos de la imagen en un gran comentario, algo como esto?



/*ALL OF THE BINARY IMAGE DATA*/


Este será un archivo Javascript válido. Sin embargo, los archivos de imagen deben comenzar con una secuencia específica de bytes, un encabezado de archivo específico para el formato de imagen. Por ejemplo, los archivos PNG siempre deben comenzar con la secuencia de bytes 89 50 4E 47 0D 0A 1A 0A . Si la imagen comienza con /*, entonces el archivo ya no es un archivo de imagen.



Este encabezado de archivo me llevó a la siguiente idea: ¿qué pasa si usamos esta secuencia de bytes como nombre de variable y le asignamos el valor de una cadena larga?



PNG=`ALL OF THE BINARY IMAGE DATA`;


Usamos cadenas de plantilla en lugar de cadenas normales, "o 'porque los datos binarios pueden contener saltos de línea y las cadenas de plantilla funcionan mejor con ellos.



Desafortunadamente, la mayoría de las secuencias de bytes en los encabezados de los archivos de imagen contienen caracteres no imprimibles que no se pueden usar en nombres de variables. Pero hay un formato que podemos usar: GIF. El bloque de encabezado GIF se parece a 47 49 46 38 39 61 , que se convierte convenientemente a la cadena ASCII GIF89a : ¡nombre de variable absolutamente válido!



Elegir el tamaño de imagen correcto



Ahora que hemos encontrado un formato de imagen que comienza con un nombre de variable válido, debemos agregar el signo igual y el signo inverso. Por lo tanto, los siguientes cuatro bytes del archivo serán: 3D 09 60 04





Primeros bytes de la imagen



En formato GIF, los cuatro bytes después del encabezado definen las dimensiones de la imagen. Necesitamos encajar en ellos 3D (signo igual) y 60 (tilde, abriendo una línea). GIF utiliza un orden de little endian, por lo que el segundo y el cuarto caracteres tienen un gran impacto en el tamaño de las imágenes. Deben ser lo más pequeños posible para que la imagen no tenga decenas de miles de píxeles de ancho y alto. Por lo tanto, necesitamos almacenar grandes bytes en 3D y 60 en los bytes menos significativos.



El segundo byte del ancho de la imagen debe ser un espacio en blanco válido, porque será un espacio entre el signo igual y el comienzo de la línea.GIF89a= `...... También vale la pena recordar que el código de caracteres hexadecimales debe ser lo más pequeño posible, de lo contrario la imagen será enorme.



El carácter de espacio en blanco más pequeño es 09 (carácter de tabulación horizontal). Nos da el ancho de la imagen 3D 09 , que es 2365 en little endian; un poco más ancho de lo que me gustaría, pero perfectamente aceptable.



Para el segundo byte de la altura, puede elegir un valor que proporcione una buena relación de aspecto. Elegí 04 , que nos da una altura de 60 04 , o 1120 píxeles.



Poner el script en el archivo



Hasta ahora, nuestro GIF ejecutable no hace casi nada. Simplemente asigna una GIF89acadena larga a la variable global . ¡Queremos que suceda algo interesante! La mayoría de los datos dentro del GIF se utilizan para codificar la imagen, por lo que si intentamos insertar Javascript allí, es probable que la imagen esté muy distorsionada. Pero por alguna razón, el formato GIF contiene algo llamado Extensión de comentario . Este es un lugar para almacenar metadatos que no son interpretados por el decodificador GIF, el lugar perfecto para nuestra lógica Javascript.



Esta extensión de comentario se encuentra justo después de la tabla de colores GIF. Como podemos poner cualquier contenido allí, podemos cerrar fácilmente la línea GIF89a, agregue todo el Javascript y luego inicie un bloque de comentarios de varias líneas para que el resto de la imagen no afecte al analizador de Javascript.



En última instancia, nuestro archivo puede verse así:



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

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

REST OF THE IMAGE */


Sin embargo, hay una pequeña limitación: aunque el bloque de comentarios en sí puede ser de cualquier tamaño, consta de varios subbloques, y el tamaño máximo de cada uno de ellos es 255. Hay un byte entre los subbloques que determina la longitud del siguiente subbloque. Por lo tanto, para que quepa un script grande allí, debe dividirse en pequeños fragmentos, algo como esto:



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


Los códigos hexadecimales en los comentarios son bytes que determinan el tamaño del siguiente subbloque. No son específicos de Javascript, pero son necesarios para el formato de archivo GIF. Para evitar interferir con el resto del código, deben colocarse en los comentarios. Escribí un pequeño script que procesa fragmentos de script y los agrega al archivo de imagen:



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



Limpiar datos binarios



Ahora que tenemos la estructura básica, debemos asegurarnos de que los datos de la imagen binaria no estropeen la sintaxis del código. Como se mencionó en la sección anterior, el archivo está dividido en tres secciones: la primera es la asignación a la variable GIF89a , la segunda es el código Javascript y la tercera es un comentario de varias líneas.



Echemos un vistazo a la primera parte sobre la asignación de un valor a una variable:



GIF89a= ` BINARY DATA `;


Si los datos binarios contienen un carácter `o una combinación de caracteres ${, entonces tenemos un problema, porque terminarán la cadena de patrón o crearán una expresión no válida. La solución es bastante simple: ¡simplemente cambie los datos binarios! Por ejemplo, en lugar de carácter `(hexadecimal 60 ), puede utilizar carácter a(hexadecimal 61 ). Dado que esta parte del archivo contiene una paleta de colores, esto dará lugar a cambios menores en algunos colores, por ejemplo, para usar un color en #286148lugar de #286048. Es poco probable que alguien note la diferencia.



Luchamos contra la distorsión



Al final del código Javascript, hemos abierto un comentario de varias líneas para que los datos de la imagen binaria no afecten el análisis de Javascript:



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


Si los datos de la imagen contienen una secuencia de caracteres */, el comentario terminará prematuramente, invalidando el archivo Javascript. Aquí nuevamente, podemos cambiar manualmente uno de los dos caracteres para que no terminen el comentario. Sin embargo, dado que ahora estamos en la sección de la imagen codificada, el resultado será una imagen dañada, por ejemplo:





Imagen dañada



En el peor de los casos, es posible que la imagen no se muestre en absoluto. Al elegir cuidadosamente el bit a invertir, pude minimizar la distorsión. Afortunadamente, solo hubo unos pocos casos de combinaciones dañinas */. Todavía hay ligeras distorsiones en la imagen final, por ejemplo, en la parte inferior de la línea "Archivo Javascript válido", pero en general estoy bastante contento con el resultado.



Terminando el archivo



Nos queda la última operación: completar el archivo. El archivo debe terminar con 00 3B bytes , por lo que debemos terminar el comentario antes. Dado que este es el final del archivo y cualquier daño potencial sería sutil, simplemente terminé el comentario del bloque y agregué un comentario de una línea para que el final del archivo no causara problemas de análisis:



/* BINARY DATA*/// 00 3B


Persuadir al navegador para que ejecute la imagen



Ahora, después de todo esto, finalmente tenemos un archivo que es tanto una imagen como un archivo Javascript válido. Sin embargo, debemos superar el último obstáculo: si cargamos una imagen en el servidor e intentamos usarla en una etiqueta script, lo más probable es que obtengamos un error similar:



Se negó a ejecutar el script desde ' http: // localhost: 8080 / image.gif ' porque su tipo MIME ('image / gif') no es ejecutable. [Rechazo de ejecutar el script desde ' http: // localhost: 8080 / image.gif ' porque su tipo MIME no es ejecutable.]


Es decir, el navegador dice correctamente: "¡Esta es una imagen, no la ejecutaré!" Y en la mayoría de los casos, esto es bastante apropiado. Pero aún queremos cumplirlo. La solución es simplemente no decirle al navegador que se trata de una imagen. Para esto, escribí un pequeño servidor que sirve una imagen sin información de encabezado.



Sin la información de tipo MIME del encabezado, el navegador no sabe que es una imagen y hace exactamente lo que es mejor en contexto: renderizarla como una imagen en una etiqueta <img>o ejecutarla como Javascript en una etiqueta <script>.



Pero ... ¿por qué es todo esto?



Todavía no lo he descubierto yo mismo. Tal tarea es un buen calentamiento para la mente, pero si puedes pensar en una situación en la que realmente pueda ser útil, ¡házmelo saber!






Publicidad



Los servidores para desarrolladores son servidores virtuales de nuestra empresa.

Durante mucho tiempo hemos estado utilizando exclusivamente unidades de servidor rápidas de Intel y no ahorramos en hardware , solo en equipos de marca y las soluciones más modernas del mercado para brindar servicios.






All Articles