
Descompila bytecode V8 serializado (archivos JSC) en código legible de alto nivel similar a JavaScript, con soporte para múltiples versiones de V8, salida en árbol y nombres de funciones deterministas para el análisis de malware.
View8 es una herramienta de análisis estático diseñada para descompilar objetos de bytecode V8 serializados (archivos JSC) en código legible de alto nivel. Para analizar y desensamblar estos objetos serializados, View8 utiliza un binario V8 compilado con parches. Como resultado, View8 produce una salida textual similar a JavaScript.
9.4.146.24 (usada en Node V16.x)10.2.154.26 (usada en Node V18.x)11.3.244.8 (usada en Node V20.x)Para versiones compiladas, visita la página de lanzamientos.
--inp, : El nombre del archivo de entrada.-i--out, -o: Ruta de salida. Dependiendo de las opciones seleccionadas, la salida puede ser un único archivo o un árbol de directorios.--input_format, -f: Especifica el formato de entrada. Las opciones son:
raw: la entrada es un archivo JSC sin procesar.disassembled: el archivo de entrada ya está desensamblado.serialized: la entrada ya está descompilada y almacenada en un formato serializado. El formato serializado actual es pickle de Python; usa solo entradas de confianza.--export_format, -e: Especifica los formatos de exportación. Las opciones son v8_opcode, translated, decompiled y serialized. Se pueden combinar varias opciones. Valor predeterminado: decompiled.--path, -p: Ruta al binario desensamblador. Es necesario si la entrada está en formato raw y View8 no puede localizar automáticamente el desensamblador correspondiente.--scope: Propaga los argumentos de ámbito. Valor predeterminado: 1.--normalize: Reemplaza los identificadores de funciones derivados de direcciones con nombres deterministas basados en el orden de análisis.--normalize-map [CSV]: Al usar --normalize, escribe un CSV que mapea cada nombre de función original a su nombre normalizado. Cuando se omite la ruta, View8 deriva <output>.name_map.csv de --out, o de --inp cuando no se establece una ruta de salida.--tree, -t: Divide la salida en una estructura de árbol en lugar de almacenar todas las funciones en un solo archivo. Especifica la función que se usará como raíz del árbol. Para comenzar desde la función principal predeterminada, usa start.--split_mode: Modo de división del árbol. Las opciones son declarers, calls y references. Valor predeterminado: declarers.--inline_depth, -d: En los modos calls y references, incluye las funciones alcanzables desde la raíz del árbol seleccionada hasta la profundidad N en el archivo principal del árbol. La profundidad 0 significa solo la raíz seleccionada; la profundidad 1 incluye llamadas/referencias directas; la profundidad 2 incluye sus hijos. No se usa con declarers.--inline_branch_limit, -l: En modo árbol, incluye ramas hijas completas con un máximo de N funciones en el archivo principal del árbol cuando las ramas hijas están incluidas por --inline_depth. Las ramas más grandes se guardan por separado.--split_depth: En los modos calls y references, limita la profundidad de recorrido de las ramas de uso exportadas. Valor predeterminado: 4.--include, -n: Archivo que contiene las funciones a incluir en la salida.--exclude, -x: Archivo que contiene las funciones a excluir de la salida.--func: Muestra una función seleccionada.--show_all: En el modo de visualización de funciones, también muestra las líneas marcadas como ocultas.--verbosity, -v: Nivel de verbosidad. Rango aceptado: 0 a 3.Para descompilar un archivo de bytecode V8 y exportar el código descompilado:
python view8.py -i input_file -o output_fileDe forma predeterminada, View8 detecta la versión del bytecode V8 del archivo de entrada usando VersionDetector.exe y busca automáticamente un binario desensamblador compatible en la carpeta Bin. Esto se puede cambiar especificando un binario desensamblador diferente con la opción --path o -p:
python view8.py -i input_file -o output_file --path /path/to/disassemblerPara omitir el proceso de desensamblado y proporcionar un archivo ya desensamblado como entrada, usa la opción --input_format disassembled o -f disassembled:
python view8.py -i input_file -o output_file -f disassembledUsa --normalize para reemplazar los nombres de funciones derivados de direcciones con identificadores deterministas. Para preservar la relación entre los nombres originales y normalizados, agrega --normalize-map:
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map \
--out decompiled/sample.dec.txt \
--export_format decompiled serializedEsto escribe decompiled/sample.dec.name_map.csv con las siguientes columnas:
original_name,normalized_name
func_start_0x268514e9dcd9,func_start_0x100000000
func_rne_0x268514eb0779,func_rne_0x100000001También se puede proporcionar una ruta CSV explícita:
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map mappings/sample.names.csv \
--out decompiled/sample.dec.txtA veces es útil descompilar el archivo en un formato serializado que preserve los objetos y estructuras analizados. Este tipo de salida puede ser más fácil de procesar posteriormente que un formato de texto, por ejemplo durante una desofuscación adicional. Para crear una salida serializada, usa el formato de exportación serialized:
python view8.py -i input_file -o output_file -e serializedAdvertencia de seguridad: el formato serializado actual es un archivo pickle de Python (.pkl). Deserializar datos de fuentes no confiables puede ejecutar código arbitrario. Solo carga archivos serializados que hayas generado tú mismo.
Para cargar una salida serializada y exportarla en otro formato, usa --input_format serialized o -f serialized:
python view8.py -i input_file -o output_file -f serializedEspecifica los formatos de exportación usando la opción --export_format o -e. Puedes combinar varios formatos:
v8_opcodetranslateddecompiledserializedPor ejemplo, para exportar tanto los opcodes V8 como el código descompilado lado a lado:
python view8.py -i input_file -o output_file -e v8_opcode decompiledDe forma predeterminada, el formato utilizado es decompiled.
Para cargas útiles empaquetadas grandes, escribir todas las funciones descompiladas en un solo archivo puede ser difícil de analizar. La salida en árbol divide la raíz seleccionada y las funciones relacionadas en una estructura de directorios.
Usa --tree para seleccionar la raíz del árbol:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_out/ \
--tree startEl valor especial start significa la función principal predeterminada recuperada por View8.
El árbol se puede dividir usando uno de tres modos:
declarers: sigue las relaciones de declaración léxica, es decir, qué funciones se declaran dentro o bajo otra función. Esto es útil para comprender estructuras empaquetadas o similares a módulos, pero no representa el flujo de ejecución.calls: sigue las llamadas directas a funciones, como func_x(...). Esto es útil para recuperar el esqueleto de ejecución desde una raíz seleccionada.references: sigue todas las referencias visibles a funciones, no solo las llamadas directas. Esto incluye callbacks, manejadores exportados, manejadores de rutas, propiedades de objetos y otras funciones asignadas. Este modo es útil para descubrir superficies de capacidades, pero puede producir árboles mucho más grandes que calls.Ejemplo:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode callsEl archivo principal del árbol siempre contiene la función raíz seleccionada.
En los modos calls y references, --inline_depth controla cuántos niveles del grafo se incluyen en el archivo principal:
--inline_depth 0 -> solo la raíz
--inline_depth 1 -> raíz + llamadas/referencias directas
--inline_depth 2 -> raíz + llamadas/referencias directas + sus hijosPor ejemplo, el siguiente comando crea una vista general de ejecución compacta que contiene la raíz y sus llamadas directas:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1Para incluir una capa adicional de llamadas:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2--inline_depth solo se admite con los modos calls y references. No se usa con declarers.
Las ramas hijas grandes se guardan en archivos separados. En los modos calls y references, --split_depth controla la profundidad de recorrido de las ramas de uso exportadas:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5La opción --inline_branch_limit controla si las ramas hijas completas pequeñas también se incluyen en el archivo principal:
--inline_branch_limit 3Esto significa que las ramas hijas completas que contengan como máximo 3 funciones pueden incluirse en el archivo principal. Las ramas más grandes se guardan por separado.
En los modos calls y references, las ramas hijas solo se incluyen cuando el --inline_depth seleccionado ya incluye funciones hijas. Por ejemplo, --inline_depth 0 significa solo la raíz, por lo que las ramas hijas no se incluyen incluso si son pequeñas.
Para una vista general de ejecución compacta:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5Para una vista general arquitectónica más amplia:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2 \
--split_depth 5Para callbacks exportados, manejadores, rutas y superficies de capacidades:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_refs/ \
--tree start \
--split_mode references \
--inline_depth 1 \
--split_depth 3Para estructura léxica o similar a módulos:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_declarers/ \
--tree start \
--split_mode declarers \
--split_depth 3Para mostrar una función seleccionada, usa --func:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_namePara mostrar también las líneas marcadas como ocultas:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_name \
--show_allLa versión del bytecode V8 se almacena como un hash al principio del archivo. A continuación se muestran las opciones disponibles para VersionDetector.exe:
-h: Obtiene una versión y devuelve su hash.-d: Obtiene un hash en formato little-endian y devuelve su versión correspondiente mediante fuerza bruta.-f: Obtiene un archivo y devuelve su versión.