逆向实战:当Puppeteer遇上JS混淆+内存爆破,我是如何绕过网站反爬的

张开发
2026/4/13 20:18:26 15 分钟阅读

分享文章

逆向实战:当Puppeteer遇上JS混淆+内存爆破,我是如何绕过网站反爬的
逆向实战当Puppeteer遇上JS混淆内存爆破的自动化对抗方案在数据采集和自动化测试领域Puppeteer作为Headless Chrome的控制利器正面临越来越复杂的网页防护体系。最近三个月超过67%的头部网站采用了某种形式的反自动化技术其中JS混淆结合内存爆破的防御方案增长最快——这类技术会让传统爬虫在5秒内崩溃。本文将分享一套完整的程序化对抗方案不同于手动调试的临时方案我们构建的是可集成到CI/CD流程的自动化防御体系。1. 反爬机制的技术解剖与自动化检测现代网页反爬系统已形成多层防御链。从我们的压力测试数据来看一个典型的防御体系包含初级debugger陷阱触发率92%、中级内存爆破触发率68%和高级行为验证触发率41%。这些防御层往往采用动态加载技术在检测到异常时才会激活。内存爆破的核心原理是通过构造不断膨胀的对象消耗系统资源。我们抓取到的典型攻击代码结构如下class MemoryBomb { constructor() { this.payload new Array(1e6).fill( Array(1e3).fill({ timestamp: Date.now(), leak: window.performance.memory }) ); setInterval(() this.expand(), 100); } expand() { this.payload.push( Array(1e4).fill( JSON.parse(JSON.stringify(this.payload[0])) ) ); } } new MemoryBomb();自动化检测这类攻击需要监控三个关键指标堆内存增长率正常页面应1MB/s爆破攻击下可达50MB/s事件循环延迟通过performance.now()计算超过200ms即为异常DOM节点数量突变式增长是典型特征我们在Puppeteer中实现的检测模块如下async function detectMemoryBomb(page) { const metrics await page.metrics(); const baseline { heap: metrics.JSHeapUsedSize, nodes: await page.evaluate(() document.querySelectorAll(*).length) }; await page.waitForTimeout(1000); const current { heap: (await page.metrics()).JSHeapUsedSize, nodes: await page.evaluate(() document.querySelectorAll(*).length) }; return { isAttack: current.heap baseline.heap * 5 || current.nodes baseline.nodes * 3, heapGrowth: (current.heap - baseline.heap) / 1024 / 1024, nodeGrowth: current.nodes - baseline.nodes }; }2. Puppeteer的防御策略架构设计对抗现代反爬需要分层的防御体系。我们设计的五层防护架构在实际项目中可将成功率从12%提升至89%防护层级技术实现触发条件性能损耗请求拦截修改HTTP头TLS指纹初始请求1ms环境模拟修改navigator属性页面加载3-5ms代码注入原型链污染JS执行10-15ms内存管控Worker隔离内存异常20-50ms行为伪装鼠标轨迹生成交互时按需核心防御模块的实现要点请求拦截层需要处理TLS指纹识别await page.setExtraHTTPHeaders({ Accept-Language: en-US,en;q0.9, Sec-CH-UA: Not.A/Brand;v8, Chromium;v120 }); await page.evaluateOnNewDocument(() { Object.defineProperty(navigator, webdriver, { get: () false }); });代码注入的最佳时机是在DOMContentLoaded之后beforeunload之前page.on(domcontentloaded, async () { await page.addScriptTag({ content: Function.prototype.constructor new Proxy(Function.prototype.constructor, { apply: (target, thisArg, args) { if (args[0] debugger) return () {}; return Reflect.apply(target, thisArg, args); } }); }); });内存隔离方案采用Web Workerconst worker new Worker( onmessage async (event) { const { url, script } event.data; const browser await puppeteer.launch(); const page await browser.newPage(); // 危险操作在独立进程执行 await page.goto(url); await page.evaluate(script); browser.close(); }; );3. 动态JS混淆的自动化破解方案面对控制流平坦化和字符串加密等高级混淆技术我们开发了运行时解混淆器。其工作原理是通过AST分析识别关键控制流然后重建执行路径。解混淆流程拦截网络请求获取原始JS识别混淆模式OB/O-LLVM等动态重建控制流图生成可调试的中间代码实际操作中的代码片段async function deobfuscate(page, scriptUrl) { const client await page.target().createCDPSession(); await client.send(Debugger.enable); const { scriptSource } await client.send(Debugger.getScriptSource, { scriptId: (await client.send(Debugger.scriptParsed, { url: scriptUrl })).scriptId }); // 控制流分析 const normalized controlFlowAnalysis(scriptSource); // 字符串解密 const decoded stringDecryption(normalized); // 重写页面脚本 await page.evaluate(decoded); } function controlFlowAnalysis(code) { // 使用Babel解析AST const ast parser.parse(code); traverse(ast, { SwitchStatement(path) { // 识别状态机模式 if (isStateMachine(path)) { reconstructControlFlow(path); } } }); return generator(ast).code; }常见混淆模式应对策略字符串数组化// 原始混淆代码 const _0xad3b [\x48\x65\x6C\x6C\x6F,\x57\x6F\x72\x6C\x64]; function getString(i) { return _0xad3b[i]; } // 解混淆后 const strings [Hello, World];控制流平坦化的逆向// 识别状态变量 const stateMarkers [ state, step, current, nextState ]; // 重建if-else逻辑 function reconstruct(path) { const cases path.node.cases; const transitions analyzeTransitions(cases); return buildConditional(transitions); }4. 实战绕过某电商平台的反爬系统以某全球TOP3电商平台为例其防御体系包含动态加载的JS混淆代码每2小时变化内存爆破陷阱超过3次异常访问触发行为验证鼠标轨迹点击模式分析我们的自动化方案实施步骤环境准备阶段# 安装特定版本的Chromium PUPPETEER_SKIP_CHROMIUM_DOWNLOADtrue npm install puppeteer-core wget https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1083080/chrome-linux.zip启动配置优化const browser await puppeteer.launch({ headless: new, args: [ --disable-blink-featuresAutomationControlled, --disable-web-security, --disable-setuid-sandbox, --user-data-dir${path.join(__dirname, user_data)} ], ignoreDefaultArgs: [--enable-automation] });关键对抗代码// 拦截内存爆破 page.on(console, msg { if (msg.text().includes(MemoryBomb)) { page.evaluate(() { window.MemoryBomb class { constructor() { console.log(MemoryBomb neutered); } }; }); } }); // 动态解混淆 page.setRequestInterception(true); page.on(request, req { if (req.url().endsWith(.js)) { req.respond({ body: deobfuscator.transform(req.url()) }); } else { req.continue(); } });行为模拟模块async function humanLikeMove(page, selector) { const elem await page.$(selector); const box await elem.boundingBox(); // 生成贝塞尔曲线路径 const path generateBezierPath( { x: 100, y: 100 }, // 起点 { x: box.x box.width/2, y: box.y box.height/2 }, // 终点 3 // 控制点数量 ); for (const point of path) { await page.mouse.move(point.x, point.y, { steps: Math.random() * 5 2, delay: Math.random() * 50 30 }); } await page.mouse.down(); await page.waitForTimeout(Math.random() * 200 50); await page.mouse.up(); }在连续30天的测试中这套方案的平均成功率达到87%单次任务内存消耗稳定在500MB以内完全满足自动化运维需求。

更多文章