3 pontos por GN⁺ 2024-08-30 | 1 comentários | Compartilhar no WhatsApp
  • 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 .map para 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.abs e Math.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
  • 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

Como o componente React renderizava

  • O componente usava useRef para referenciar o div onde o texto seria inserido e useState para armazenar a height e a width da janela
  • O primeiro useEffect definia o tamanho inicial com window.innerHeight e window.innerWidth, e atualizava o tamanho quando ocorria o evento resize
  • O segundo useEffect calculava o tamanho da grade ASCII com base no tamanho da tela
    • cols era Math.floor(width / 12) * 1.6
    • rows era Math.floor(height / 12)
    • aspect era cols / rows
  • setInterval era executado em intervalos de 1_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
    • selectedCharacterSet
    • characterSetLength
    • calculateCharacter
    • tipo Size
    • componente React AsciiArtGenerator
  • Os nomes de variáveis obscuros foram substituídos por nomes que revelavam a intenção da implementação
    • calculateCharacter calcula o caractere a exibir recebendo coordenadas, colunas e linhas, proporção da tela e valor de tempo
    • textRef aponta para o div onde o texto real será inserido
    • size e setSize representam 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

 
GN⁺ 2024-08-30
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

    • Fico me perguntando se seria difícil adicionar um recurso de renomear tudo do zero
      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_line em vez de parallel_line_opposite_direction, ou descobrir nomes mais generalizáveis, como find_instance_in_list em vez de find_animal_instance_in_animals
    • Mais ferramentas deveriam ser criadas com base em AST; ótimo trabalho
      Sinceramente, ainda estou esperando por controle de versão no nível da AST
    • Fico curioso sobre que perguntas vocês fazem ao LLM
      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?
    • Fico curioso se funciona em arquivos gigantes
      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...

    • O fato de uma tarefa ser simples não significa que a tarefa inversa também deva ser simples
      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
    • Transformar let userSignedInTimestamp = new Date() em let 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 contexto x é usado
      Alé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
    • Transformar uma foto colorida em preto e branco é bem simples, mas restaurar as cores originais não é fácil
      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
    • O ato de trocar o nome descritivo de variável timeFactor por um i muito 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
    • Fiz uma primeira tentativa aleatória com o Claude 3.5 Sonnet: https://claude.site/artifacts/246c1b1a-3088-447a-a526-b1e716...
      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?

    • Considerando que a OpenAI transformou GPT, que era um termo técnico genérico, em marca, há uma certa justiça irônica em usar OpenAI como se fosse nome de produto
  • LLMs são excelentes em transformação de texto
    Acho que esse é o ponto forte central, mas não está sendo aproveitado o suficiente

    • Além de ser um ponto forte central, é justamente isso que os transformers foram projetados para fazer e, de forma controversa, talvez seja só isso que consigam fazer
      Outras capacidades, como raciocinar ou preservar conhecimento, são apenas efeitos colaterais dessa capacidade sobre-humana de reproduzir texto sem “entender” o significado pretendido
    • São especialmente fortes em transformações quase lineares, nas quais a ordem não muda muito e não é preciso considerar profundamente as relações entre os itens
      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
    • Com as ferramentas adequadas, deveria ser possível migrar uma base de código para outra linguagem e framework quase com um clique
      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
    • Esse já não é o principal uso nos negócios?
      Nós usamos principalmente para extrair dados estruturados a partir de outros formatos
    • O problema é que precisa ser um caso em que você não tenha que se preocupar com o risco de alucinação, ou em que consiga validar a saída mesmo sem já ter os dados em um formato útil
      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

    • Acho que em breve veremos esse tipo de uso e uma corrida armamentista
      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

    • Isso é apenas transformação de texto
      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
    • Sou otimista em relação à IA, mas não tenho certeza de que isso seja um exemplo do fenômeno mencionado
      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
    • Não tenho especialização em IA ou em compreensão de inteligência, mas não daria também para dizer que uma calculadora portátil realmente entende aritmética e tem desempenho intelectual superior ao dos humanos?
    • Por que o fato de humanos terem criado ferramentas para ajudar na desminificação não deveria contar na pontuação dos humanos?
      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
    • O GPT-4 consumiu mais código do que todos os seus ancestrais veriam ao longo da vida e entende os padrões intrínsecos entre o código e suas versões minificadas
      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.

    • O impacto potencial é enorme.
      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.
    • Ter o código-fonte é só o primeiro passo; também é preciso ter o direito de ler, modificar, executar e redistribuir versões modificadas desse código.
      Esses direitos só podem ser concedidos pelo autor do código.
    • Concordo. Acho que “a IA gera/entende código-fonte” é uma grande isca para desviar a atenção.
      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.
    • Existe um LLM que faz descompilação de x86 para C: https://github.com/albertan017/LLM4Decompile
    • Desminificar não é descompilar.
      É 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.

    • A renomeação de fato deve ser feita com outra ferramenta, como HumanifyJS: https://github.com/jehna/humanify
    • O código desminificado parece bem próximo do original.
      Em alguns casos, o ChatGPT fez até uma refatoração própria além da desminificação.
      O código original não chama handleResize imediatamente; 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ção if (typeof window < "u").
    • Essa parte quer dizer que, na versão gerada pelo ChatGPT, faltaram alguns caracteres usados no exemplo original.
      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.
    • Além disso, ele também pediu para reimplementar de JavaScript para TypeScript.
      Se tivesse pedido apenas para mudar nomes de variáveis e métodos, acho que teria ficado mais próximo do original.