
CLI de Go que desofusca la salida de javascript-obfuscator (obfuscator.io) y desminifica JavaScript usando transformaciones AST, decodificación estática y un fallback de sandbox goja.
jd es una herramienta de Go que desofusca la salida de javascript-obfuscator (obfuscator.io) y desminifica JavaScript. Es un port a Go de webcrack y synchrony.
Detecta e invierte la salida de javascript-obfuscator:
var/const/let)Más de 20 transformaciones de legibilidad:
| Transformación | Ejemplo |
|---|---|
| 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() → sentencias separadas |
| split-variable-declarations | var a=1, b=2 → var a=1; var b=2 |
| block-statements | envolver cuerpos de una sola sentencia en { } |
| 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 | eliminar void/!/typeof sin efecto a nivel de sentencia |
| remove-double-not | !!true → true |
El parser de goja no soporta la sintaxis de módulos ES (import/export). jd pre-extrae las sentencias import/export, parsea el script restante, ejecuta las transformaciones y luego antepone las sentencias a la salida. Las expresiones import() dinámicas se distinguen correctamente de las declaraciones import.
Para archivos que goja no puede parsear completamente (p. ej. definiciones de lenguaje del editor Monaco con uso intensivo de regex), jd recurre al formateo a nivel de código fuente: un tokenizador divide por punto y coma preservando literales regex, cadenas y comentarios textualmente.
brew install ejfkdev/tap/jd
Descárgalos desde GitHub Releases (Linux/macOS/Windows, x86_64/ARM64).
go build -o jd .
# Deobfuscate a file (output to stdout)
jd obfuscated.js
# Write to a file
jd obfuscated.js -o cleaned.js
# Read from stdin
cat obfuscated.js | jd -
# Process a directory — recursively processes all .js/.mjs/.cjs files,
# mirrors the directory tree to the output directory.
jd src/ -o dist/
# Directory mode with default output: creates <input>-deobfuscated
jd src/
# Specify file extensions
jd src/ -o dist/ --ext .js,.mjs
# Parallel processing (N workers, default: number of CPUs)
jd src/ -o dist/ -j 8
# Skip non-code files (only output processed JS)
jd src/ -o dist/ --copy-noncode=false
# Only deobfuscate, skip unminify
jd --unminify=false obfuscated.js
# JSON output with warnings
jd --json obfuscated.js
| Flag | Por defecto | Descripción |
|---|---|---|
-o, --output | stdout | archivo o directorio de salida (por defecto para dir: <input>-deobfuscated) |
--deobfuscate | true | ejecutar desofuscación de obfuscator.io |
--unminify | true | ejecutar transformaciones de legibilidad |
--sandbox | auto | ejecución del decodificador: auto (estático y luego sandbox), only (siempre sandbox), off (solo estático) |
--timeout | 10s | timeout del sandbox por archivo |
--ext | .js,.mjs,.cjs | extensiones de archivo a procesar en modo directorio |
-j, --workers | 0 (=CPU) | workers paralelos para modo directorio |
--copy-noncode | true | copiar archivos que no son código al directorio de salida |
-v, --verbose | false | imprimir diagnósticos en stderr |
--json | false | emitir 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