View8 是一款静态分析工具,旨在将序列化的 V8 字节码对象(JSC 文件)反编译为高级可读代码。为了解析和反汇编这些序列化对象,View8 使用了打过补丁的已编译 V8 二进制文件。因此,View8 生成的文本输出类似于 JavaScript。
9.4.146.24(用于 Node V16.x)10.2.154.26(用于 Node V18.x)11.3.244.8(用于 Node V20.x)如需已编译版本,请访问发布页面。
--inp、-i:输入文件名。--out、-o:输出路径。根据所选选项,输出可以是单个文件或目录树。--input_format、-f:指定输入格式。选项包括:
raw:输入为原始 JSC 文件。disassembled:输入文件已反汇编。serialized:输入已反编译并以序列化格式存储。当前序列化格式为 Python pickle;请仅使用可信输入。--export_format、-e:指定导出格式。选项为 v8_opcode、translated、decompiled 和 serialized。可组合多个选项。默认值:decompiled。--path、-p:反汇编器二进制文件的路径。如果输入为原始格式且 View8 无法自动定位匹配的反汇编器,则此项为必填。--scope:传播作用域参数。默认值:1。--normalize:将基于地址的函数标识符替换为基于解析顺序的确定性名称。--normalize-map [CSV]:使用 --normalize 时,写入一个 CSV 文件,映射每个原始函数名称到其规范化名称。省略路径时,View8 会从 --out 推导出 <output>.name_map.csv;若未设置输出路径,则从 --inp 推导。--tree、-t:将输出拆分为树结构,而非将所有函数存储在一个文件中。指定将用作树根的函数。要从默认主函数开始,请使用 start。--split_mode:树拆分模式。选项为 declarers、calls 和 references。默认值:declarers。--inline_depth、-d:在 calls 和 references 模式下,将所选树根可达的、深度不超过 N 的函数包含在主树文件中。深度 0 表示仅所选根;深度 1 包含直接被调用者/引用者;深度 2 包含它们的子级。不用于 declarers 模式。--inline_branch_limit、-l:在树模式下,当子分支被 --inline_depth 包含时,将包含至多 N 个函数的完整子分支内联到主树文件中。较大的分支将单独保存。--split_depth:在 calls 和 references 模式下,限制导出的使用分支的遍历深度。默认值:4。--include、-n:包含要输出函数的文件。--exclude、-x:包含要从输出中排除的函数的文件。--func:显示所选函数。--show_all:在函数显示模式下,同时显示标记为隐藏的行。--verbosity、-v:详细程度级别。可接受范围:0 到 3。要反编译 V8 字节码文件并导出反编译代码:
python view8.py -i input_file -o output_file默认情况下,View8 使用 VersionDetector.exe 检测输入文件的 V8 字节码版本,并自动在 Bin 文件夹中搜索兼容的反汇编器二进制文件。可以通过 --path 或 -p 选项指定不同的反汇编器二进制文件来更改此行为:
python view8.py -i input_file -o output_file --path /path/to/disassembler要跳过反汇编过程并提供已反汇编的文件作为输入,请使用 --input_format disassembled 或 -f disassembled 选项:
python view8.py -i input_file -o output_file -f disassembled使用 --normalize 将基于地址的函数名称替换为确定性标识符。要保留原始名称与规范化名称之间的关系,请添加 --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这将写入 decompiled/sample.dec.name_map.csv,包含以下列:
original_name,normalized_name
func_start_0x268514e9dcd9,func_start_0x100000000
func_rne_0x268514eb0779,func_rne_0x100000001也可以提供显式的 CSV 路径:
python view8.py \
--input_format disassembled \
--inp sample.jsc.disasm.txt \
--normalize \
--normalize-map mappings/sample.names.csv \
--out decompiled/sample.dec.txt有时将文件反编译为保留解析对象和结构的序列化格式会很有用。这种类型的输出可能比文本格式更容易进行后处理,例如在进一步去混淆时。要创建序列化输出,请使用 serialized 导出格式:
python view8.py -i input_file -o output_file -e serialized安全警告:当前序列化格式为 Python pickle 文件(.pkl)。对来自不可信来源的数据进行反序列化(unpickling)可能执行任意代码。请仅加载您自己生成的序列化文件。
要加载序列化输出并以其他格式导出,请使用 --input_format serialized 或 -f serialized:
python view8.py -i input_file -o output_file -f serialized使用 --export_format 或 -e 选项指定导出格式。您可以组合多种格式:
v8_opcodetranslateddecompiledserialized例如,要并排导出 V8 操作码和反编译代码:
python view8.py -i input_file -o output_file -e v8_opcode decompiled默认使用的格式为 decompiled。
对于大型捆绑负载,将所有反编译函数写入单个文件可能难以分析。树输出将所选根及相关函数拆分为目录结构。
使用 --tree 选择树根:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_out/ \
--tree start特殊值 start 表示 View8 恢复的默认主函数。
树可以使用以下三种模式之一进行拆分:
declarers:遵循词法声明关系,即哪些函数在另一个函数内部或之下声明。这对于理解捆绑或模块化结构很有用,但不代表执行流程。calls:遵循直接函数调用,例如 func_x(...)。这对于从所选根恢复执行骨架很有用。references:遵循所有可见的函数引用,而不仅仅是直接调用。这包括回调、导出处理程序、路由处理程序、对象属性以及其他被赋值的函数。此模式对于发现能力面很有用,但可能生成比 calls 大得多的树。示例:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls主树文件始终包含所选根函数。
在 calls 和 references 模式下,--inline_depth 控制主文件中包含多少图层级:
--inline_depth 0 -> 仅根
--inline_depth 1 -> 根 + 直接被调用者/引用者
--inline_depth 2 -> 根 + 直接被调用者/引用者 + 它们的子级例如,以下命令创建一个包含根及其直接被调用者的紧凑执行概览:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1要包含额外一层调用:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2--inline_depth 仅支持 calls 和 references 模式。不用于 declarers 模式。
较大的子分支会保存到单独的文件中。在 calls 和 references 模式下,--split_depth 控制导出的使用分支的遍历深度:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5--inline_branch_limit 选项控制是否将较小的完整子分支也包含在主文件中:
--inline_branch_limit 3这意味着包含至多 3 个函数的完整子分支可能会被内联到主文件中。较大的分支将单独保存。
在 calls 和 references 模式下,仅当所选 --inline_depth 已包含子函数时,子分支才会被内联。例如,--inline_depth 0 表示仅根,因此即使子分支很小也不会被内联。
用于紧凑的执行概览:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 1 \
--split_depth 5用于更广泛的架构概览:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_calls/ \
--tree start \
--split_mode calls \
--inline_depth 2 \
--split_depth 5用于导出的回调、处理程序、路由和能力面:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_refs/ \
--tree start \
--split_mode references \
--inline_depth 1 \
--split_depth 3用于词法或模块化结构:
python view8.py \
--inp input.pkl \
--input_format serialized \
--out ./tree_declarers/ \
--tree start \
--split_mode declarers \
--split_depth 3要显示所选函数,请使用 --func:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_name要同时显示标记为隐藏的行:
python view8.py \
--inp input.pkl \
--input_format serialized \
--func func_name \
--show_allV8 字节码版本以哈希形式存储在文件开头。以下是 VersionDetector.exe 可用的选项:
-h:获取一个版本并返回其哈希。-d:以小端形式获取一个哈希,并通过暴力破解返回其对应的版本。-f:获取一个文件并返回其版本。