Skip to content

Latest commit

 

History

88 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

View8

View8 is a static analysis tool designed to decompile serialized V8 bytecode objects (JSC files) into high-level readable code. To parse and disassemble these serialized objects, View8 utilizes a patched compiled V8 binary. As a result, View8 produces a textual output similar to JavaScript.

Requirements

  • Python 3.x
  • Disassembler binary. Available versions:
    • V8 Version 9.4.146.24 (Used in Node V16.x)
    • V8 Version 10.2.154.26 (Used in Node V18.x)
    • V8 Version 11.3.244.8 (Used in Node V20.x)

For compiled versions, visit the releases page.

Usage

Command-Line Arguments

  • --inp, -i: The input file name.
  • --out, -o: Path to the output. Depending on the selected options, the output may be a single file or a directory tree.
  • --input_format, -f: Specify the input format. Options are:
    • raw: the input is a raw JSC file.
    • disassembled: the input file is already disassembled.
    • serialized: the input is already decompiled and stored in a serialized format. The current serialized format is Python pickle; use trusted input only.
  • --export_format, -e: Specify the export format(s). Options are v8_opcode, translated, decompiled, and serialized. Multiple options can be combined. Default: decompiled.
  • --path, -p: Path to the disassembler binary. Required if the input is in the raw format and View8 cannot automatically locate the matching disassembler.
  • --scope: Propagate scope arguments. Default: 1.
  • --normalize: Replace address-derived function identifiers with deterministic names based on parse order.
  • --normalize-map [CSV]: While using --normalize, write a CSV mapping every original function name to its normalized name. When the path is omitted, View8 derives <output>.name_map.csv from --out, or from --inp when no output path is set.
  • --tree, -t: Split output into a tree structure rather than storing all functions in one file. Specify the function that will be used as the tree root. To start from the default main function, use start.
  • --split_mode: Tree splitting mode. Options are declarers, calls, and references. Default: declarers.
  • --inline_depth, -d: In calls and references modes, include functions reachable from the selected tree root up to depth N in the main tree file. Depth 0 means only the selected root; depth 1 includes direct callees/references; depth 2 includes their children. Not used with declarers.
  • --inline_branch_limit, -l: In tree mode, inline complete child branches with at most N functions into the main tree file when child branches are included by --inline_depth. Larger branches are saved separately.
  • --split_depth: In calls and references modes, limit how deep exported usage branches are traversed. Default: 4.
  • --include, -n: File containing functions to include in the output.
  • --exclude, -x: File containing functions to exclude from the output.
  • --func: Display a selected function.
  • --show_all: In function display mode, also show lines marked as hidden.
  • --verbosity, -v: Verbosity level. Accepted range: 0 to 3.

Basic Usage

To decompile a V8 bytecode file and export the decompiled code:

python view8.py -i input_file -o output_file

Disassembler Path

By default, View8 detects the V8 bytecode version of the input file using VersionDetector.exe and automatically searches for a compatible disassembler binary in the Bin folder. This can be changed by specifying a different disassembler binary with the --path or -p option:

python view8.py -i input_file -o output_file --path /path/to/disassembler

Processing Disassembled Files

To skip the disassembling process and provide an already disassembled file as the input, use the --input_format disassembled or -f disassembled option:

python view8.py -i input_file -o output_file -f disassembled

Deterministic Function Names and Mapping CSV

Use --normalize to replace address-derived function names with deterministic identifiers. To preserve the relationship between the original and normalized names, add --normalize-map:

python view8.py \
  --input_format disassembled \
  --inp sample.jsc.disasm.txt \
  --normalize \
  --normalize-map \
  --out decompiled/sample.dec.txt \
  --export_format decompiled serialized

This writes decompiled/sample.dec.name_map.csv with the following columns:

original_name,normalized_name
func_start_0x268514e9dcd9,func_start_0x100000000
func_rne_0x268514eb0779,func_rne_0x100000001

An explicit CSV path can also be provided:

python view8.py \
  --input_format disassembled \
  --inp sample.jsc.disasm.txt \
  --normalize \
  --normalize-map mappings/sample.names.csv \
  --out decompiled/sample.dec.txt

Creating and Processing Serialized Files

