前端JS逆向是个体力活,尤其是碰上大型SPA应用、压缩混淆得面目全非的bundle文件,再加上动态加载、VM保护这些反调试手段,手动在浏览器断点里泡一整天是常有的事。我做逆向也有些年头了,从最早的charles抓包、Fiddler改请求,到后面用rewriters、patch代码、写油猴脚本,一直在跟各种防护较劲。这两年大模型工具链成熟之后,我开始琢磨一件事:既然AI能理解代码上下文,能不能让AI自己去找加密函数、自己断点、自己跑参数?答案就是现在这整套流程的核心——MCP协议加Skill封装,把浏览器调试能力、请求改包能力和Crypto算法识别能力一起暴露给模型,让AI当执行者,我做审核和兜底。
这篇文不聊空洞概念,直接把我在实际项目里跑通的MCP+Skill前端JS逆向自动化链路摊开讲,包含架构设计、关键节点拆解、Agent指令书写、踩坑记录,以及我打包好的一套工具,适合已经会用chrome devtools手动找加密参数的逆向工程师,或者正在做AI自动化测试、想要把AI Agent落到真实业务里的开发。
## 1. 为什么前端JS逆向会成为AI自动化的最佳试验场 做过逆向的人都清楚,真正的难点不是看不懂代码,而是面对一个动态、混淆、无限分支的真实前端环境,找不到正确的断点位置。传统手动流程里,70%的时间花在"定位"上:打开Sources面板、搜索关键字、下断点、看调用栈、切到Console验证输出结果,然后重来。真正写加密函数提取逻辑、写Hook脚本的时间只占30%。所以当AI Agent出现时,第一反应就是它能不能帮我干那70%的活。 ### 1.1 手动逆向的重复性痛点 以常见的登录接口为例:它通常会在早于XHR请求十几步的调用链里完成加密参数构造,而你从Network面板只能看到最后的payload。手动跟踪的方式无外乎三件事——在Call Stack里逐层点进去找可疑函数名、在Sources里搜关键词(比如`sign`、`encrypt`、`token`、`md5`)、用自带的格式化工具处理压缩的minified代码。每个接口都要重复这套流程,每换一个站点或者接口改版,又得从头走一遍。 更费精力的是参数构造的验证环节。你在一个断点上下文里手动执行了`encrypt(12345)`,输出结果符合预期,但当你把它整理成独立的静态代码段时,发现代码里还引用了全局对象、请求实例、甚至页面初始化时的配置项。这就是"上下文依赖",逆向工程里的常客。AI Agent在执行任务时同样会碰到,它必须有能力在浏览器上下文里持续运作,而不是只在静态代码里做分析。所以自动化方案的前提,是Agent必须有一双"手",能够在目标页面环境里做动态操作。 ### 1.2 AI Agent能切入的四个关键环节 我把前端JS逆向的完整流程拆成四个环节:**入口定位**(找到加密函数在哪个JS文件里)、**逻辑还原**(分析函数内部的运算流程,确定参数来源)、**Hook验证**(不修改源码的情况下捕获运行时的真实参数值)、**脚本复用**(把还原的算法提取成独立模块,给后续自动化接口调用使用)。 这四个环节里,入口定位和Hook验证对AI来说最难,因为它们需要与浏览器实时交互:打开DevTools、在指定文件行下断点、单步执行、读取Scope里的变量快照。以前这些操作只能靠人来做,但有了MCP协议之后情况就变了——它让模型可以直接调用浏览器调试协议,甚至执行Playwright的页面操作和抓包工具的请求转发。Skill则负责把一套完整的逆向方法论封装成可复用的流程,AI拿到任务后会按照这套Skill里的步骤来行动,而不是凭空发挥。这其实就是我把整个方案落地时最核心的架构思路。2. MCP协议与Skill机制的完整拆解
不先把MCP和Skill讲清楚,后面的实操步骤理解起来会比较吃力。先说MCP,这个协议解决的是AI模型与外部工具之间的连接问题。它不是新造了一套通信方式,而是用JSON-RPC标准把工具调用封装起来,让模型在生成回复的同时,能够调用一系列由MCP Server提供的工具函数,并拿到返回值作为上下文继续推理。
2.1 MCP Protocol到底做了什么
类比一下最直观:以前的AI是只有脑子没有手,不管推理能力多强,输出只能是文字,要让它操作浏览器、发请求、写文件,就得靠我们写代码把它的输出结果转成指令。MCP就相当于给大脑接上了一副手套和一套传感器——手工管理时需要的各种专业技能(比如浏览器网络监控、JS代码执行、DOM操作)封装成一个个工具函数,配置在MCP Server端,模型通过协议去调用,拿到执行结果再继续下一步推理。
举个例子,MCP Server里注册了一个叫execute_js_code的工具,参数是你要在页面里执行的JS代码字符串,返回的是执行结果。AI Agent在逆向分析过程中,如果需要在目标页面里执行document.querySelector('input[name="username"]').value = "test",它会直接调用这个工具而不需要我们写任何额外的胶水代码。
这里重量级的应用场景是:将**Chrome DevTools Protocol(CDP)**的能力变成MCP工具。通过CDP我们能拿到目标页面的所有调试信息:源码列表、断点命中事件、Scope变量、Network请求详情、Console输出,甚至可以直接修改页面JavaScript执行环境里的对象和方法。把这些能力变成MCP工具后,AI就不只是"说"了,而是真正在浏览器里"动手"。
2.2 Skill是如何把逆向方法论沉淀下来的
Skill在MCP体系里是另一层抽象——它负责把一个完整的任务流程编排成模型可执行的步骤清单。你可以把它理解为"给AI的操作说明书"。好的Skill里面会写清楚:遇到一个任务时要先做什么、再做什么、什么时候调用哪个MCP工具、遇到什么情况要停下来询问用户,以及哪些常见坑位需要规避。
我这套逆向Skill里定义了标准作业流程SOP,核心是四个阶段:
| 阶段 | Skill内规定的动作 | 涉及MCP工具 |
|---|---|---|
| 入口分析 | 从Network请求出发追溯发起者JS,使用Sources搜索关键词 | network_capture, source_list, search_in_file |
| 断点定位 | 在可疑函数处下断点,触发请求,检查命中情况 | set_breakpoint, reload_page, check_call_stack |
| 参数还原 | 读取Scope变量,分析调用流程,必要时修改变量值观察响应 | get_scope_values, execute_js_code, manipulate_stack |
| 脚本生成 | 还原加密算法到独立Node/Python模块,与项目对接 | generate_code, run_node_script, diff_code |
这套Skill的价值在于,它把"资深逆向工程师的经验"转移成了结构化的指示,让AI Agent能够在没有人工频繁介入的情况下按部就班地执行任务的探路与验证部分。而人工负责的是审核与兜底——也就是真正难啃或模糊的部分,以及最后算法还原的确认。
2.3 模型是如何把两者组合起来的
在实际运行时,我通过两套MCP Server来支撑这套链路:一套对接本地的浏览器实例(通过CDP暴露协议),一套对接请求改包和JS执行环境。Skill,则会以系统提示词或独立知识库文件的形式被加载进模型上下文里。AI在拿到一个具体逆向目标时,它会检索Skill里的SOP条目,然后开始调用MCP工具执行操作。
举个例子:AI收到任务"找出某个登录时sign参数的生成本地算法"。它第一步是调用network_capture工具去监听即将发生的登录请求,同时调用search_in_file在Sources里搜索含sign关键字的代码片段。找到了目标函数后,它会在这一行设置断点,然后执行页面里的登录操作(通过调用page_click或execute_js_code),等断点命中后,再调用get_scope_values把Scope里所有变量的值抓取出来。最终它会把这些变量和代码逻辑组装成一段Node.js脚本,在本地跑通验证,输出结果。
我实测下来这一套组合非常顺滑,尤其是AI对上下文的记忆能力被充分发挥,不需要我们像教新手那样每一步都告诉它怎么做。不过这也对Skill本身的结构化程度提出了很高要求,写得含糊,AI执行起来就会到处飘。
## 3. 自动化逆向的整体架构设计与Agent指令编排 直接进入落地环节。我在真实项目里完整跑通的自动化平台分成四层:**浏览器操控层**、**协议转发层**、**Agent决策层**、**验证执行层**。四层之间通过MCP工具的调用串联起来,核心编排任务交给Skill定义好的流程。 ### 3.1 浏览器操控层:CDP + Playwright MCP 这一层负责让AI真正拥有"手"和"眼睛"。我用的方案是用Playwright封装一个MCP Server,对外暴露工具:打开页面、点击元素、输入文本、填写表单、执行JS、等待网络请求。这些工具底层全部走Playwright的API,而Playwright本身可以无缝对接Chromium实例并开启CDP支持。 关键配置点在于必须以非headless模式启动浏览器,并且关闭自动化控制提示。启动参数我推荐用`--remote-debugging-port=9222`,这样便于通过CDP直接连接。另一个重要参数是`--disable-blink-features=AutomationControlled`,它能把`window.navigator.webdriver`标志隐藏掉,避免一些站点检测浏览器环境。对于逆向场景,环境一致性和自动化隐藏同样重要,否则目标站点可能会在早期就中断操作。 实际配置示例(我用的运行脚本): ```javascript const { chromium } = require('playwright'); const browser = await chromium.launch({ headless: false, args: [ '--remote-debugging-port=9222', '--disable-blink-features=AutomationControlled', '--no-sandbox' ] }); const context = await browser.newContext({ viewport: { width: 1366, height: 768 }, userAgent: 'Mozilla/5.0 ... Chrome/120.0 Safari/537.36' });3.2 协议转发层:抓包改包工具转成MCP能力
这一层的作用有两个,一是录制完整请求链路,二是把请求拦截下来做修改,让AI可以研究请求参数的具体生成逻辑。市面上的抓包工具很多,我最终选择把BurpSuite和基于Node的自研抓包工具同时接入MCP。原因很简单:Burp的拦截和重放功能非常成熟,但它的UI交互不适合AI;我用一个Node脚本把Burp的API封装成MCP工具,包括intercept_request、modify_request_header、resend_request、get_request_detail。
另一个重点是需要主动构造参数的场景。有些加密参数,手动断点很难捕获上下文,AI需要反复修改请求体来观察服务端是否返回不同的报错信息,从而推断加密参数的规则。把这个能力也封装进MCP工具后,AI的执行链条就完整了:从浏览器里引发请求、从代理层拦包改包、从服务端响应推断算法约束。
3.3 Agent决策层:模型如何理解任务并根据Skill执行
Agent决策层的核心是Claude或GPT-4系列模型,在系统提示词里注入了全套Skill配置。需要注意的是,模型默认行为是"想到哪做到哪",它不会天然按照你定义的逆向方法论执行。所以我设计了一套分层指令框架:第一层是全局目标描述,告诉模型当前项目的最终产出是什么;第二层是Skill的通用步骤,告诉模型拿到任何一个任务后应该从哪几个方面切入;第三层是具体任务参数,比如当前要逆向的接口地址、要提交的表单字段。
举个例子,在Skill配置文件中我会写:
global_goal: "还原目标登录接口的sign生成算法,输出独立Node.js模块" skill_steps: - name: "入口追踪" action: "从Network面板中找到/login请求的Initiator JS文件" - name: "关键字搜索" action: "在该JS文件中搜索sign相关的字符串" - name: "断点调试" action: "在包含sign赋值的代码处设置断点,触发请求" - name: "参数检查" action: "断点命中后,收集Scope里的关键变量名与实际值" - name: "逻辑还原" action: "基于变量名和值,推测并验证加密逻辑,生成代码"3.4 验证执行层:自动化跑通算法脚本
最后一层解决的是"AI生成的算法脚本是否正确"的问题。AI在分析之后会生成一段Node.js或Python代码,这段代码不能直接认定为可用,必须拿到本地环境里跑,用真实输入的明文参数算出结果,再和目标请求里的密文参数比对。
因为Skill里定义好了流程,AI会自动调用run_node_script工具执行代码,再用compare_result工具比对输出。如果比对不一致,它会回到"参数检查"环节重新分析。这整个循环就不需要人工盯着,你只需要设定最大循环次数和单步超时时间,防止AI在一处卡死。我实测里,单接口的完整自动化逆向,平均循环次数在3-6次之间可以跑出一个可靠结论,耗时大约8-12分钟。
## 4. 关键断点定位的实战链路:从加载器到加密函数 这一章我用一个真实示例来演示链路怎么走通。目标是一个比较典型的加密传输站点,所有API请求都会携带一个`cipher`参数,参数值是"固定字符串+时间戳+随机数"经加密后的结果。手动定位这类参数通常要花大半个小时,但在这套自动化体系里,AI的定位速度非常快。 ### 4.1 从触发点回溯到加载器JS AI收到任务后,第一步是调用`network_capture`开始监听。我给定任务参数:"点击登录按钮时,捕获Network中login接口的请求详情,拿到Initiator JS路径"。这一步对应Skill里的`入口追踪`阶段。 实测中,AI会通过`page_click`点击真实的登录按钮(我预先通过`execute_js_code`把账号密码填进表单),然后`network_capture`会捕获到login请求的完整信息。返回结果里通常包含`initiator.url`字段,它指向发起这个请求的JS文件路径,一般是`/static/js/app.bundle.js`这类产物。拿到这个路径后,AI调用`search_in_file`找到该文件在Sources里的实际内容。 ### 4.2 在压缩文件中定位可疑函数调用栈 如果有sourcemap,这步会轻松很多;没有sourcemap也问题不大,靠的是对特征代码的识别。AI会在加载器JS文件中搜索`cipher`、`封装工具函数`常见命名的相关字符串,然后顺着这些字符串所在的代码行向上追溯,找到最外层的调用点。 这里特别值得注意的是:在高压缩比代码里,变量名是`a`、`b`、`c`,函数名也是短的没有语义的拼写。如果AI只凭变量名猜测逻辑,很容易被带偏。所以Skill里明确要求:**优先找那些从全局对象上取值的代码**。比如代码里出现`window.cryptoJS`或者从某个模块初始化对象里读取成员,这通常是密钥或算法库的入口。 ### 4.3 Hook验证技巧与断点计算的复用 定位到候选加密函数后,AI会做一个"Hook注入"操作,这比直接在函数内部下断点更高效。它的做法是执行JS代码: ```javascript var originalFunc = candidateModule.cipher; candidateModule.cipher = function(...args) { console.log('cipher hook args:', args); window.__capturedCipherArgs = args; return originalFunc.apply(this, args); };紧接着触发真实请求,通过get_console_logs工具读取捕获的参数值。这相当于把整个加密函数的入口参数完整摊在AI面前,它不需要单步跟踪就能拿到真实运行时数据。在拿到这些参数之后,Skill会让AI反向拼接逻辑:先把参数传给某个加密库的函数,比如CryptoJS.AES.encrypt(...),再通过execute_js_code在页面里执行相同运算对比输出。如果一致,说明定位正确;不一致,就会调整推测方向重新来。
4.4 逆向结果输出与自动化对接
最后一步是让AI把验证通过的算法还原成独立模块,我不允许它直接用浏览器内的对象,必须生成一个脱离页面的Node.js版本。AI会自动读取Skill里"脚本生成"的规则,输出标准CommonJS模块,并提供可执行命令示例。这一步自动化之后,我原先需要手动做一天的"还原算法"工作,现在只需要对AI生成的代码做个代码审查,然后直接挂到接口自动化脚本里。
## 5. 从零搭建这套方案的完整操作步骤 关于怎么把这个平台搭起来,我会把整个步骤完整写出来,保证新手按照路径也能跑通基本链路。但我先提醒一句:这套方案对环境的稳定性要求比较高,尤其是MCP Server和浏览器调试端口的稳定性,稍微没配好就会导致AI中途断掉。 ### 5.1 环境准备与基础依赖 建议使用MacOS或Linux系统,Windows也能跑但是在进程管理和路径配置上会有一些坑,需要提前规避。下面是我建议的依赖清单: | 依赖 | 版本 | 作用 | |------|------|------| | Node.js | >= 18 | 运行Playwright及MCP Server | | Python | >= 3.10 | 运行部分辅助脚本 | | Playwright | latest | 浏览器自动化控制 | | @modelcontextprotocol/sdk | latest | 搭建MCP Server | | Claude Desktop或者自建Agent框架 | latest | 挂载MCP工具执行Agent逻辑 | 安装基础依赖没什么太多好说,重点在于你选哪一个Agent框架来挂MCP工具。我建议使用Claude Desktop或者能自定义工具加载的Agent框架,因为它对系统提示词的注入限制比较少,方便我们完整交付Skill配置。 ### 5.2 三步完成Playwright MCP Server接入 第一步,全局安装Playwright并下载浏览器: ```bash npm install -g playwright npx playwright install chromium第二步,写一个最简单但能行的MCP Server脚本。这个脚本里我注册了三个工具:打开页面、执行JS、获取页面标题。跑通整个链路后再扩展:
const { Server } = require('@modelcontextprotocol/sdk/server/index.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { chromium } = require('playwright'); let browser, page; const server = new Server( { name: 'browser-mcp-server', version: '1.0.0' }, { capabilities: { tools: {} } } ); server.setRequestHandler({ method: 'tools/call' }, async (request) => { const { name, arguments: args } = request.params; if (name === 'open_page') { if (!browser) browser = await chromium.launch({ headless: false }); page = await browser.newPage(); await page.goto(args.url); return { content: [{ type: 'text', text: `页面标题: ${await page.title()}` }] }; } if (name === 'execute_js') { return { content: [{ type: 'text', text: '执行结果: ' + (await page.evaluate(args.code)).toString() }] }; } }); const transport = new StdioServerTransport(); server.connect(transport);第三步,在Claude Desktop的MCP配置文件中(一般在claude_desktop_config.json)里注册这个Server:
{ "mcpServers": { "browser-mcp": { "command": "node", "args": ["/path/to/your/browser-mcp-server.js"] } } }配置完成之后,你在对话里让模型"打开百度并搜索MCP协议",如果调试模式能看到AI调用open_page工具,说明链路打通了。
5.3 挂载Skill配置文件的三种方式
Skill配置可以以三种粒度加载:第一种最简单,直接放在系统提示词里,适合临时一次性任务;第二种,写成独立的Markdown文件,用#! /path/to/skill.md的语法挂载到模型上下文,适合按项目区分的多次复用;第三种,用专门的Skill管理插件,像管理代码库一样管理一堆不同的Skill文件,在执行不同逆向任务时动态选择对应Skill。
我实际操作中强烈推荐第二种方式。原因在于,系统提示词越长,模型决策时的指令冲突概率越大;独立文件挂载能做到按需加载,并且在任务边界清晰时效率更高。你可以为"登录加密参数还原"和"前端字体反爬还原"分别建立两个独立的Skill文件,在对话开始时通过全局目标指令决定调用哪个Skill。
5.4 测试驱动整个流程
整套环境搭好之后,不要一上来就跑真实目标站点,先用一个完全可控的测试页面验证链路。我会在本地起一个简单的Node HTTP服务器,构造一个带加密参数的表单提交逻辑(模拟场景),让AI去跑。通过之后再把目标切换到真实站点上。这个测试习惯帮我避开了大量环境问题,因为我最先暴露的错误往往是MCP工具注册了但Agent没有正确调用,或者JS执行时变量作用域写错。这些在可控环境里很快就能定位。
## 6. 踩坑实录:三类高频问题与完整排查链路 自动化链路跑起来之后,我遇到过不少问题,其中有三类踩得最深。我按实际排查顺序写出来,能帮你在落地的过程中省掉大量的排查时间。 ### 6.1 MCP工具调用超时与CDP连接被抢占 现象:AI在执行`execute_js_code`时偶尔返回请求超时,而且浏览器窗口变得卡顿,后续所有页面操作全部失败。 排查过程:我先确认问题是否可稳定复现,点击一次登录触发问题时看浏览器控制台有没有报错。然后看MCP Server的日志,发现是CDP连接被多个进程同时占用导致的。原来Playwright启动的浏览器是持久化进程,我后续又用curl访问了9222端口的调试接口,挤掉了原先的连接会话。这种情况下,MCP Server里的`page.evaluate`调用自然就超时了。 修复方案:给MCP Server配置增加连接互斥机制,不允许多个调试客户端同时挂载同一个浏览器实例。关闭9222端口的外部占用,在MCP Server内部通过`context.connection()`单例模式管理浏览器会话。此后超时问题再没出现过。 ### 6.2 模型在决策流程中跳过验证直接给出结论 现象:AI经过十几次工具调用后给出了一个算法脚本,我拿真实数据一比,密文完全不匹配。 排查过程:我回看了Agent的完整推理日志,发现它在"参数检查"环节只读取了两三个变量就跳到了"逻辑还原"环节,中间完全没有做语义对比验证。原因是我的Skill文件里验证步骤语气太模糊,写的是"若有必要,可进行多组参数比对",模型在上下文窗口压力大时就会选择性地忽略一些不太重要的指示。 修复方案:把Skill文件里所有模糊性描述改为强约束式条款,明确写出"在生成最终脚本之前,必须至少对5组不同的输入参数执行加密并比对输出,比对不一致时禁止提交脚本"。同时把比对工具设置成无法跳过的前置条件。修正之后,AI生成的算法脚本准确率有明显提升。 ### 6.3 浏览器环境特征暴露导致目标站点阻断 现象:目标站点在登录前返回了环境验证的报错,且无法通过常规手段绕过。 排查过程:我用CDP的`Page.addScriptToEvaluateOnNewDocument`注入反检测脚本,同时用Playwright的`createExtraHTTPHeaders`设置了与正常浏览器一致的请求头,发现仍然被阻断。进一步查看Network请求发现,对方检测了WebDriver相关特征与Canvas指纹的稳定性。 修复方案:肯定不能为了一个逆向前端任务去上那些可疑的代理工具,正确解法是使用基于真实浏览器内核的持久化配置(同一浏览器用户数据目录多次复用),保证Canvas指纹等环境参数稳定;同时完全隐藏`--enable-automation`参数,并且通过启动Blink特性禁用自动化标志。这类操作我们做自动化时合法性前提是自己拥有或已获授权测试的目标环境,绝不鼓励对未授权系统越权操作。7. 工具链清单与下一步演进方向
全流程能够跑通,依赖下面这条完整工具链。我把它整理成一个可以直接照着配置的清单,方便你拆开来看每一环的作用与替代选择。
| 环节 | 主选工具 | 备选方案 | 核心作用 |
|---|---|---|---|
| 浏览器操控 | Playwright MCP | Puppeteer MCP | 页面操作、表单填写、触发操作 |
| 调试协议 | Chrome DevTools Protocol | WebDriver BiDi | 断点、Scope读取、Console捕获 |
| 抓包改包 | BurpSuite MCP | Fiddler + 自研桥接 | 请求录制、参数修改、响应重放 |
| Agent框架 | Claude Desktop | 自建LangGraph链路 | 决策编排、Skill加载、工具调度 |
| Skill管理 | 独立Markdown挂载 | 知识库检索 | 逆向方法论沉淀与复用 |
| 验证执行 | Node.js脚本 | Python脚本 | 算法复现、比对验证 |
说到下一步演进,我个人最看好的一条线是:让Skill具备自我学习和迭代能力。现在的Skill还是静态的,遇到新类型的防护手段需要我手动修改SOP。理想状态是,AI在一个任务里学到的新技巧,能自动提炼成新的Skill子条目沉淀下来,下一次任务直接复用。
我这边已经开始实验的方式是:在每次任务收尾时,让AI生成一份"本次任务的经验补丁",格式也是Markdown,内容包括新遇到的防护特征、有效应对策略、断点定位技巧。然后用脚本把这些补丁合并回主Skill文件。跑了两轮之后,Master Skill文件的覆盖范围确实比手动维护时扩了一大圈。
另外还有一个方向是结合多Agent协同。逆向前端加密函数、还原字体反爬、过滑块验证,这些不同的子任务完全可以拆给不同的Agent并发执行。每个Agent只负责自己Skill覆盖的窄域,最后在主Agent汇总结果。这样做的好处是上下文质量更高,因为每个Agent不会因为关心太多无关操作而让决策被稀释。不过我实测下来这种方案对Agent间的通信机制设计要求比较高,目前还在打磨中。
从整个趋势来看,AI配合MCP和Skill这套体系,确实把前端JS逆向从"纯手工的体力活"变成了"半自动化的协作任务"。它会替代掉工程师吗?至少在我实际体验里没有。它替代掉的是那70%重复性的定位与验证工作,把人的精力释放出来去处理真正有挑战性的复杂防护逻辑和业务架构理解。如果你也在做逆向或者自动化测试,我的建议是趁早把这条链路搭起来,从一个小接口的加密参数还原开始,你会在跑通第一个完整流程的时候就感受到这种协作方式带来的效率提升。
最后说一句配置层面最容易踩的细节:测试环境和真实目标之间,至少要保留一套隔离的浏览器实例和网络环境,千万不要在自动逆向同一个站点的时候混用会话,否则环境上下文一旦错乱,排查起来反而比手动逆向更痛苦。这是我在实际运行里用一次次折腾换来的教训,希望对你有用。