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