
CLI em Go que desobfusca a saída do javascript-obfuscator (obfuscator.io) e desminifica JavaScript usando transformações de AST, decodificação estática e um fallback de sandbox goja.
jd é uma ferramenta Go que desofusca a saída do javascript-obfuscator (obfuscator.io) e desminifica JavaScript. É um port em Go do webcrack e do synchrony.
Detecta e reverte a saída do javascript-obfuscator:
var/const/let)Mais de 20 transformações de legibilidade:
| Transformação | Exemplo |
|---|---|
| computed-properties | console["log"] → console.log |
| merge-strings | "a" + "b" → "ab" |
| unminify-booleans | !0 → true, !1 → false |
| number-expressions | 1 + 2 → 3 |
| void-to-undefined | void 0 → undefined |
| raw-literals | 0x1 → 1 |
| sequence | a(), b(), c() → instruções separadas |
| split-variable-declarations | var a=1, b=2 → var a=1; var b=2 |
| block-statements | envolve corpos de instrução única em { } |
| logical-to-if | a && b() → if (a) b() |
| ternary-to-if | a ? b() : c() → if (a) b() else c() |
| merge-else-if | else { if (...) } → else if (...) |
| for-to-while | for(;;) → while(true) |
| yoda | 5 === x → x === 5 |
| infinity | 1/0 → Infinity |
| invert-boolean-logic | !(a == b) → a != b |
| unary-expressions | remove void/!/typeof sem efeito no nível de instrução |
| remove-double-not | !!true → true |
O parser do goja não suporta sintaxe de módulo ES (import/export). O jd pré-extrai as instruções import/export, faz o parse do script restante, executa as transformações e então insere as instruções no início da saída. Expressões import() dinâmicas são corretamente distinguidas de declarações import.
Para arquivos que o goja não consegue analisar completamente (por exemplo, definições de linguagem do editor Monaco com uso intenso de regex), o jd recorre à formatação em nível de código-fonte: um tokenizador divide por ponto e vírgula preservando literais regex, strings e comentários literalmente.
brew install ejfkdev/tap/jd
Baixe em GitHub Releases (Linux/macOS/Windows, x86_64/ARM64).
go build -o jd .
# Desofuscar um arquivo (saída para stdout)
jd obfuscated.js
# Escrever em um arquivo
jd obfuscated.js -o cleaned.js
# Ler da entrada padrão
cat obfuscated.js | jd -
# Processar um diretório — processa recursivamente todos os arquivos .js/.mjs/.cjs,
# espelha a árvore de diretórios no diretório de saída.
jd src/ -o dist/
# Modo diretório com saída padrão: cria <input>-deobfuscated
jd src/
# Especificar extensões de arquivo
jd src/ -o dist/ --ext .js,.mjs
# Processamento paralelo (N workers, padrão: número de CPUs)
jd src/ -o dist/ -j 8
# Ignorar arquivos que não são código (apenas gera JS processado)
jd src/ -o dist/ --copy-noncode=false
# Apenas desofuscar, ignorar a desminificação
jd --unminify=false obfuscated.js
# Saída JSON com avisos
jd --json obfuscated.js
| Flag | Padrão | Descrição |
|---|---|---|
-o, --output | stdout | arquivo ou diretório de saída (padrão para diretório: <input>-deobfuscated) |
--deobfuscate | true | executa a desofuscação do obfuscator.io |
--unminify | true | executa as transformações de legibilidade |
--sandbox | auto | execução do decoder: auto (estático e depois sandbox), only (sempre sandbox), off (apenas estático) |
--timeout | 10s | timeout do sandbox por arquivo |
--ext | .js,.mjs,.cjs | extensões de arquivo a processar no modo diretório |
-j, --workers | 0 (=CPU) | workers paralelos para o modo diretório |
--copy-noncode | true | copia arquivos que não são código para o diretório de saída |
-v, --verbose | false | imprime diagnósticos em stderr |
--json | false | emite JSON {code, warnings} |
jd/
├── main.go # CLI entry
├── internal/
│ ├── cli/ # cobra CLI (i18n: English/Chinese)
│ ├── deobfuscator/ # top-level pipeline + ES module preprocessing
│ ├── jsast/ # AST walker (Cursor, Replace, Remove, Clone)
│ ├── codegen/ # AST → JavaScript printer (pretty/compact)
│ ├── scope/ # lexical scope & binding analysis
│ ├── sandbox/ # goja VM wrapper for decoder execution
│ ├── decoder/ # static decoding: Base64/RC4/rotation
│ ├── deobfuscate/ # string-array/rotator/decoder detection + transforms
│ ├── unminify/ # 20 readability transforms + fixpoint runner
│ └── transform/ # Transform abstraction + ApplyFixpoint
└── testdata/samples/ # test fixtures
parse → splitModuleStatements → deobfuscate → unminify → generate