ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Hook Math.random 逆向分析

Hook Math.random 逆向分析 一、背景与意义在前端逆向工程与反爬对抗中Math.random()是最常被关注的全局 API 之一。大量网站将其用于生成请求指纹、加密盐值、混淆参数、时间戳扰动、反爬检测探针等场景。当逆向分析者需要还原参数生成逻辑、复现签名算法或绕过随机校验时精准 HookMath.random并捕获其调用栈与输出值往往是突破的第一步。与Canvas、WebGL等指纹 API 不同Math.random调用频率高、侵入性强、返回值无特征直接替换容易触发环境检测。因此对它的 Hook 技术也从简单的函数覆盖逐步演进到字节码插桩、引擎层面劫持等多种方案。二、Math.random 的底层实现在深入 Hook 之前先理解其底层生成逻辑有助于判断逆向价值。2.1 标准定义ECMAScript 标准仅规定Math.random()返回一个[0, 1)区间的伪随机数不指定具体算法由各引擎自行实现。这意味着不同浏览器、不同版本的生成序列可能完全不同。2.2 V8 引擎实现以 Chrome / Node.js 使用的 V8 为例历史上经历过三代算法早期线性同余生成器LCG周期短、可预测性强中期Mersenne TwisterMT19937周期 2^19937 - 1当前V8 v7.5XorShift128 算法基于 128 位状态性能更高XorShift128 的核心逻辑可简化为// 伪代码示意 state0 seed0; state1 seed1; function xorshift128plus() { let s1 state0; let s0 state1; const result s0 s1; state0 s0; s1 ^ s1 23; s1 ^ s1 17; s1 ^ s0; s1 ^ s0 26; state1 s1; return (result 0) / Math.pow(2, 64); }理论上只要捕获到连续的若干次输出就可以反推内部状态进而预测后续所有随机数 —— 这也是逆向分析中追求 “可复现” 的理论基础。2.3 逆向价值判断如果目标网站仅用Math.random生成非关键扰动Hook 捕获值即可如果用于核心加密盐或会话标识则需要进一步还原种子与状态实现 “预测式” 复现。三、基础 Hook 方案3.1 直接覆盖法最简单的 Hook 方式是直接重写Math.randomconst originalRandom Math.random; Math.random function() { const value originalRandom.call(Math); console.trace(Math.random 被调用返回值, value); return value; };优点零依赖、写法简单、兼容性好。缺点极易被检测。网站可通过toString()校验、toString.toString()嵌套校验、原生函数标记检测等手段识别。常见检测方式// 检测 1toString 对比 if (Math.random.toString() ! function random() { [native code] }) { console.log(环境异常); } // 检测 2原型链与原生标记 if (Math.random.constructor ! Function.prototype.constructor) { console.log(环境异常); }3.2 Proxy 包装法针对对象属性访问的 Hook可以使用Proxy拦截Math对象的属性读取const mathProxy new Proxy(Math, { get(target, prop) { if (prop random) { return function hookedRandom() { const val target.random(); // 记录调用栈、参数、返回值 return val; }; } return target[prop]; } });局限网站直接持有Math.random的引用后Proxy 会失效且Proxy本身在部分沙箱环境中可被检测。3.3 原型链 HookMath.random挂载在Math对象上并非原型方法因此原型链 Hook 对它不直接生效。但可以结合Object.defineProperty做属性劫持const originalRandom Math.random; Object.defineProperty(Math, random, { get() { console.trace(Math.random 被访问); return originalRandom; }, configurable: true });这种方式可以捕获 “读取函数” 的动作但无法在每次调用时插入逻辑适合做访问检测而非值捕获。四、进阶 Hook 与对抗检测4.1 伪装 toString绕过最基础的toString检测只需重写钩子函数的toStringconst originalRandom Math.random; function hookedRandom() { const val originalRandom(); console.log(val); return val; } hookedRandom.toString function() { return function random() { [native code] }; }; Math.random hookedRandom;但这只能绕过单层检测。面对Function.prototype.toString.call(Math.random)这种强制调用原型方法的检测自定义toString会失效。4.2 劫持 Function.prototype.toString更深一层的对抗是连toString一起 Hookconst originalToString Function.prototype.toString; const originalRandom Math.random; function hookedRandom() { return originalRandom(); } Function.prototype.toString function() { if (this hookedRandom) { return function random() { [native code] }; } return originalToString.call(this); }; Math.random hookedRandom;风险修改Function.prototype.toString影响面极大更容易触发更底层的环境检测。在高强度反爬站点不建议使用。4.3 基于 AST 插桩的 Hook当运行时 Hook 容易被检测时另一种思路是在代码执行前修改源码 —— 即在 AST 层面将Math.random()替换为自定义函数调用。典型流程通过Service Worker或代理工具拦截目标 JS 文件使用 Babel / Acorn 解析为 AST遍历CallExpression识别callee.object.name Math callee.property.name random替换为自定义钩子函数调用保留原始返回值重新生成代码并交付执行这种方式的优势在于运行时Math.random本身仍是原生函数检测方无法通过函数引用、toString 等手段发现异常。缺点是实现成本高且需要处理代码混淆、eval 动态执行等场景。五、逆向分析实战从 Hook 到还原5.1 捕获调用时机与调用栈逆向的第一步不是改值而是摸清调用规律。一个完整的 Hook 至少应记录调用时间戳调用栈Error().stack或console.trace返回值调用次数计数示例const logs []; const originalRandom Math.random; Math.random function() { const val originalRandom(); const stack new Error().stack; logs.push({ time: performance.now(), value: val, stack: stack }); return val; };通过分析调用栈可以定位到是哪个混淆函数在使用随机数进而缩小逆向范围。5.2 固定返回值用于差分分析在逆向加密算法时常需要 “控制变量”。将Math.random固定返回某个值观察输出变化可以快速判断它是否参与了最终签名Math.random function() { return 0.123456789; // 固定值 };如果签名结果随固定值稳定复现说明随机数是算法输入之一反之则可能仅用于混淆或探针。5.3 状态还原与预测对于 XorShift128 算法理论上收集连续 2~4 个 64 位输出即可反推内部状态。但实际浏览器中Math.random返回的是浮点数低位精度会丢失通常需要更多样本 暴力搜索来恢复状态。这一步的工程复杂度较高一般用于需要 “离线复现整套加密” 的场景。多数逆向任务中直接 Hook 取值并转发到本地计算已经足够。六、反 Hook 检测与绕过思路6.1 常见检测手段原生性检测toString、[native code]字符串匹配、name属性校验引用缓存检测页面加载初期缓存Math.random引用后续对比是否被替换频率与分布检测统计大量调用结果的分布判断是否符合均匀分布时序检测Hook 函数通常比原生函数慢可通过执行耗时判断沙箱探针在 iframe 中创建纯净环境与当前上下文对比6.2 绕过原则最小侵入能不 Hook 就不 Hook优先通过 AST 插桩或断点调试获取值引擎层 Hook使用 CDPChrome DevTools Protocol在调试器层面下断点捕获值运行时完全无感知及时恢复Hook 取值后立即恢复原生函数避免长时间暴露iframe 偷取从干净 iframe 中引入原生Math.random替换回主线程七、总结Math.random的 Hook 是前端逆向的入门级技术但对抗深度可以延伸到引擎层与沙箱安全。从最简单的函数覆盖到 AST 插桩、CDP 调试劫持、状态还原预测不同场景对应不同方案。对于大多数逆向分析场景“Hook 取值 调用栈定位”已经能解决 80% 的问题真正需要还原随机数种子、实现预测的场景并不多见。学习的重点应放在 “如何低检测地 Hook” 与 “如何通过 Hook 结果辅助定位核心算法” 上而非单纯追求替换本身。
返回列表