JSC 文件分析:V8 字节码、bytenode 产物与字符串提取

JSC 文件的三种类型

.jsc 不是一种统一格式,常见有三类:

类型本质能否反编译
bytenode 生成V8 字节码基本不能还原源码
JavaScriptCore(iOS/WebKit)JSCore 缓存无稳定通用工具
微信小程序V8 字节码或混淆加密体几乎不可能还原

核心结论:JSC 是编译产物,不是源码压缩。变量名、结构、注释已丢失,不存在完美反编译。

View8 报错原因

RuntimeError: Failed to detect version for file index.jsc

View8 要先检测 V8 版本,失败通常是这几个原因:

  1. 文件不是 V8 cache——bytenode 生成的 .jsc 结构与 V8 code cache 不同
  2. .jsc 被加了 header、base64 或 zip 包装
  3. V8 版本超出 View8 支持范围(通常只支持 8.x~11.x)

用 Python 快速判断:

with open("index.jsc", "rb") as f:
    print(f.read(32).hex())

V8 code cache 开头有固定的 magic bytes。如果头部是乱码或压缩特征,View8 直接放弃。

Python 提取可读字符串

不管是哪种 JSC,都可以先提取二进制文件中的可读字符串,判断是否有逆向价值:

import re

with open("index.jsc", "rb") as f:
    data = f.read()

strings = re.findall(rb"[ -~]{6,}", data)

seen = set()
for s in strings:
    text = s.decode("utf-8", errors="ignore")
    if text not in seen:
        seen.add(text)
        print(text)

正则 [ -~]{6,} 匹配长度 >= 6 的 ASCII 可读字符串。

结果判断

  • 出现 /api/tokenuserInfo 等 → 文件未加密,有分析价值
  • 全是乱码 → 已加密或压缩,需换思路

bytenode JSC 的运行时分析

bytenode 产物无法静态反编译,但可以在 Node.js 中加载后 hook:

require("bytenode");

// hook 所有函数调用
const originalCall = Function.prototype.call;
Function.prototype.call = function(...args) {
    if (this.name) {
        console.log("[call]", this.name, args.slice(0, 2));
    }
    return originalCall.apply(this, args);
};

require("./index.jsc");

或者用 Node.js inspect 调试:

node --inspect-brk -e "require('bytenode'); require('./index.jsc')"

然后用 Chrome DevTools 连接 chrome://inspect,可以打断点、查看调用栈。

三种方案对比

方案成功率适用场景
Python strings 提取高(未加密)快速判断有无价值
运行时 hookbytenode 产物
Node inspect 调试有 require 入口
静态 V8 字节码分析仅作辅助

对于加密 JSC(AES/RC4 加密,或微信小游戏自定义 VM),以上方法均不适用,需要先逆向解密逻辑。