
Ferramenta estática de desofuscação para bytecode V8 JavaScript compilado, com foco em payloads JSCeal. Fornece filtros orientados por padrões, desachatamento de fluxo de controle, reconstrução de strings e renomeação opcional de funções assistida por LLM para análise.
Esta ferramenta é dedicada a desofuscar estaticamente bytecode JavaScript V8 compilado que foi protegido com javascript-obfuscator.
Ela opera sobre pseudocódigo produzido pelo View8, em vez do código-fonte JavaScript original. O projeto foi desenvolvido e testado em payloads JSCeal.
Os filtros são orientados por padrões e destinam-se principalmente a servir como um kit de ferramentas de pesquisa e uma implementação de referência. A ferramenta não é um desofuscador JavaScript de propósito geral, não reconstrói o código-fonte original e não produz JavaScript executável. Sua saída permanece como pseudocódigo View8 destinado à inspeção estática, pesquisa, comparação e exportação de árvores de funções.
pickle do Python. Carregar um arquivo .pkl malicioso ou não confiável pode executar código. Carregue apenas arquivos serializados que você mesmo gerou localmente com o View8.requirements.txt;brotli para o fluxo de trabalho de descompactação em lote no Linux;Crie um ambiente Python isolado:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
O backend OpenAI em deobf_ai.py requer adicionalmente o pacote Python OpenAI:
python3 -m pip install openai
O backend Anthropic usa a API HTTP por meio de requests. O backend Ollama espera um servidor Ollama acessível.
O payload JSCeal original app.jsc é comprimido com Brotli. No Linux, ele pode ser descomprimido com o utilitário brotli:
brotli -d app.jsc -o app.decompressed.jsc
O fluxo de trabalho em lote sob scripts/ executa esta etapa com scripts/unpack_all.sh.
No Windows, ou quando o utilitário de linha de comando brotli não estiver disponível, o auxiliar Node.js incluído pode ser usado como fallback. Ele apenas descomprime a entrada e não a executa:
node Utils/decompress-jsc.js app.jsc
Ele grava:
app.jsc.decompressed.jsc
O cache de código V8 é específico da versão. Use um desmontador compilado para a mesma versão do V8 que o payload.
As amostras JSCeal usadas durante o desenvolvimento eram baseadas no V8 10.2.154.26. Desmontadores padrão de uma compilação V8 não relacionada não funcionarão corretamente.
A árvore de fontes contém o código-fonte do desmontador e os patches V8 necessários sob:
Utils/disasm/v8dasm.cpp
Utils/disasm/patches/
Um binário Linux pré-compilado é distribuído com o lançamento do projeto, enquanto a árvore de fontes contém o código-fonte e os patches necessários para recompilá-lo. Uma descrição detalhada está disponível na Wiki do projeto. Após obter ou compilar o v8dasm correspondente, execute:
/path/to/v8dasm app.decompressed.jsc > app.jsc.disasm.txt
Alimente o arquivo desmontado para view8.py e produza tanto a saída serializada para processamento posterior quanto o pseudocódigo legível por humanos:
mkdir -p decompiled
python3 View8/view8.py \
--input_format disassembled \
--inp app.jsc.disasm.txt \
--normalize \
--out decompiled/app.dec.txt \
--export_format decompiled serialized
Isso produz:
decompiled/app.dec.txt
decompiled/app.dec.pkl
A opção --normalize torna os identificadores de funções gerados reproduzíveis em execuções repetidas de desmontagem e descompilação.
Existem filtros separados para as camadas individuais de ofuscação. Eles podem ser aplicados juntos à saída serializada do View8 com deobf_all.py:
mkdir -p deobfuscated
python3 deobf_all.py \
--inp decompiled/app.dec.pkl \
--out deobfuscated/app.deobf.txt \
--export_format decompiled serialized
O filtro de strings padrão é a variante 2, usada pela maioria dos payloads JSCeal analisados. Para selecionar o esquema de strings mais simples explicitamente, adicione:
--str_deobf 1
As saídas típicas são:
deobfuscated/app.deobf.txt
deobfuscated/app.deobf.pkl
deobfuscated/app.deobf.txt.strings.txt
decompiled/app.dec.resolved_funcs.csv
O CSV de funções resolvidas é um cache específico da amostra. Quando ele está ausente, a passagem de strings recupera a configuração necessária do decodificador, grava o CSV e continua desofuscando strings na mesma execução. Execuções posteriores reutilizam o cache e são normalmente mais rápidas.
Não reutilize um CSV de funções resolvidas com um payload descompilado diferente.
Após todos os filtros estruturais de desofuscação terem sido aplicados, deobf_ai.py pode propor nomes que descrevem o comportamento das funções. Ele suporta backends Anthropic, OpenAI e Ollama.
Passe o modelo explicitamente para que as execuções permaneçam reproduzíveis.
export ANTHROPIC_API_KEY='...'
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend anthropic \
--model '<model-id>' \
--export_format decompiled serialized
export OPENAI_API_KEY='...'
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend openai \
--model '<model-id>' \
--export_format decompiled serialized
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--llm_backend ollama \
--model '<local-model>' \
--ollama_url http://localhost:11434 \
--export_format decompiled serialized
No modo padrão, o renomeador constrói uma árvore de chamadas diretas começando pela função de entrada e renomeia apenas funções alcançadas por meio de chamadas. Adicione --greedy para incluir todas as referências de funções visíveis, incluindo callbacks e manipuladores atribuídos.
O CSV de duas colunas gerado atua como um cache e permite que uma execução interrompida continue. Selecione um cache existente explicitamente com --csv:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--csv deobfuscated/app.deobf.renamed_funcs.greedy.example-model.csv \
--llm_backend anthropic \
--model '<model-id>' \
--greedy \
--export_format decompiled serialized
No modo normal, o CSV é tratado como um cache potencialmente parcial. Os nomes em cache são aplicados primeiro, funções já cobertas pelo cache são removidas da árvore de chamadas ou referências selecionada, e o LLM é invocado apenas para funções que permanecem não resolvidas. Se o CSV cobrir completamente essa árvore, nenhuma chave de API ou conexão LLM é necessária. Se cobrir apenas parte da árvore, o backend selecionado é inicializado e os mapeamentos recém-gerados são anexados ao mesmo CSV.