JavaScript VMP (Virtual Machine Protection) 分析与调试
VMP 的基本逻辑和特征
JavaScript VMP (虚拟机器保护) 是一种代码混淆技术,它将原始JavaScript代码转换为在自定义虚拟机中执行的字节码,使得逆向工程变得困难。
主要特征
字节码执行:
原始JS代码被编译为自定义字节码
运行时通过解释器执行这些字节码
虚拟指令集:
自定义的操作码和操作数
可能包含算术、逻辑、控制流等虚拟指令
堆栈或寄存器架构:
模拟传统虚拟机的堆栈或寄存器操作
反调试技术:
检测开发者工具
检测调试器存在
使用无限循环或异常干扰调试
代码动态生成:
运行时动态构造关键代码片段
可能配合
eval
或Function
构造函数使用
调试方法
1. 静态分析
代码格式化:使用工具美化混淆代码
识别虚拟机结构:查找以下模式:
// 典型的VMP结构 var vm = {stack: [],ip: 0,bytecode: [...],dispatch: function() {while(this.ip < this.bytecode.length) {var opcode = this.bytecode[this.ip++];switch(opcode) {case 0x01: /* 操作1 */ break;case 0x02: /* 操作2 */ break;// ...}}} };
2. 动态调试
使用Chrome DevTools:
设置断点并单步执行
监控调用栈和变量变化
使用"Blackbox script"功能忽略库代码
Hook关键函数:
// Hook Function构造函数
var originalFunction = Function;
Function = function() {console.log('Function constructor called with args:', arguments);return originalFunction.apply(this, arguments);
};
3. 反反调试技巧
禁用调试检测:
// 覆盖常见的调试检测 Object.defineProperty(window, 'console', {get: function() {return {log: function(){}, debug: function(){}, /* 其他方法 */};} });
修改时间相关检测:
// 干扰基于时间的检测 Date.now = function() { return 0; }; performance.now = function() { return 0; };
还原VMP代码的步骤
识别字节码加载部分:找到字节码数组和解释器主循环
分析指令集:通过交叉引用确定各操作码的功能
重建控制流:
跟踪跳转指令(如JMP、CALL、RET)
重建函数调用关系
模拟执行:
编写脚本模拟虚拟机执行
记录执行路径和数据流
转换为高级代码:
根据模拟结果将字节码转换回JS代码
实用工具
Babel:用于解析和转换JS代码
AST Explorer:可视化分析代码结构
Terser:代码反混淆工具
自定义解析脚本:针对特定VMP实现编写解析器
示例分析
假设遇到如下VMP代码:
var _0xabc = [0x1, 0x2, 0x3, 'push', 'pop', 0x4];
var vm = {s: [],p: 0,run: function() {while(this.p < _0xabc.length) {var op = _0xabc[this.p++];if(typeof op == 'string') {this[op]();} else {this.s.push(op);}}},push: function() { /* ... */ },pop: function() { /* ... */ }
};
vm.run();
分析步骤:
识别字节码数组
_0xabc
分析解释器循环
run
方法确定指令含义(数字为数据,字符串为操作)
模拟执行并记录堆栈状态