逆向实战:用Chrome DevTools动态调试某讯滑块验证码的JS与VMP核心
逆向实战:用Chrome DevTools动态调试滑块验证码的JS与VMP核心
滑块验证码作为现代Web应用常见的安全防护手段,其背后的JavaScript逻辑往往采用虚拟机保护(VMP)技术来增加逆向难度。本文将带您深入实战,使用Chrome开发者工具(DevTools)逐步剖析滑块验证码的关键参数生成过程,并探索VMP代码的执行流程。
1. 环境准备与基础调试
在开始逆向分析前,我们需要配置合适的调试环境。以下是基本准备工作:
- Chrome浏览器:确保使用最新版本(建议Chrome 100+)
- 开发者工具:快捷键
F12或Ctrl+Shift+I打开 - 调试配置:
- 在
Sources面板启用Local Overrides功能 - 设置
XHR/fetch Breakpoints捕获特定API请求 - 开启
Preserve log保持网络请求记录
- 在
// 示例:在Console快速检查页面是否加载了VMP相关代码 Object.keys(window).filter(k => k.includes('VM') || k.includes('TENCENT'))提示:分析前建议开启无痕窗口,避免浏览器扩展干扰调试过程
滑块验证码通常会在滑动完成后向服务端发送验证请求,其中包含多个加密参数。通过拦截cap_union_new_verify接口,我们可以观察到以下关键字段:
| 参数名 | 疑似来源 | 特征描述 |
|---|---|---|
| ua | User-Agent的Base64编码 | 可直接解码验证 |
| sess | 前置接口返回 | 通常来自prehandle请求 |
| collect | JS函数生成 | 常关联VMP保护的getData |
| eks | JS函数生成 | 常关联VMP保护的getEks |
| vData | XMLHttpRequest改写 | 动态注入的加密数据 |
2. 关键参数定位技巧
2.1 基础参数追踪
对于ua和sess这类相对简单的参数,可采用直接搜索法:
- 在
Sources面板按Ctrl+Shift+F全局搜索collect等关键词 - 使用
Network面板查看请求调用栈 - 对疑似函数右键选择
Reveal in source panel
// 示例:快速验证ua参数是否为User-Agent的编码 atob('...ua参数值...') === navigator.userAgent2.2 VMP保护参数分析
对于受VMP保护的collect和eks参数,需要更精细的调试方法:
函数调用追踪:
- 在疑似生成函数处设置条件断点
- 使用
Call Stack面板观察执行链路
原型链检查:
// 检查XMLHttpRequest是否被重写 console.log(XMLHttpRequest.prototype.send.toString())内存快照对比:
- 在参数生成前后使用
Memory面板保存堆快照 - 对比差异定位关键对象
- 在参数生成前后使用
注意:VMP代码通常表现为巨大的Base64字符串或字节数组,特征明显但可读性差
3. VMP核心逆向实战
3.1 虚拟机结构解析
典型的JS VMP实现包含以下核心组件:
- 字节码加载器:解码Base64字符串为可执行指令
- 虚拟CPU:包含PC寄存器、堆栈等模拟环境
- 指令集解释器:执行虚拟操作码
// 常见VMP初始化代码结构 __TENCENT_CHAOS_VM = function(pc, bytecode, caller, stack) { // 虚拟机核心执行逻辑 while(!halt) { opcode = bytecode[pc++]; switch(opcode) { case 0x01: // 虚拟指令1 case 0x02: // 虚拟指令2 // ... } } }3.2 动态调试策略
针对VMP代码的特殊性,推荐采用以下调试技巧:
执行流控制:
- 在虚拟机入口设置断点
- 使用
Step into和Step over交替跟踪
关键点监控:
- 监控
ArrayBuffer和TypedArray操作 - 拦截
Function.prototype.apply/call调用
- 监控
环境隔离:
// 创建纯净环境测试可疑函数 const iframe = document.createElement('iframe'); document.body.appendChild(iframe); const sandbox = iframe.contentWindow;行为日志记录:
// 重写关键方法记录调用信息 const originalApply = Function.prototype.apply; Function.prototype.apply = function() { console.log('Calling:', this.name); return originalApply.apply(this, arguments); }
4. 高级逆向技巧与防护对策
4.1 反调试对抗方案
现代VMP实现常包含反调试措施,常见应对方法:
| 反调试技术 | 破解方案 | 实现示例 |
|---|---|---|
| 时间差检测 | 重写Date/performance方法 | window.performance.now = () => 0 |
| 断点检测 | 使用条件断点替代常规断点 | 在DevTools设置Conditional breakpoint |
| 无限循环陷阱 | 脚本阻塞检测 | 使用setTimeout分块执行 |
| 环境差异检测 | 伪装运行环境 | 修改navigator.userAgent |
4.2 自动化提取方案
对于需要批量分析的场景,可考虑半自动化方案:
请求参数提取器:
const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function(body) { if(this._url.includes('verify')) { console.log('Captured params:', new URLSearchParams(body)); } return originalSend.call(this, body); }函数行为记录器:
function wrapFunction(target, name) { const original = target[name]; target[name] = function() { console.log(`[${name}] called with:`, arguments); return original.apply(this, arguments); } }字节码转储工具:
// 捕获VMP初始化时的字节码数组 const bytecodeArrays = []; const originalConcat = Array.prototype.concat; Array.prototype.concat = function() { if(this.length > 1000) { // 假设大数组是字节码 bytecodeArrays.push([...this]); } return originalConcat.apply(this, arguments); }
在实际逆向过程中,发现某些VMP实现会动态修改指令集,这需要建立指令变化追踪机制。一个实用的技巧是在虚拟机主循环插入日志点,记录PC指针与操作码的对应关系。