Sometimes it is useful to decompile the file into a serialized format that preserves the parsed objects and structures. This type of output may be easier to post-process than a text format, for example during further deobfuscation. To create a serialized output, use the serialized export format:

python view8.py -i input_file -o output_file -e serialized

Security warning: the current serialized format is a Python pickle file (.pkl). Unpickling data from untrusted sources can execute arbitrary code. Only load serialized files that you generated yourself.

To load a serialized output back and export it in another format, use --input_format serialized or -f serialized:

python view8.py -i input_file -o output_file -f serialized

Export Formats

Specify the export format(s) using the --export_format or -e option. You can combine multiple formats:

  • v8_opcode
  • translated
  • decompiled
  • serialized

For example, to export both V8 opcodes and decompiled code side by side:

python view8.py -i input_file -o output_file -e v8_opcode decompiled

By default, the format used is decompiled.

Tree Output

For large bundled payloads, writing all decompiled functions into a single file can be difficult to analyze. Tree output splits the selected root and related functions into a directory structure.

Use --tree to select the tree root:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_out/ \
  --tree start

The special value start means the default main function recovered by View8.

Tree Splitting Modes

The tree can be split using one of three modes:

  • declarers: follows lexical declaration relationships, meaning which functions are declared inside or under another function. This is useful for understanding bundled or module-like structure, but it does not represent execution flow.
  • calls: follows direct function calls, such as func_x(...). This is useful for recovering the execution skeleton from a selected root.
  • references: follows all visible function references, not only direct calls. This includes callbacks, exported handlers, route handlers, object properties, and other assigned functions. This mode is useful for discovering capability surfaces, but it can produce much larger trees than calls.

Example:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_calls/ \
  --tree start \
  --split_mode calls

Main File Inlining

The main tree file always contains the selected root function.

In calls and references modes, --inline_depth controls how many graph levels are included in the main file:

--inline_depth 0  -> root only
--inline_depth 1  -> root + direct callees/references
--inline_depth 2  -> root + direct callees/references + their children

For example, the following command creates a compact execution overview containing the root and its direct callees:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_calls/ \
  --tree start \
  --split_mode calls \
  --inline_depth 1

To include one additional call layer:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_calls/ \
  --tree start \
  --split_mode calls \
  --inline_depth 2

--inline_depth is only supported with calls and references modes. It is not used with declarers.

Branch Splitting

Large child branches are saved into separate files. In calls and references modes, --split_depth controls how deep exported usage branches are traversed:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_calls/ \
  --tree start \
  --split_mode calls \
  --inline_depth 1 \
  --split_depth 5

The --inline_branch_limit option controls whether small complete child branches are also included in the main file:

--inline_branch_limit 3

This means that complete child branches containing at most 3 functions may be inlined into the main file. Larger branches are saved separately.

In calls and references modes, child branches are only inlined when the selected --inline_depth already includes child functions. For example, --inline_depth 0 means root only, so child branches are not inlined even if they are small.

Recommended Tree Workflows

For a compact execution overview:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_calls/ \
  --tree start \
  --split_mode calls \
  --inline_depth 1 \
  --split_depth 5

For a broader architectural overview:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_calls/ \
  --tree start \
  --split_mode calls \
  --inline_depth 2 \
  --split_depth 5

For exported callbacks, handlers, routes, and capability surfaces:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_refs/ \
  --tree start \
  --split_mode references \
  --inline_depth 1 \
  --split_depth 3

For lexical or module-like structure:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --out ./tree_declarers/ \
  --tree start \
  --split_mode declarers \
  --split_depth 3

Function Display Mode

To display a selected function, use --func:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --func func_name

To also show lines marked as hidden:

python view8.py \
  --inp input.pkl \
  --input_format serialized \
  --func func_name \
  --show_all

VersionDetector.exe

The V8 bytecode version is stored as a hash at the beginning of the file. Below are the options available for VersionDetector.exe:

  • -h: Retrieves a version and returns its hash.
  • -d: Retrieves a hash in little-endian form and returns its corresponding version using brute force.
  • -f: Retrieves a file and returns its version.

About

View8 - Decompiles serialized V8 objects back into high-level readable code.

Topics

Resources

Stars

374 stars

Watchers

8 watching

Forks

Releases

Packages

Contributors

Languages