- Um desenvolvedor colocou no ChatGPT um JavaScript minificado para entender a implementação de um componente de arte ASCII visto na web, e o modelo desfez o fluxo ofuscado em uma estrutura legível
- O original era um componente React que calculava colunas e linhas conforme o tamanho da janela e atualizava uma grade de strings a 60 fps com
setInterval - O ChatGPT explicou a seleção do conjunto de caracteres, o cálculo de caracteres baseado em coordenadas e tempo e o fluxo de atualização de
div.textContent, depois reescreveu tudo como código TypeScript/React equivalente - No começo, parecia que caracteres de bloco como
░▒▓█estavam ausentes e que a implementação era diferente, mas, ao verificar novamente o script original, tratava-se de um problema de codificação ao copiar e colar - Depois de atualizar com o conjunto de caracteres correto, a saída ficou igual à do componente original, mostrando que o ChatGPT pode ser usado de forma prática para entender e reimplementar código minificado
Encontrando a implementação de arte ASCII em JavaScript minificado
- Para verificar como o bloco de arte ASCII da página do reactive.network hackathon foi implementado, o desenvolvedor analisou o código-fonte
- O código relevante estava minificado, o que dificultava a leitura direta por uma pessoa; normalmente, seria necessário decifrá-lo manualmente ou procurar um arquivo
.mappara restaurar o original - Desta vez, ele colou todo o código minificado no ChatGPT e pediu uma explicação sobre seu funcionamento
Estrutura revelada pelo código minificado
- O código tinha uma estrutura que gerava conteúdo de texto dinâmico usando JavaScript e React
- Os principais elementos eram os seguintes
- Usava aliases curtos para funções matemáticas como
Math.floor,Math.abseMath.min - Mantinha um conjunto de caracteres composto por
"reactive.network REACTIVE.NETWORK"e caracteres especiais/de bloco - Selecionava o conjunto de caracteres a usar conforme o resultado de
Date.now() % 3 - Calculava o índice do caractere a exibir em cada coordenada com base no comprimento do conjunto de caracteres selecionado
- Usava aliases curtos para funções matemáticas como
- A função central,
eT, recebia coordenadas de posição e um valor de tempo, e retornava um caractere a ser exibido- O valor de tempo era convertido com
t.time * 8e-5 - As coordenadas eram ajustadas usando o menor valor entre colunas e linhas e a proporção da tela
- O índice do caractere era definido combinando o resultado do cálculo
Math.cos(position.x * position.x - position.y * position.y)com o valor de tempo
- O valor de tempo era convertido com
Como o componente React renderizava
- O componente usava
useRefpara referenciar odivonde o texto seria inserido euseStatepara armazenar a height e a width da janela - O primeiro
useEffectdefinia o tamanho inicial comwindow.innerHeightewindow.innerWidth, e atualizava o tamanho quando ocorria o eventoresize - O segundo
useEffectcalculava o tamanho da grade ASCII com base no tamanho da telacolseraMath.floor(width / 12) * 1.6rowseraMath.floor(height / 12)aspecteracols / rows
setIntervalera executado em intervalos de1_000 / 60, recriando a string de acordo com 60 fps- Em cada frame, ele percorria todas as linhas e colunas, concatenava os caracteres calculados e colocava a string final em
element.textContent
Reescrita em TypeScript com código mais legível
- Em seguida, o desenvolvedor pediu que fosse implementado um código equivalente em TypeScript, mas de forma mais legível para humanos
- O código criado pelo ChatGPT desdobrou os papéis do código minificado nos seguintes nomes
selectedCharacterSetcharacterSetLengthcalculateCharacter- tipo
Size - componente React
AsciiArtGenerator
- Os nomes de variáveis obscuros foram substituídos por nomes que revelavam a intenção da implementação
calculateCharactercalcula o caractere a exibir recebendo coordenadas, colunas e linhas, proporção da tela e valor de tempotextRefaponta para odivonde o texto real será inseridosizeesetSizerepresentam o estado do tamanho da janela
- O código reescrito ficou muito mais legível do que o código minificado original e foi suficiente para aprender como o componente era implementado
Mal-entendido sobre caracteres ausentes e problema de codificação
- Na comparação inicial, parecia que o LLM havia deixado passar alguns detalhes de implementação
- Em especial, foi concluído que caracteres como
░▒▓█, vistos no componente original, estavam ausentes no resultado do ChatGPT - Depois, um usuário do fórum HN apontou a possibilidade de ser um problema de copiar e colar e, após nova verificação, constatou-se que o código original continha caracteres diferentes daqueles colados no ChatGPT
- Ao baixar o script diretamente, foi possível obter os caracteres corretos, e a causa do problema foi confirmada como uma diferença de codificação
- Depois de corrigir o conjunto de caracteres, a saída ficou idêntica à do componente original
1 comentários
Comentários do Hacker News
Sou o criador do HumanifyJS e criei uma ferramenta baseada em LLM separada para esse uso
Ela usa LLM no nível da AST para garantir que o código continue funcionando depois da desofuscação: https://github.com/jehna/humanify
Quero dizer um recurso que apaga todos os nomes significativos do usuário em código comum e depois escolhe novos nomes com base no algoritmo e nos nomes restantes, como nomes embutidos
Às vezes faço isso manualmente com LLM ao refatorar, e é útil para encontrar termos mais padronizados, como
antiparallel_lineem vez deparallel_line_opposite_direction, ou descobrir nomes mais generalizáveis, comofind_instance_in_listem vez defind_animal_instance_in_animalsSinceramente, ainda estou esperando por controle de versão no nível da AST
Vocês dão a função inteira e perguntam “o que deve ser renomeado?” repetidamente até tudo ser alterado, ou pedem tudo de uma vez, depois fazem parse da saída e verificam se a AST ainda bate?
Quero dizer algo em torno de 50 mil linhas
Edit: agora estou rodando um arquivo JS de apenas 1,2 mil linhas no modo openai, e depois de 20 minutos ele só chegou a 70%. Mesmo que, em tese, funcione em um arquivo de 50 mil linhas, acho melhor nem tentar
A minificação de JS é bastante mecânica e simples, então o trabalho de revertê-la também deveria ser relativamente fácil
Claro que, em geral, é tedioso o bastante para uma pessoa fazer manualmente, mas a transformação em si é bem limitada, então dá para ler se você tiver apenas algumas anotações rastreando os identificadores ofuscados
A desminificação ou desofuscação mais geral ainda parece ser um problema em aberto. No passado escrevi alguns programas deliberadamente ofuscados e, na minha experiência, o ChatGPT não os entendeu nem em nível superficial
Por exemplo, havia um comentário tentando explicar com GPT-4 o código de um gist de um interpretador Brainfuck em C de 160 bytes [1], e a “versão esclarecida” não se parecia em nada com o código original
[1] https://gist.github.com/lifthrasiir/596667#gistcomment-47512...
Exemplos incluem multiplicação/fatoração, derivação/integração, lembrar o passado/prever o futuro
Desofuscar código é claramente um desses problemas inversos difíceis, e pode ficar facilmente mais difícil por causa de bugs, rotinas não usadas ou irrelevantes, e implementações erradas que por acaso produzem o resultado certo
Nesse sentido, se o ChatGPT produz bons resultados em desofuscação, isso é algo positivo, não algo que se deveria esperar a priori. É bom que você tenha verificado os limites do ChatGPT com um problema mais difícil, mas acho que o que você encontrou é apenas um limite superior. Não considero o exemplo do texto original trivial
let userSignedInTimestamp = new Date()emlet x = new Date()é trivial, mas para fazer o caminho inverso é bem provável que seja preciso ler e entender o código ao redor para ver em que contextoxé usadoAlém disso, o restante do código também está minificado, então é ainda mais difícil; e, mesmo que você acerte tudo, no fim é uma transformação com perdas, então o nome da variável pode ter carregado características que não estavam explícitas no código
Isso acontece porque dados desapareceram no processo
A minificação funciona da mesma forma. Muitas informações necessárias para entender o código desaparecem, e recuperar essas informações pode ser muito complicado
timeFactorpor umimuito mais curto pode ser mecânico e simples, mas esse processo é destrutivo, e revertê-lo não é relativamente fácilÉ impossível sem um entendimento bastante sofisticado do que o código realmente faz. Foi exatamente essa a parte que o LLM fez aqui; não é surpreendente, mas é legal. Não curto essa postura de desmerecer de forma tão imediata
Não testei porque não estou no PC
O título não deveria usar ChatGPT ou o nome do modelo, GPT-4, em vez de OpenAI, que é o nome da empresa?
LLMs são excelentes em transformação de texto
Acho que esse é o ponto forte central, mas não está sendo aproveitado o suficiente
Outras capacidades, como raciocinar ou preservar conhecimento, são apenas efeitos colaterais dessa capacidade sobre-humana de reproduzir texto sem “entender” o significado pretendido
Por exemplo, não conseguem ordenar uma lista, mas conseguem traduzir idiomas. Porque uma lista quase corretamente ordenada está errada, enquanto uma tradução quase correta muitas vezes é aceitável para nós
Se uma equipe está avaliando se vale a pena converter grandes partes de código Python para Elixir, não precisa mais ficar apenas no chute
Há pouco tempo, traduzi um script Python para JavaScript e ficou impecável; com algumas proteções, acho que daria para escalar isso
Nós usamos principalmente para extrair dados estruturados a partir de outros formatos
Além disso, também é preciso não ter o conhecimento ou a habilidade para resolver mais rápido com awk, Python, Perl etc.
Alguns desenvolvedores que usavam compressão como meio de ofuscação certamente vão ficar irritados
Isso me lembra que versões antigas do ColdFusion tinham uma ferramenta de “criptografia” de código. O algoritmo era muito fraco, então não demorou para alguém criar uma ferramenta de descriptografia
Mesmo assim, algumas pessoas não gostaram, porque acreditavam que a ferramenta era segura e a usavam para vender código sem revelar o código-fonte. Era o fim dos anos 90/início dos anos 2000, antes de o open source se tornar o padrão esmagador
Haverá competições em que sites dependem em parte de código do lado do cliente ofuscado, e participantes espertos agora poderão jogá-lo no ChatGPT para aumentar suas chances de ganhar
Isso é só o começo
Este é um exemplo de desempenho intelectual superior ao humano
Não dá para negar. Essa tarefa é intelectual e não tem relação com simples memorização. Também não há na web uma quantidade enorme de pares de código minificado/código desminificado com os quais um LLM pudesse treinar
O LLM entende o que está desminificando e, nesse assunto específico, em geral é superior aos humanos
Dá para ter certeza de que não há muitos pares de código minificado e não minificado na web? Eles podem ser facilmente gerados a partir de JS existente para uso em conjuntos de treinamento, e também há uma quantidade enorme de JS não minificado que já foi usado no treinamento
O motivo pelo qual humanos têm dificuldade para entender código minificado são nomes de variáveis opacos, laços estranhos, espaços em branco minimizados etc. Computadores não têm dificuldade com essas coisas; afinal, é por isso que minificamos. A estrutura de atenção deveria se sair bem nesse tipo de tarefa
Também acho que há uma quantidade enorme de código minificado/não minificado. É exatamente para isso que servem os arquivos de source map. Considerando o investimento da OpenAI em navegação na web e desenvolvimento de software, não seria surpreendente se dados minificados/não minificados tivessem sido incluídos no treinamento
Um computador multiplicar instantaneamente números de 1000 dígitos é um exemplo de humanos primeiro terem criado essa ferramenta e, com ela, conseguido fazer a multiplicação. O que mais ali foi intelectualmente bem-sucedido? O computador também não se criou sozinho
Se você traça a fronteira da inteligência humana no crânio e não considera as ferramentas que essa inteligência cria e usa como etapas do processo de resolver problemas, então também precisa aceitar que humanos não são inteligentes o suficiente para ir ao espaço, fazer cirurgias ou preparar a maior parte de suas refeições
Não parece absurdo que ele reconheça a forma abstrata de um código sem nomes de variáveis e mapeie nomes de variáveis legíveis por humanos para padrões parecidos que viu na imensa internet
Interessante, e parece ter melhorado bastante
Há pouco mais de um ano, recebi um e-mail pedindo ajuda com um plugin customizado de um site WordPress hackeado e tentei usar o GPT para desofuscar código malicioso. Na época, eu avançava muito melhor seguindo o código passo a passo por conta própria
Depois de ler este texto, tentei de novo [0]. Ele me deu algo compreensível, mas o código estava ofuscado a ponto de basicamente fazer eval de uma string ilegível por meio do objeto Window, então isso por si só não bastou
Parte do relatório que enviei na época dizia algo assim: consegui investigar um JavaScript fortemente ofuscado, detectar o carregamento da página, chamar uma aparência de cálculo que em teoria era constante e então decifrar a lógica que redirecionava a página para um site malicioso
[0] https://chatgpt.com/share/f51fbd50-8df0-49e9-86ef-fc972bca6b...
Há alguém trabalhando em um LLM de descompilação?
Parece que daria para transformar todo código em algo como open source.
Nesse caso, os dados de treinamento seriam fáceis de criar. Bastaria compilar em massa código gratuito do GitHub com vários compiladores e treinar o modelo para reverter a compilação. É um caso em que dados sintéticos de treinamento são adequados e relativamente fáceis de gerar.
O descompilador seria treinado apenas para reverter a compilação, e tarefas como adicionar comentários poderiam ficar a cargo dos LLMs de código maiores já existentes.
Além de tornar as coisas open source, dá para imaginar a possibilidade de descompilar e modificar facilmente apps proprietários para corrigir bugs ou adicionar recursos. Isso poderia ser uma grande libertação, especialmente para programas que morreram há muito tempo.
Por motivos legais, é bem provável que esse comportamento vire uma funcionalidade bloqueada nos principais modelos.
Esses direitos só podem ser concedidos pelo autor do código.
Se a IA realmente entende código muito bem, ela simplesmente vai criar ou corrigir binários.
Acredito que, quando surgir um programador de IA de verdade, ele não vai se importar com código legível por humanos e vai programar tudo em linguagem de máquina. Se receber a tarefa de manter um sistema existente, vai entender por completo toda a pilha de software e hardware.
É parecido com modelos de difusão de geração de imagens não se preocuparem em aprender técnicas reais de desenho.
É mais próximo de renomear variáveis e funções e inserir quebras de linha.
É muito parecido com o modo como eu costumo usar LLMs [0].
Uso como primeiro passo de decifração quando preciso ler código na marra.
É incrivelmente libertador jogar um código espaguete todo bagunçado e pedir para ele destrinchar em algo mais legível, para então começar dali.
Como o autor também descobriu, LLMs às vezes deixam passar detalhes, mas eu mesmo posso pegar essas partes.
Outro uso é quando entendo o que o código faz, mas não consigo encaixar na cabeça por que ele foi feito daquele jeito. Isso acontece especialmente quando a pessoa que escreveu o código já não está mais na empresa. Nesses casos, coloco o método no chat com o LLM, explico o que ele faz e pergunto por que certas coisas podem ter sido feitas de determinada maneira.
Nem sempre é perfeito, mas com bastante frequência ele produz uma explicação que realmente faz sentido, resiste à revisão e traz uma nova percepção. Uma ou duas vezes, até me impediu de fazer uma refatoração que teria dado dor de cabeça depois.
[0] Uso o chatGPT e, mais recentemente, vários outros modelos, principalmente variantes do Claude, via openwebUI como frontend para comparar as diferenças. Também dá para montar configurações interessantes em que modelos diferentes revisam as respostas uns dos outros.
Se o código desminificado não corresponde ao código minificado, como no trecho final dizendo que “a resposta do LLM parece ter perdido alguns detalhes de implementação”, a utilidade cai bastante.
Especialmente porque, em muitos casos, não dá simplesmente para executar o código, como no post, e conferir a diferença.
Edit: vendo a thread abaixo, parece que era um problema de codificação. Ainda assim, a precisão continua me preocupando um pouco.
Em alguns casos, o ChatGPT fez até uma refatoração própria além da desminificação.
O código original não chama
handleResizeimediatamente; ele tem esse conteúdo inline. Provavelmente o minificador fez o inlining de verdade. A única diferença real aqui é a ausência da condiçãoif (typeof window < "u").Especificamente, estou falando dos caracteres de bloco, mas parece que o HN não permite colar caracteres Unicode. Eles aparecem na versão original, mas não na versão gerada pelo ChatGPT.
Ainda assim, é bem possível que tenha sido porque eu não incluí todo o contexto necessário.
Desqualificar toda a saída porque faltaram alguns caracteres é ser minucioso demais. Fora isso, para mim a saída parece idêntica.
Se tivesse pedido apenas para mudar nomes de variáveis e métodos, acho que teria ficado mais próximo do original.