- Un desarrollador puso en ChatGPT un JavaScript minificado para entender cómo estaba implementado un componente de arte ASCII que vio en la web, y el modelo deshizo el flujo ofuscado hasta convertirlo en una estructura legible
- El original era un componente de React que calculaba columnas y filas según el tamaño de la ventana, y actualizaba una cuadrícula de texto a 60fps con
setInterval - ChatGPT explicó la selección del conjunto de caracteres, el cálculo de caracteres basado en coordenadas y tiempo, y el flujo de actualización de
div.textContent, para luego reescribirlo como código TypeScript/React equivalente - Al principio pareció que la implementación era distinta porque faltaban caracteres de bloque como
░▒▓█, pero al revisar de nuevo el script original se confirmó que era un problema de codificación al copiar y pegar - Tras actualizarlo con el conjunto de caracteres correcto, la salida pasó a ser igual a la del componente original, mostrando que ChatGPT puede usarse de forma práctica para entender y reimplementar código minificado
Encontrando una implementación de arte ASCII en JavaScript minificado
- Se revisó el código fuente para comprobar cómo estaba implementado el bloque de arte ASCII en la página de reactive.network hackathon
- El código relacionado estaba minificado, así que era difícil de leer directamente para una persona; normalmente esto implicaría descifrarlo manualmente o buscar un archivo
.mappara restaurar el original - Esta vez, se pegó el código minificado completo en ChatGPT y se le pidió que explicara qué hacía
La estructura que reveló el código minificado
- El código tenía una estructura que usaba JavaScript y React para generar contenido de texto dinámico
- Los elementos principales eran los siguientes
- Se usaban alias cortos para funciones matemáticas como
Math.floor,Math.absyMath.min - Había un conjunto de caracteres compuesto por
"reactive.network REACTIVE.NETWORK"y caracteres especiales y de bloque - Se elegía qué conjunto de caracteres usar según el resultado de
Date.now() % 3 - A partir de la longitud del conjunto seleccionado, se calculaba el índice del carácter a mostrar en cada coordenada
- Se usaban alias cortos para funciones matemáticas como
- La función principal,
eT, recibía coordenadas de posición y un valor de tiempo, y devolvía un solo carácter para mostrar- El valor de tiempo se transformaba con
t.time * 8e-5 - Las coordenadas se ajustaban usando el valor menor entre columnas y filas, junto con la proporción de la pantalla
- El índice del carácter se determinaba combinando el resultado de
Math.cos(position.x * position.x - position.y * position.y)con el valor de tiempo
- El valor de tiempo se transformaba con
Cómo renderiza el componente de React
- El componente usaba
useRefpara referenciar eldivdonde se colocaría el texto, yuseStatepara guardar el height y width de la ventana - El primer
useEffectestablecía el tamaño inicial conwindow.innerHeightywindow.innerWidth, y actualizaba esos valores cuando ocurría el eventoresize - El segundo
useEffectcalculaba el tamaño de la cuadrícula ASCII a partir del tamaño de la pantallacolseraMath.floor(width / 12) * 1.6rowseraMath.floor(height / 12)aspecteracols / rows
setIntervalse ejecutaba cada1_000 / 60, regenerando la cadena para ajustarse a 60fps- En cada cuadro se recorrían todas las filas y columnas, se concatenaban los caracteres calculados y la cadena final se asignaba a
element.textContent
Reescritura del código en TypeScript para hacerlo legible
- Luego, el desarrollador pidió implementar un código equivalente en TypeScript, pero de una forma fácil de leer para humanos
- El código generado por ChatGPT descompuso el papel del código minificado con nombres como los siguientes
selectedCharacterSetcharacterSetLengthcalculateCharacter- Tipo
Size - Componente de React
AsciiArtGenerator
- Los nombres de variables crípticos se cambiaron por otros que revelaban la intención de la implementación
calculateCharactercalcula el carácter a mostrar a partir de las coordenadas, las columnas y filas, la proporción de pantalla y el valor de tiempotextRefapunta aldivreal donde entra el textosizeysetSizerepresentan el estado del tamaño de la ventana
- El código reescrito era mucho más fácil de leer que el original minificado, y tenía un nivel suficiente para aprender cómo estaba implementado el componente
El malentendido por caracteres faltantes y el problema de codificación
- En la comparación inicial, parecía que el LLM había pasado por alto algunos detalles de implementación
- En particular, se creyó que en el resultado de ChatGPT faltaban caracteres como
░▒▓█que sí aparecían en el componente original - Después, un usuario del foro de HN sugirió que podría tratarse de un problema de copiar y pegar, y una verificación adicional mostró que el código original contenía caracteres distintos de los que se habían pegado en ChatGPT
- Al descargar directamente el script, se obtuvieron los caracteres correctos y se confirmó que la causa del problema era una diferencia de codificación
- Después de corregir el conjunto de caracteres, la salida quedó idéntica a la del componente original
1 comentarios
Comentarios de Hacker News
Soy la persona que hizo HumanifyJS, y creé una herramienta basada en LLM específicamente para este caso de uso
Usa el LLM a nivel de AST para garantizar que el código siga funcionando incluso después de desofuscarlo: https://github.com/jehna/humanify
Me refiero a una función que borre todos los nombres significativos del usuario en código normal y luego elija nombres nuevos basándose en el algoritmo y los nombres que queden, por ejemplo los nombres integrados
A veces lo hago manualmente con un LLM al refactorizar, y sirve para encontrar términos más estándar como
antiparallel_lineen vez deparallel_line_opposite_direction, o descubrir nombres que se puedan generalizar más, comofind_instance_in_listen vez defind_animal_instance_in_animalsSinceramente, todavía sigo esperando control de versiones a nivel de AST
No sé si le das la función completa y le preguntas “¿qué habría que renombrar?” repitiendo hasta que todo cambie, o si le pides todo de una vez, luego parseas la salida y verificas que el AST siga coincidiendo
Me refiero a unas 50 mil líneas
Edit: ahora mismo apenas estoy probándolo con un archivo JS de 1.2 mil líneas en modo openai, y después de 20 minutos solo va en 70%. Aunque en teoría funcione con archivos de 50 mil líneas, probablemente sea mejor no intentarlo
La minificación de JS es bastante mecánica y simple, así que revertirla también debería ser relativamente fácil
Claro, en general sigue siendo lo bastante tedioso como para que una persona no quiera hacerlo a mano, pero la transformación en sí es bastante limitada, así que con notas para rastrear identificadores ofuscados debería poder leerse
La desminificación o desofuscación más general todavía parece un problema abierto. Hace tiempo escribí algunos programas ofuscados a propósito, y por mi experiencia ChatGPT ni siquiera logró entenderlos a nivel superficial
Por ejemplo, hubo un comentario que intentó explicar con GPT-4 el código de un gist de un intérprete de Brainfuck en C de 160 bytes [1], y esa “versión aclarada” no se parecía en nada al código original
[1] https://gist.github.com/lifthrasiir/596667#gistcomment-47512...
Por ejemplo: multiplicación/factorización prima, derivación/integración, recordar el pasado/predecir el futuro
Desofuscar código claramente es uno de esos problemas inversos difíciles, y puede volverse mucho más difícil por bugs, rutinas no usadas o irrelevantes, e implementaciones incorrectas que por casualidad producen el resultado esperado
En ese sentido, si ChatGPT logra resultados decentes al desofuscar, es algo positivo; no hay motivo para esperar a priori que deba poder hacerlo. Está bien haber comprobado los límites de ChatGPT con un problema más difícil, pero creo que lo encontrado es solo un techo superior. No consideraría trivial el ejemplo del artículo original
let userSignedInTimestamp = new Date()porlet x = new Date()es trivial, pero para ir en sentido contrario probablemente haya que leer y entender el código alrededor para ver en qué contexto se usaxAdemás, el resto del código también está minificado, así que es más difícil, y aun si todo sale bien sigue siendo una transformación con pérdida, así que puede que el nombre de la variable contuviera propiedades que no estaban explícitas en el código
Eso pasa porque en el proceso se pierden datos
La minificación funciona igual. Se pierde mucha información necesaria para entender el código, y recuperar esa información puede ser muy complicado
timeFactorpor uno mucho más corto comoipuede ser algo mecánico y simple, pero el proceso es destructivo y revertirlo no es relativamente fácilDe hecho, es imposible sin entender con bastante sofisticación qué hace realmente el código. Eso es precisamente lo que hizo el LLM aquí, y aunque no me sorprende, sí está muy bien. Esa actitud de descalificarlo tan rápido no me gusta mucho
No pude probarlo porque no estoy en una PC
¿No debería el título usar ChatGPT o el nombre del modelo, GPT-4, en vez del nombre de la empresa, OpenAI?
Los LLM destacan en la transformación de texto
Esa es su fortaleza principal, pero parece que no se está aprovechando lo suficiente
Otras capacidades como razonar o conservar conocimiento son solo efectos secundarios de esta habilidad sobrehumana de reproducir texto sin “entender” el significado pretendido
Por ejemplo, no pueden ordenar una lista, pero sí pueden traducir un idioma. Una lista casi bien ordenada está mal, pero una traducción casi correcta muchas veces sí la aceptamos
Si un equipo está pensando si vale la pena convertir grandes partes de código Python a Elixir, ya no tendría que basarse solo en suposiciones
Hace poco intenté traducir un script de Python a JavaScript y quedó impecable; con unas pocas barreras de seguridad, parece algo escalable
Nosotros lo usamos sobre todo para extraer datos estructurados a partir de otros formatos
Además, también hay que carecer del conocimiento o la habilidad para resolverlo más rápido con awk, Python, Perl, etc.
Algunos desarrolladores que usaban la compresión como método de ofuscación sin duda se van a molestar
Esto me recordó cuando versiones antiguas de ColdFusion traían una herramienta para “encriptar” código. El algoritmo era tan débil que no tardó mucho en aparecer una herramienta para revertirlo
Aun así, a algunas personas les molestó porque confiaban en que esa herramienta era segura y la usaban para vender código sin entregar el fuente. Estamos hablando de finales de los 90 y principios de los 2000, antes de que el código abierto se volviera la opción dominante por amplio margen
Los sitios web ofrecerán competencias que dependan en parte de código del lado del cliente ofuscado, y los participantes más astutos ahora podrán pasárselo a ChatGPT para mejorar sus probabilidades de ganar
Esto apenas está empezando
Este es un ejemplo de desempeño intelectual superior al humano
No se puede negar. Esta tarea es intelectual y no tiene que ver con simple memorización. Tampoco hay tantísimos pares de código comprimido/código restaurado en la web como para que un LLM aprenda de eso
El LLM entiende aquello que está descomprimiendo y, al menos en este tema concreto, en general supera al ser humano
¿De verdad se puede estar seguro de que no hay muchos pares de código comprimido y no comprimido en la web? Se pueden generar fácilmente a partir de JS existente para usarlos como conjunto de entrenamiento, y además ya hay cantidades enormes de JS sin comprimir en el entrenamiento
A los humanos nos cuesta entender código comprimido por nombres de variables opacos, bucles incómodos, espacios en blanco minimizados y cosas así. A las computadoras eso no les cuesta, y de hecho esa es la razón por la que comprimimos. La estructura de attention debería ser buena para este tipo de tarea
También supongo que hay muchísimos pares de código comprimido/no comprimido. Ese es justamente el propósito de los archivos source map. Viendo cuánto ha invertido OpenAI en navegación web y desarrollo de software, no sorprendería que hayan incluido datos comprimidos/no comprimidos en el entrenamiento
Que una computadora multiplique al instante números de 1000 dígitos es un caso en que los humanos primero construyeron esa herramienta y lograron hacer multiplicación con ella. ¿Qué otra cosa habría sido un éxito intelectual ahí? La computadora tampoco se construyó sola
Si cortamos los límites de la inteligencia humana en el cráneo y no contamos las herramientas que esa inteligencia crea y usa como parte del proceso de resolver problemas, entonces también habría que aceptar que los humanos no son lo bastante inteligentes para ir al espacio, operar a alguien o cocinar la mayoría de sus comidas
No parece descabellado que reconozca la forma abstracta del código con nombres de variables omitidos y mapee esos patrones a nombres de variables legibles por humanos basándose en patrones similares vistos en el inmenso internet
Interesante, y parece haber mejorado bastante
Hace poco más de un año recibí un correo pidiéndome ayuda con el plugin personalizado de un sitio de WordPress hackeado, e intenté usar GPT para la desofuscación de malware. En ese momento, avanzar siguiendo el código manualmente paso a paso rendía mucho más
Después de leer esta publicación, volví a intentarlo [0]. Me dio contenido comprensible, pero estaba ofuscado hasta el punto de prácticamente hacer eval de cadenas que no se podían leer a través del objeto Window, así que eso por sí solo no bastó
Parte del informe que envié en ese momento decía algo así: pude desentrañar código JavaScript fuertemente ofuscado, detectar la carga de la página, desenmascarar una llamada que simulaba ser un cálculo teóricamente constante, y descifrar la lógica que redirigía la página a un sitio malicioso
[0] https://chatgpt.com/share/f51fbd50-8df0-49e9-86ef-fc972bca6b...
¿Alguien está trabajando en un LLM descompilador?
Parece que podría volver todo el código como si fuera open source
En este caso, los datos de entrenamiento serían fáciles de crear. Bastaría con compilar en masa código gratuito de GitHub con varios compiladores y entrenarlo para revertir la compilación. Es un caso donde los datos sintéticos de entrenamiento serían apropiados y bastante fáciles de generar
El descompilador solo tendría que entrenarse para revertir la compilación, y para tareas como agregar comentarios se podrían usar LLMs de código más grandes que ya existen
No solo para abrir código, sino también para imaginar una situación donde se puedan descompilar y modificar fácilmente apps propietarias para corregir bugs o agregar funciones. Sobre todo, sería una gran liberación para programas abandonados hace mucho tiempo
Por razones legales, es muy probable que en los modelos principales esto termine siendo una función bloqueada
Solo quien escribió el código puede otorgar esos derechos
Si la IA de verdad entendiera tan bien el código, simplemente crearía o corregiría el binario
Si aparece un programador de IA real, creo que va a codificar todo en lenguaje de máquina sin preocuparse por código legible para humanos. Si le encargan mantener un sistema existente, entenderá de punta a punta todo el stack de software y hardware
Es parecido a cómo los modelos de difusión para generar imágenes no se preocupan por aprender técnicas reales de dibujo
Es más parecido a renombrar variables y funciones y poner saltos de línea
Se parece muchísimo a una forma en que uso LLMs con frecuencia [0]
Cuando tengo que leer código a puro esfuerzo, los uso como primer paso para descifrarlo
Hay algo enormemente liberador en meter código espagueti hecho un desastre, pedir que lo desenvuelvan para que sea más legible, y empezar desde ahí
Como también descubrió el autor, los LLM a veces se comen detalles, pero esas partes las puedo detectar yo mismo
Otro uso es cuando entiendo qué hace el código, pero no termino de captar por qué está hecho de cierta manera. Eso pasa especialmente cuando quien escribió ese código ya no está en la empresa. En ese caso, meto el método en un chat con un LLM, le pido que explique qué hace y luego le pregunto por qué algunas cosas podrían estar hechas de esa forma
No siempre acierta, pero bastante seguido da explicaciones que realmente tienen sentido, resisten revisión y me aportan nuevas ideas. Una o dos veces incluso evitó que refactorizara de una manera que después me habría traído dolores de cabeza
[0] Uso chatGPT y, últimamente, varios otros modelos, principalmente openwebUI como frontend para variantes de Claude, para ver las diferencias. También se pueden armar configuraciones interesantes donde distintos modelos revisan las respuestas de los demás
Si el código desminificado no coincide con el código minificado, entonces su utilidad cae muchísimo, como en la parte final donde dice que “la respuesta del LLM parece haber pasado por alto algunos detalles de implementación”
Sobre todo porque en muchos casos no se puede simplemente ejecutar el código, como en el artículo, para comprobar la diferencia
Edición: viendo el hilo de abajo, parece que fue un problema de codificación. Aun así, la precisión me sigue preocupando un poco
En algunos casos, ChatGPT incluso hizo su propia refactorización además de desminificar
El código original no llama a
handleResizeinmediatamente, sino que tiene su contenido inline. Probablemente el minificador hizo el inlining de verdad. La única diferencia real aquí es que falta la condiciónif (typeof window < "u")En concreto, se refiere a caracteres de bloque, y parece que HN no deja pegar caracteres Unicode. En la versión original sí se ven, pero en la generada por ChatGPT no
Aun así, también es totalmente posible que haya sido porque no incluí todo el contexto que hacía falta
Descalificar toda la salida porque faltan unos pocos caracteres ya roza lo excesivamente quisquilloso. Fuera de eso, para mí la salida es idéntica
Si solo le hubiera pedido cambiar los nombres de variables y métodos, probablemente habría quedado más cerca del original