瑞数5作为当前最复杂的反爬方案之一,其核心防御机制在于对浏览器环境的深度检测。我在实际逆向某期刊网站时发现,相比传统加密参数破解,真正的难点在于处理异步执行流程和事件监听链。这两个机制会创建动态检测点,任何执行顺序的偏差都会导致轨迹异常。
举个具体例子:当页面加载时,瑞数5会通过navigator.getBattery()发起异步调用,随后在回调函数中触发7个load事件监听器。这些监听器内部又嵌套了setTimeout延时操作,形成多层异步依赖。实测发现,如果直接用Node.js同步执行这些代码,最终生成的cookie长度会从279位骤减到120位左右——这就是典型的轨迹异常。
首先需要搭建本地沙箱环境。我推荐使用vm2模块而非纯Node环境,因为它能更好地模拟浏览器上下文。以下是关键配置:
javascript复制const { NodeVM } = require('vm2');
const vm = new NodeVM({
sandbox: {
window: {
addEventListener: (type, cb) => setTimeout(cb, 0),
navigator: {
getBattery: () => Promise.resolve({
level: 0.8,
charging: true
})
}
}
}
});
通过浏览器调试工具捕获三个核心JS文件时,要注意:
$_ts基础参数$_ts扩展参数eval执行主逻辑,需要特别关注_$ya变量瑞数5的异步检测主要集中在三个层面:
硬件接口异步:
javascript复制navigator.getBattery().then(_$mg);
这个方法会返回设备电池状态,回调函数_$mg中会生成关键环境变量sy
事件驱动异步:
javascript复制window.addEventListener('load', () => {
setTimeout(_$w, Math.random()*100);
});
注意这里存在7个并行监听的load事件,每个事件的触发顺序会影响后续轨迹
延时任务异步:
javascript复制function _$w() {
document.body.style.width = '100%';
}
这些setTimeout操作会修改DOM样式,其执行间隔需要与浏览器保持一致
在逆向过程中,我发现最棘手的是load事件监听链。通过浏览器调试工具可以捕获完整的事件序列:
| 监听器ID | 依赖参数 | 触发动作 |
|---|---|---|
| #1 | sy | 创建div节点 |
| #2 | level | 修改body样式 |
| #3 | Ap | 写入localStorage |
| ... | ... | ... |
实现精准重放需要:
Proxy对象监控事件注册navigator.getBattery()的返回值会影响后续流程。经过反复测试,发现必须满足以下条件:
level值必须在0.7-0.9之间charging必须为true推荐这样模拟:
javascript复制const batteryMock = {
then(cb) {
setTimeout(() => cb({
level: 0.85,
charging: true,
__proto__: BatteryManager.prototype
}), 80);
}
};
navigator.getBattery = () => batteryMock;
瑞数5会监测以下DOM操作:
<meta>标签documentElement.styleinnerHTML插入特定结构的节点这里有个细节坑点:通过a.href赋值时,浏览器会自动计算host等属性,但直接赋值对象会缺失这些属性。必须这样处理:
javascript复制const a = document.createElement('a');
a.href = 'http://qikan.cqvip.com';
// 手动补全派生属性
a.__defineGetter__('host', () => 'qikan.cqvip.com');
在后期分析中发现,瑞数5会监控四种鼠标事件:
mousemove事件的坐标变化率mouseleave与mouseenter的触发间隔mousedown的时间戳建议使用EventTarget.dispatchEvent模拟:
javascript复制const event = new MouseEvent('mousemove', {
clientX: startX + Math.random()*10,
clientY: startY + Math.random()*10
});
document.dispatchEvent(event);
推荐使用自建Hook系统监控关键操作:
javascript复制const origAddEventListener = window.addEventListener;
window.addEventListener = function(type, listener) {
console.log(`[Hook] Add ${type} listener`);
return origAddEventListener.call(this, type, listener);
};
特别有用的Hook点包括:
Document.prototype.cookieElement.prototype.setAttributewindow.localStorage开发了一个简单的轨迹对比脚本:
javascript复制function trace(key) {
return new Proxy({}, {
get(target, prop) {
console.log(`[Trace] ${key}.${prop}`);
return Reflect.get(...arguments);
}
});
}
window.navigator = trace('navigator');
在沙箱环境中运行完整流程可能需要10-15秒,通过以下优化可降至3秒内:
worker_threads并行处理异步任务遇到轨迹不一致时,建议按以下顺序检查:
localStorage中nd、cDro、YWTU三个参数的值clientWidth/clientHeight是否匹配Performance时间线有个容易忽略的点:window.matchMedia的检测会涉及DPI计算,需要模拟正确的返回值:
javascript复制window.matchMedia = (query) => ({
matches: query.includes('min-width'),
addListener: () => {}
});
对于需要长期维护的项目,建议实现环境自适配系统:
Proxy自动捕获未定义属性核心逻辑类似:
javascript复制const envDB = {};
const handler = {
get(target, prop) {
if (!(prop in target)) {
envDB[prop] = { hits: 0 };
return undefined;
}
return target[prop];
}
};
window = new Proxy(window, handler);
这套系统在实际项目中帮我们减少了70%的维护成本,特别是在瑞数5频繁更新检测策略的情况下。