-
-
[原创]开源栈式 JS 虚拟机(JSVMP)功能介绍、痛点、后续计划
-
发表于: 2天前 554
-
Twisted:开源栈式 JS 虚拟机(JSVMP)功能介绍、痛点、后续计划
仓库:twisted
测试地址:click
定位
Twisted 是用 TypeScript 实现的栈帧式 JSVMP 工具链:将子集 JavaScript 编译为自定义 IR,再汇编为字节码,由浏览器或 Node 侧 VM 解释执行。目标场景包括提高前端逻辑被脚本化还原的成本(如风控、反作弊相关研究)
模块总览
| 模块 | 职责 |
|------|------|
| Compiler | Babel 解析 AST → 线性 `Instruction[]`(IR) |
| Assembler | IR → bundle:`{ bytecode: number[], meta: string[] }` |
| Runtime / VM | 解释执行 bytecode(依赖注入、`async`/`await`、闭包、调用约定等) |
| Obfuscator | IR 级混淆 Pass,可串联执行 |
| Builder | esbuild 打包浏览器 runtime;可选 `javascript-obfuscator` |
| CLI | `build` / `runtime` / `all`,串联整条编译流水线 |
Compiler
由 Babel 得到 AST 后,再递归遍历节点,将 JavaScript 译为 IR。
为何先 IR,再 bytecode?
多一层 IR 是为了工程上的可维护性与可扩展性:优化、混淆、回填标签等可在结构化指令序列上完成,再统一交给 Assembler 落盘为字节码,比「AST 直出 bytecode」更易迭代。
Assembler
把 IR 编成 VM 所需的 bundle:完成 `Jmp` / `JmpIf` 等跳转回填、字符串等常量进 `meta` 池等。
产物形态示例:
{ bytecode: number[]; meta: string[] }
Runtime / VM
栈帧式虚拟机,执行 Assembler 输出的 bytecode,与 Compiler 的指令语义一一对应。
Obfuscator
在 IR 层做变换(如算术多态),使多次编译得到的 IR 不必相同,从而抬高静态/模式化分析成本。具体 Pass 以 `src/obfuscator/` 为准。
Builder
将 VM 与内嵌 bundle 打成可在浏览器加载的脚本;流程上包含 ES 降级(如降至 ES5)与可选的 javascript-obfuscator 混淆,详见 `src/builder/`。
开发痛点
- 可参考的成熟开源项目很少:浏览器侧「JS → 自定义字节码 + 自研 VM」整条链路,公开、可维护、能跟着跑的仓库不多,很多细节只能自己摸。
- 体系化文章与教程稀缺:编译器前端、IR 设计、跳转回填、VM 调用约定、与混淆流水线结合等,分散在零星博客或逆向讨论里,缺少从零到可跑产物的成篇文档,入门与对比成本都高。
后续计划
- 先落地控制流图(CFG):在现有线性 IR 之上建立基本块、前后继等结构,便于分析与后续优化;再在此之上逐步丰富 IR Pass。
- 反 Hook / 环境探测:在编译期或运行时侧增加对关键 API(如 `console`、`fetch`、`eval` 等)是否被篡改、代理或调试器附加的检测与对抗策略(与示例 `example/fingerprint.js` 一类逻辑对齐,逐步产品化)。
- 按优先级补齐 Compiler / VM(逻辑运算、`throw`、`try catch`、等语法)。
- 欢迎通过 Issue / PR 讨论架构与对抗思路;功能与修复建议尽量附带或可更新 `npm test`。
我并不是反作弊和风控业内人士,很多想法和功能都是闭门造车,希望大家指正