
Kit de desofuscación estática para bytecode de JavaScript V8 compilado, centrado en cargas útiles de JSCeal. Proporciona filtros basados en patrones, aplanamiento inverso del flujo de control, reconstrucción de cadenas y renombrado opcional de funciones asistido por LLM para el análisis.
Esta herramienta está dedicada a desofuscar estáticamente el bytecode de JavaScript V8 compilado que fue protegido con javascript-obfuscator.
Opera sobre el pseudocódigo producido por View8, en lugar de sobre el código fuente JavaScript original. El proyecto fue desarrollado y probado con payloads de JSCeal.
Los filtros están basados en patrones y están pensados principalmente como un kit de herramientas de investigación y una implementación de referencia. La herramienta no es un desofuscador de JavaScript de propósito general, no reconstruye el código fuente original y no produce JavaScript ejecutable. Su salida sigue siendo pseudocódigo de View8 destinado a inspección estática, búsqueda, comparación y exportación de árboles de funciones.
pickle de Python. Cargar un archivo .pkl malicioso o no confiable puede ejecutar código. Solo carga archivos serializados que hayas generado localmente con View8.requirements.txt;brotli para el flujo de trabajo de desempaquetado por lotes en Linux;Crea un entorno de Python aislado:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
El backend de OpenAI en deobf_ai.py requiere adicionalmente el paquete de Python de OpenAI:
python3 -m pip install openai
El backend de Anthropic usa la API HTTP a través de requests. El backend de Ollama espera un servidor de Ollama accesible.
El payload original app.jsc de JSCeal está comprimido con Brotli. En Linux, se puede descomprimir con la utilidad brotli:
brotli -d app.jsc -o app.decompressed.jsc
El flujo de trabajo por lotes bajo scripts/ realiza este paso con scripts/unpack_all.sh.
En Windows, o cuando la utilidad de línea de comandos brotli no esté disponible, se puede usar el helper de Node.js incluido como respaldo. Solo descomprime la entrada y no la ejecuta:
node Utils/decompress-jsc.js app.jsc
Escribe:
app.jsc.decompressed.jsc
La caché de código V8 es específica de la versión. Usa un desensamblador construido para la misma versión de V8 que el payload.
Las muestras de JSCeal utilizadas durante el desarrollo estaban basadas en V8 10.2.154.26. Los desensambladores predeterminados de una compilación de V8 no relacionada no funcionarán correctamente.
El árbol de fuentes contiene el código fuente del desensamblador y los parches de V8 requeridos bajo:
Utils/disasm/v8dasm.cpp
Utils/disasm/patches/
Un binario de Linux precompilado se distribuye con el lanzamiento del proyecto, mientras que el árbol de fuentes contiene el código fuente y los parches necesarios para recompilarlo. Hay una descripción detallada disponible en el Wiki del proyecto. Después de obtener o construir el v8dasm coincidente, ejecuta:
/path/to/v8dasm app.decompressed.jsc > app.jsc.disasm.txt
Alimenta el archivo desensamblado a view8.py y produce tanto la salida serializada para procesamiento posterior como el pseudocódigo legible 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
Esto produce:
decompiled/app.dec.txt
decompiled/app.dec.pkl
La opción --normalize hace que los identificadores de funciones generados sean reproducibles entre ejecuciones repetidas de desensamblado y descompilación.
Hay filtros separados para las capas individuales de ofuscación. Se pueden aplicar juntos a la salida serializada de View8 con deobf_all.py:
mkdir -p deobfuscated
python3 deobf_all.py \
--inp decompiled/app.dec.pkl \
--out deobfuscated/app.deobf.txt \
--export_format decompiled serialized
El filtro de cadenas predeterminado es la variante 2, utilizada por la mayoría de los payloads de JSCeal analizados. Para seleccionar explícitamente el esquema de cadenas más simple, agrega:
--str_deobf 1
Las salidas típicas son:
deobfuscated/app.deobf.txt
deobfuscated/app.deobf.pkl
deobfuscated/app.deobf.txt.strings.txt
decompiled/app.dec.resolved_funcs.csv
El CSV de funciones resueltas es una caché específica de la muestra. Cuando está ausente, la pasada de cadenas recupera la configuración del decodificador requerida, escribe el CSV y continúa desofuscando cadenas en la misma ejecución. Las ejecuciones posteriores reutilizan la caché y normalmente son más rápidas.
No reutilices un CSV de funciones resueltas con un payload descompilado diferente.
Después de que se hayan aplicado todos los filtros de desofuscación estructural, deobf_ai.py puede proponer nombres que describan el comportamiento de las funciones. Soporta backends de Anthropic, OpenAI y Ollama.
Pasa el modelo explícitamente para que las ejecuciones sean reproducibles.
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
En el modo predeterminado, el renombrador construye un árbol de llamadas directas comenzando desde la función de entrada y renombra solo las funciones alcanzadas a través de llamadas. Agrega --greedy para incluir todas las referencias de funciones visibles, incluyendo callbacks y handlers asignados.
El CSV de dos columnas generado actúa como una caché y permite que una ejecución interrumpida continúe. Selecciona una caché existente explícitamente con --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
En el modo normal, el CSV se trata como una caché potencialmente parcial. Los nombres en caché se aplican primero, las funciones ya cubiertas por la caché se eliminan del árbol de llamadas o referencias seleccionado, y el LLM se invoca solo para las funciones que permanecen sin resolver. Si el CSV cubre completamente ese árbol, no se requiere clave de API ni conexión LLM. Si cubre solo parte del árbol, el backend seleccionado se inicializa y los mapeos recién generados se agregan al mismo CSV.
Usa el mismo modo de árbol que se usó cuando se creó el CSV. Un CSV producido a partir de una ejecución --greedy normalmente necesita --greedy nuevamente si el objetivo es continuar esa ejecución en lugar de solo reutilizar el subconjunto de llamadas directas.
Usa --apply-csv-only cuando el CSV ya contenga las etiquetas que deseas aplicar, incluyendo mapeos revisados, editados, importados o rebasados:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--out deobfuscated/app.renamed.txt \
--csv renamed_functions.normalized.csv \
--apply-csv-only \
--export_format decompiled serialized
Este modo:
--csv explícito;--func.Las filas cuyo identificador de función original no exista en la entrada se ignoran. El comando falla cuando el CSV no contiene mapeos aplicables al archivo cargado.
Usa --func con el identificador completo exacto de la función para solicitar un análisis semántico enfocado de una función desofuscada:
python3 deobf_ai.py \
--inp deobfuscated/app.deobf.pkl \
--func func_example_0x100001234 \
--llm_backend anthropic \
--model '<model-id>'
El análisis incluye un nombre propuesto, resumen de comportamiento, entradas y valor de retorno, efectos secundarios, lógica paso a paso, pseudocódigo limpio, evidencia de respaldo e incertidumbres sin resolver. Proporcionar --csv agrega nombres semánticos en caché como contexto para las referencias dentro de la función seleccionada sin modificar el corpus cargado. Usa --analysis-out analysis/function.md para guardar el informe como Markdown. Las coincidencias difusas se imprimen solo como sugerencias; el identificador de función solicitado debe coincidir exactamente.
Usa --help para opciones que controlan la temperatura, el procesamiento por lotes, el modo de pensamiento de Anthropic, los límites de tokens y las rutas personalizadas de CSV.
Los nombres generados por LLM son ayudas de navegación, no evidencia. Verifícalos siempre contra el cuerpo desofuscado.
La salida desofuscada de JSCeal suele ser muy grande. Carga la salida serializada de nuevo en View8 y divídela en árboles de funciones más pequeños.
En esta etapa, agrega --scope 0. La propagación de alcance ya ha sido realizada por el desofuscador, y repetirla puede propagar valores incorrectamente.
Un árbol basado en relaciones de declarador:
python3 View8/view8.py \
--input_format serialized \
--inp deobfuscated/app.deobf.pkl \
--out trees/declarers \
--export_format decompiled \
--tree start \
--scope 0
Una vista general compacta de llamadas directas:
python3 View8/view8.py \
--input_format serialized \
--inp deobfuscated/app.deobf.pkl \
--out trees/calls \
--export_format decompiled \
--tree start \
--scope 0 \
--split_mode calls \
--inline_depth 1 \
--split_depth 5
Un árbol de referencias más amplio, incluyendo callbacks y handlers asignados:
python3 View8/view8.py \
--input_format serialized \
--inp deobfuscated/app.deobf.pkl \
--out trees/references \
--export_format decompiled \
--tree start \
--scope 0 \
--split_mode references \
--inline_depth 1 \
--split_depth 3
Diferentes archivos JSC pueden usar diferentes modos de ofuscación de cadenas.
El modo más simple observado usa desplazamiento de índices y es manejado por deobf_str1.py. El modo más común de JSCeal usa Base64, RC4, cadenas fragmentadas e índices transformados; es manejado por deobf_str2.py.
El pipeline completo selecciona la variante 2 por defecto. Los filtros también se pueden ejecutar de forma independiente para pruebas.
deobf_str2.pyUsa --help para mostrar todos los modos y opciones disponibles:
python3 deobf_str2.py --help
Se puede iniciar una ejecución directa de desofuscación de cadenas con:
python3 deobf_str2.py \
--inp decompiled/app.dec.pkl \
--out work/app.strings.txt \
--export_format decompiled serialized
Durante la ejecución, el script identifica las funciones decodificadoras de cadenas, carga cualquier configuración en caché válida, resuelve las faltantes, guarda el CSV resultante y decodifica las cadenas. No se requiere una segunda ejecución.
Cuando deobf_str2.py se usa directamente, su nombre de CSV predeterminado es resolved_funcs.csv. Selecciona una ruta específica de la muestra con --csv o -c:
python3 deobf_str2.py \
--inp decompiled/app.dec.pkl \
--out work/app.strings.txt \
--csv decompiled/app.dec.resolved_funcs.csv \
--export_format decompiled serialized \
--verbosity 1
Al encadenar filtros individuales, conserva la salida serializada entre etapas para que las pasadas posteriores puedan continuar operando sobre los objetos de View8.
deobf_all.py aplica las siguientes etapas en orden:
El renombrado de funciones asistido por LLM es opcional y se ejecuta por separado después de la desofuscación estructural.
El repositorio incluye un flujo de trabajo auxiliar completo bajo scripts/. Todos los scripts se mantienen en un solo directorio y obtienen la misma configuración centralizada.
scripts/config.sh rutas compartidas de herramientas y espacio de trabajo
scripts/copy_payloads.sh recopilar y nombrar por MD5 los archivos app.jsc de JSCeal
scripts/unpack_all.sh descompresión Brotli
scripts/disasm_all.sh desensamblado V8 por lotes
scripts/decompile_all.sh descompilación View8 por lotes
scripts/deobfuscate_all.sh desofuscación por lotes con un registro combinado
scripts/run_unattended.sh desofuscación y validación desatendidas
scripts/collect_output.sh recopilar cachés de decodificadores y listados de cadenas
El scripts/config.sh suministrado contiene rutas de un entorno de ejemplo:
JSC_DEOBF_ROOT="$HOME/jsc_deobfuscator"
V8DASM="$HOME/code/v8/v8dasm"
Edita ese archivo una vez para configurar la ruta de instalación, el desensamblador V8 coincidente, los directorios del espacio de trabajo, los comandos externos, las rutas de registro y el diseño de recopilación. El espacio de trabajo por defecto es el directorio desde el que se lanza el script auxiliar.
Cada valor también se puede sobrescribir a través de una variable de entorno. JSC_HELPER_CONFIG puede seleccionar un archivo de configuración diferente.
Una ejecución por lotes típica es:
scripts/copy_payloads.sh
scripts/unpack_all.sh
scripts/disasm_all.sh
scripts/decompile_all.sh
scripts/deobfuscate_all.sh
scripts/collect_output.sh
Los scripts conservan las convenciones utilizadas para el corpus de JSCeal, incluyendo tratar los archivos app.jsc descubiertos como payloads comprimidos con Brotli y nombrarlos por MD5. Revisa scripts/README.md antes de aplicar el flujo de trabajo a muestras no relacionadas.
Para un lote largo, scripts/run_unattended.sh lanza la desofuscación con nohup, escribe archivos de registro con marca de tiempo, PID y estado, y valida cada salida generada para detectar referencias sin resolver a funciones decodificadoras de cadenas en caché:
scripts/run_unattended.sh
Se pueden proporcionar muestras seleccionadas explícitamente:
scripts/run_unattended.sh \
decompiled/sample1.dec.pkl \
decompiled/sample2.dec.pkl
View8/ descompilador View8 y exportador de árboles de funciones
Utils/decompress-jsc.js respaldo de descompresión Brotli para Windows
Utils/disasm/v8dasm.cpp código fuente del desensamblador V8
Utils/disasm/patches/ parches V8 requeridos por el desensamblador
Utils/check_unresolved_decoder_references.py
helper de validación de salida
deobf_all.py pipeline completo de desofuscación predeterminado
deobf_str1.py filtro simple de desplazamiento de índices de cadenas
deobf_str2.py filtro de cadenas RC4/Base64 con recuperación de índices
deobf_scope2.py propagación de alcance y diccionario
deobf_unflattener.py aplanamiento inverso del flujo de control
deobf_replace_ops.py reemplazo de proxies y envoltorios de operaciones
deobf_globals.py propagación global
deobf_inline_temporaries.py limpieza final conservadora
deobf_ai.py renombrado opcional de funciones asistido por LLM
scripts/ helpers configurables de lotes y validación
Cada pasada principal se puede ejecutar por separado para pruebas. Ejecuta el script seleccionado con --help para ver su interfaz completa:
python3 deobf_str1.py --help
python3 deobf_str2.py --help
python3 deobf_scope2.py --help
python3 deobf_unflattener.py --help
python3 deobf_replace_ops.py --help
python3 deobf_globals.py --help
python3 deobf_inline_temporaries.py --help
javascript-obfuscator. Las nuevas variantes pueden requerir detectores o transformaciones adicionales.El pipeline ha sido probado por regresión contra el corpus de JSCeal utilizado en la investigación adjunta. Las comprobaciones básicas de lanzamiento incluyen:
python3 -m compileall -q .
python3 deobf_all.py --help
python3 deobf_str2.py --help
python3 deobf_ai.py --help
python3 View8/view8.py --help
Para cada muestra del corpus, verifica que la ejecución:
.pkl como la .txt;El script auxiliar desatendido automatiza la validación final de referencias a decodificadores.
javascript-obfuscator.El código fuente de JSC Deobfuscator creado para este proyecto está licenciado bajo
la Licencia Pública General de GNU, versión 2 o (a tu elección) cualquier versión posterior
(GPL-2.0-or-later). Consulta LICENSE para el texto completo de la licencia.
Copyright (C) 2026 Aleksandra "Hasherezade" Doniec @ Check Point Research.
El submódulo View8 es un proyecto separado. El material del desensamblador derivado de terceros
bajo Utils/disasm/ conserva su procedencia existente y no se vuelve a licenciar
mediante el aviso de copyright anterior.