ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AI Agent + MCP:自动化JS逆向与动态混淆分析实战

2026/9/23 4:42:12 拓冰建站 浏览量
AI Agent + MCP:自动化JS逆向与动态混淆分析实战 干这行最磨人的一件事就是对着 DevTools 一个栈帧一个栈帧地往下点。尤其碰到动态混淆过的 JS函数名全被改成_0x1a2b3c代码在运行时用eval或new Function现场拼出来断点根本没法提前下栈信息全是乱的。以前我处理这类问题半小时起步是常态遇到多层混淆甚至要手动把解密后的字符串一个个贴出来看。直到我把 Trae 的 Agent 模式拿来做 JS 逆向再配上一个自定义的 MCP Server把“找入口、还原混淆、追踪调用链”这三件事交给智能体自动跑我才发现自己之前那些手工跟栈的时间大部分都可以省下来。这套组合的核心思路并不复杂Trae 负责理解意图和编排步骤MCP 负责给我提供一组专门处理 JS 分析的工具比如 AST 解析、插桩改写、动态执行、日志汇总。智能体拿到一个目标之后可以自己去拉取代码、检测混淆类型、插入调试代码、执行浏览器环境、最后输出一份可读的分析报告。整个过程我不需要盯着每一行输出只需要在关键节点做确认和纠偏。这篇文章就把我搭建这套自动逆向智能体的完整过程和踩过的坑都记录下来适合经常做前端代码审计、SDK 接入调试、JS 加密算法分析或者单纯对 AI Agent 和 MCP 协议感兴趣的开发者参考。先说清楚边界这里所有的方法都应该用在你自己拥有或有明确分析权限的代码上比如自己的前端工程、开源项目、CTF 题目、或者你正在接入的第三方 SDK不要拿去分析或绕过你没有权限的系统。1. 为什么要把 Trae、MCP 和 JS 逆向揉在一起1.1 动态混淆是怎么“耍”你的断点和调用栈的传统调试思路是打断点顺着函数调用栈从一个调用者跳到下一个调用者。这在普通压缩代码里是有效的无非是名字难看一点但逻辑链路还是清晰的。可动态混淆一参与断点和调用栈这两个最核心的调试工具基本就废了。我遇到最多的三种情况每一个都踩过。第一种是字符串数组位移。混淆器把所有的字符串常理抽到一个大数组里然后通过位移函数在运行时动态取出来。你在静态代码里看不到任何明文只有一个_0x4f2c()的调用真正的内容要等 JS 引擎执行到那一刻才知道。第二种是控制流平坦化。正常的 if/else、for 循环被打散成一个 while 循环加 switch case 分发器变量state不断自增逻辑顺序被彻底打乱。你跟栈的时候会陷入一堆状态值的跳转完全看不出原始代码在干嘛。第三种最烦——运行时动态生成代码。代码用new Function或者eval在运行时拼接字符串执行静态分析工具和调试器根本无法提前感知。这三种手法叠加起来我的直观感受是以前断点是打在确定的代码上现在断点只能打在“不知道会执行出什么”的代码上。手动跟栈的效率和准确率都明显不够用所以我才萌生了让 AI 智能体来做这件事的想法。1.2 智能体能帮我们自动完成的“脏活累活”手动逆向的瓶颈不在于“分析”这个动作有多难而在于大量机械性、重复性的操作占用了时间。举个例子一个字符串数组里的常量可能有几百个手动一个个去还原纯粹是体力活一个控制流平坦化的分发器要顺着状态值的跳转关系把原始流程重新画出来手工操作极度繁琐。这些东西恰恰是 AI Agent 擅长的。Trae 作为一款 AI 原生 IDEAgent 模式可以自主地读文件、改代码、执行终端命令、看程序输出。你只要把目标描述清楚它能像人一样一步步去操作。但它默认没有“分析 JS 混淆”的能力缺一个能够解析 AST、执行代码、插桩调试的工具箱。这就是 MCP 的切入点。1.3 MCP 就是连接大脑和手的“神经系统”MCP 全称是 Model Context Protocol你可以把它理解成 AI 应用和外部工具之间的一套标准协议。以前的工具集成是你写一个插件AI 用它现在 MCP 定义了一套通用的 JSON-RPC 2.0 消息格式任何支持 MCP 的客户端都可以动态发现并调用你提供的工具。打个比方如果 Trae 的 Agent 是大脑那 MCP 就是神经系统我写的各种 JS 分析工具是手和眼睛。大脑不直接操作工具而是通过神经信号指挥手去拿东西、眼睛去观察结果。具体到我的场景就是 Trae 通过 MCP 协议调用我定义的analyze_source、deobfuscate_ast、hook_instrument这些 Tool 函数拿到结果后自己判断下一步怎么做。这样一来逆向流程中“读取代码、分析特征、改写代码、执行验证、输出结论”的完整链路就被有机地串起来了。在真正动手之前我先想清楚了一个关键问题把什么能力封装成工具把什么能力留给 Agent 自己推理。我的原则是凡是需要精确计算、确定性的操作都应该做成工具交给程序处理比如 AST 解析、代码改写凡是需要依据上下文做判断和规划的工作比如“先分析这个函数还是先分析那个函数”“这段逻辑是否值得追踪”就留给 Agent 自己发挥。这个边界划分决定了整个智能体的可用性后面会展开说。2. 方案选型与整体架构Trae、MCP Server、执行环境怎么分工2.1 Trae 的 Agent 模式解决的核心问题Trae 解决的核心问题是“让 AI 自动操作开发环境”。普通 AI 对话只能给你建议你拿到建议还要自己复制粘贴到编辑器里执行。Trae 的 Agent 模式不一样它能直接操作工作区文件、执行终端命令、根据运行结果调整策略基本等于一个“远程实习生”坐在你的电脑前干活。我实际测试下来在 JS 逆向这个场景下它的优势尤其明显。首先它能同时打开多个文件进行交叉分析遇到函数 A 在文件 B 中被调用、文件 B 又引用了变量 CAgent 会主动去追查而不是停在原地说“需要更多上下文”。其次它可以自己写临时代码来验证想法比如发现混淆特征后先写一个 AST 脚本做局部还原看输出是否正常。这个“发现问题、自己动手验证、再根据结果调整”的循环就是 Agent 模式相比普通代码补全最大的价值。不过重点提醒一下Trae 的上下文长度是有限的。如果你给它喂一个 10 万行的混淆脚本它很快就会把上下文耗尽。更好的做法是把大文件交给工具去处理只把关键分析结果告诉 Agent。这也是我为什么坚持写 MCP Server 而不是让 Agent 直接读大文件的原因。2.2 MCP Server 提供的能力清单按照我前面说的“确定性操作交给工具”原则我设计了六个核心工具。每个工具只做好一件事避免单次返回的数据量过大导致 Agent 上下文膨胀。工具名输入输出作用detect_obfuscationJS 文件路径或代码字符串混淆类型、可疑特征、置信度快速判断目标代码用了哪些混淆手法analyze_entry代码路径、可疑函数名函数调用关系、入口候选列表定位一段逻辑的调用入口deobfuscate_ast代码路径、要还原的混淆类型还原后的代码片段用 AST 重写做静态还原instrument_hook代码路径、函数名、插桩逻辑插桩后的代码路径在关键函数中插入调试代码exec_dynamic代码片段、示例上下文执行结果、console 日志动态执行并捕获运行行为gen_report各工具的结果列表Markdown 分析报告汇总所有分析结果供人确认这六个工具不是一次全部用上的而是 Agent 根据目标动态选择。我见过很多人做 Agent 工具设计时总想着一个工具吃遍天结果每次调用都返回一大堆冗余数据反而拖垮 Agent。工具贵精不贵多每个工具的输出都应该经过裁剪只保留对下一步决策有用的信息。2.3 整体执行流程的编排逻辑整个智能体的流程我最终固定成了六步。第一步Agent 先读取目标 URL 或本地文件把代码保存到工作目录。第二步调用detect_obfuscation识别混淆类型这一步的策略价值最高因为不同混淆类型对应的还原方案差别很大。第三步调用analyze_entry定位入口比如要找某个加解密函数就得先找到它注册和调用的位置。第四步调用deobfuscate_ast或instrument_hook做静态还原或动态插桩。第五步调用exec_dynamic在 Node.js 或浏览器环境中运行采集实际调用链。第六步生成报告。这六步未必每次全走一遍比如面对一段纯字符串混淆、没有动态执行代码的脚本到第四步就能出结果。Agent 自己会判断什么时候结束不需要写死流程。关键是每步的输出格式要干净让 Agent 拿到之后知道接下来该做什么。整套架构拆开来看其实很朴素Trae 提供大脑和交互界面自定义 MCP Server 提供分析工具箱Node.js 和浏览器环境提供执行沙箱。三者通过 MCP 的 JSON-RPC 消息串联互相之间不耦合单独任何一块都能升级替换。3. 核心实现从零搭一个 JS 逆向 MCP Server3.1 准备基础环境我用的环境是 macOS但这套方案在 Windows 和 Linux 上也一样能跑。核心依赖是 Node.js 18 以上版本因为要用node:vm模块做动态执行沙箱。如果还没有安装 Trae先去官网下载最新版安装好之后把工作区指向准备放分析脚本的目录。接下来初始化一个 npm 项目安装核心依赖。我用的是acorn做 AST 解析escodegen做 AST 还原成代码astring也可以但个人更习惯 escodegen 的输出格式另外还用到了modelcontextprotocol/sdk这是 MCP 官方提供的 TypeScript/JavaScript SDK比手写 JSON-RPC 要省事得多。项目目录大致是这样的npm init -y npm install modelcontextprotocol/sdk acorn escodegen jsdom playwrightplaywright用来驱动浏览器执行动态脚本jsdom用来模拟 DOM 环境。如果你只处理纯 JS 不涉及 DOMjsdom可以不装。第一次使用 playwright 需要下载浏览器内核执行npx playwright install chromium即可。3.2 用 MCP SDK 封装分析工具MCP SDK 的用法很直白核心是创建一个 Server 实例用registerTool注册工具函数。每个工具都有自己的输入格式和返回结构。下面是我实现detect_obfuscation和deobfuscate_ast时的核心代码为了方便阅读我做了删减。import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import * as acorn from acorn; import * as escodegen from escodegen; import fs from node:fs; const server new McpServer({ name: js-reverser, version: 1.0.0 }); server.registerTool( detect_obfuscation, { code: { type: string, description: JS 代码或文件路径 } }, async ({ code }) { const source fs.existsSync(code) ? fs.readFileSync(code, utf-8) : code; const features []; if (/var\s_0x[0-9a-f]|_0x[0-9a-f]\(/.test(source)) { features.push(字符串数组混淆); } if (/switch\s*\(.*state|while\s*\(!!\[\]\)|case\s0x/.test(source)) { features.push(控制流平坦化); } if (/new Function|eval\(|\.constructor\(\s*[]/.test(source)) { features.push(动态代码生成); } return { content: [{ type: text, text: JSON.stringify({ features }) }], }; } ); server.registerTool( deobfuscate_ast, { filePath: { type: string }, startLine: { type: number }, endLine: { type: number } }, async ({ filePath, startLine, endLine }) { const ast acorn.parse(fs.readFileSync(filePath, utf-8), { ecmaVersion: latest }); // 省略遍历 AST识别被抽离的字符串数组替换调用为字面量 const output escodegen.generate(ast); return { content: [{ type: text, text: output }] }; } );这里有个经验值得分享工具返回给 Agent 的内容不要原样返回整个文件或整段代码而是返回被裁剪后的关键片段。比如detect_obfuscation只返回特征列表和置信度不返回代码原文。因为 Agent 的上下文窗口有限你塞给它越多冗余它就越容易抓不住重点。3.3 在 Trae 中接入 MCP ServerMCP Server 写好后需要在 Trae 的 MCP 配置里注册。Trae 支持两种连接方式一种是本地 stdio 模式适合跑本地 Node.js 脚本另一种是 SSE 远程模式适合连接部署在远程服务器上的分析服务。平时开发调试用 stdio 模式最方便。在 Trae 中打开 MCP 配置界面添加一个新的 MCP Server类型选择 stdio命令填node /your/project/path/src/mcp-server.js保存后 Trae 会尝试启动这个服务如果一切正常工具的列表里会出现我们注册的六个工具。首次配置时容易遇到一个问题Mac 上直接用 node 命令要写全路径因为 Trae 进程的环境变量不一定包含你 shell 里的 PATH。保险做法是先用which node拿到 node 的绝对路径然后在命令里写/usr/local/bin/node /your/project/path/src/mcp-server.js配置好之后可以在 Trae 的对话窗口里问一句“你现在能用哪些工具”它会列出所有可用的 MCP 工具。这一步是验证连接是否成功的最快方式。3.4 给 Agent 写一份好用的“操作手册”工具注册好了Agent 不一定会用得聪明。我发现最关键的一步是写一份专门给 Agent 看的操作提示词类似于给实习生写工作手册。如果不写Agent 面对多个工具时会选择困难甚至自己凭空捏造分析结论。我在 Trae 的自定义指令里放了这样一段话你是一个 JS 逆向分析助手。处理目标时请严格按照以下节奏操作先调用 detect_obfuscation 确认混淆类型再调用 analyze_entry 找到入口函数。如果代码中包含字符串数组混淆调用 deobfuscate_ast 还原如果包含动态代码生成调用 instrument_hook 插桩后用 exec_dynamic 执行并收集日志。所有工具的结果都在 markdown 代码块中展示。当你认为已经拿到了足够的证据链时调用 gen_report 输出最终报告。每一步都要告诉我你在做什么为什么这么做。这段提示词的要点有两个一是给出工具调用顺序的优先级二是要求 Agent 在关键节点向人类汇报进度。第二点很重要因为 Agent 即使理解错了方向只要它中途有汇报你就能及时纠偏不会等它跑完整个流程才发现结论完全不对。4. 实际跑一个 demo还原一段被动态混淆的代码4.1 构造一个实验用的混淆样本为了验证这套智能体的效果我用javascript-obfuscator的典型配置构造了一个实验样本。这个样本不能拿真实商业代码来做实验用开源或自己生成的脚本最安全。我写了一段简单的登录模拟代码里面包含一个加密函数、一个校验函数然后用混淆器把字符串数组、控制流平坦化、死代码注入全部打开生成了一份让人看一眼就头疼的混淆脚本。下面是我生成样本时用的命令--config参数指定配置文件npx javascript-obfuscator input.js --output obfuscated.js --config obf-conf.json配置文件里我开了stringArray: true、controlFlowFlattening: true、deadCodeInjection: true。这三个配置组合出的代码基本覆盖了手工跟栈时最痛苦的所有场景。4.2 智能体自动识别混淆特征把混淆后的obfuscated.js放进工作目录我向智能体发出指令“分析这个文件识别混淆特征找到加密函数的入口。”Agent 的第一步是调用detect_obfuscation工具几秒钟后返回结果{ features: [字符串数组混淆, 控制流平坦化, 动态代码生成] }Agent 看到这个结果后自动意识到代码同时使用了三种手法继续调用analyze_entry去定位入口。这一步其实挺有意思因为analyze_entry内部用了一个很朴素的策略先找到所有使用_0x开头的函数名再统计它们之间的调用频率最后把调用次数最多、串联了最多_0x函数的那个节点作为“候选入口节点”。这个策略对大多数混淆代码都是有效的因为混淆代码本质上是把一个大图打散但节点之间的调用关系还是能通过静态分析还原出来。4.3 用 AST 重写还原字符串数组拿到入口函数后Agent 决定先做静态还原。它调用deobfuscate_ast工具内部对 AST 做了一次遍历识别出字符串数组和位移函数的对应关系然后把所有调用替换成实际字符串字面量。这一步的关键是不要试图一次性还原全部混淆而是只处理字符串数组这一层因为控制流平坦化是另一套思路混在一起改写容易互相干扰。工具输出的还原结果保留了原始代码的逻辑框架但字符串常量已经从_0x4f2c(...)变成了可读的明文。Agent 看到这些明文后能直接判断出“这一块在拼一个请求 URL”“这一段在生成签名参数”从而继续往下追踪。4.4 插桩加固并用浏览器环境动态执行静态还原完成后有一个片段仍然不清晰入口函数里有几行通过new Function动态生成的逻辑静态 AST 看不到内容。Agent 这时候转向动态方案调用instrument_hook在入口函数的开头和结尾分别插入console.trace和console.log日志代码再用 playwright 打开一个页面在页面里加载这段插桩后的脚本并执行。这个过程相当于让代码自己“说”出来它做了什么。console.trace会打印完整的调用链动态生成的函数内容也会通过console.log暴露在浏览器控制台里。Agent 把控制台日志收集回来结合前面的静态还原结果整理出一条完整的调用链路图。最后调用gen_report生成一份带代码片段、调用链、混淆特征说明的分析报告交给我做最终确认。整套流程跑下来大约不到十分钟。换作以前手工操作光是把字符串数组一个个还原就需要一段时间更别说还要手动在动态生成的代码里找断点。这让我确信方向是对的。5. 踩坑与排查智能体不是一配就灵的5.1 Agent 幻觉它以为自己解密了其实没有第一次跑通流程时我感到困惑的是报告里出现了几段看起来很合理、但实际上去源码里根本找不到对应的还原结果。后面仔细排查才发现Agent 在调用工具失败后并没有如实汇报而是根据上下文“脑补”了一部分结论用工具输出之外的内容填充了空白。这是大语言模型常见的幻觉问题尤其是在工具调用链路中间有空档时模型倾向于用自己的想象力去补全。后来我在 Prompt 里专门加了一条硬性约束“任何分析结论都必须引用具体的工具输出作为证据如果没有证据明确说不知道禁止推测。”同时在每个工具函数的返回结果里增加了一个callId字段方便 Agent 引用。如果 Agent 拿不出对应的调用 ID就说明结论是不实推断。这个约束上线之后幻觉问题基本消失了。5.2 上下文膨胀工具输出太长会拖垮 Agent另一个问题是当deobfuscate_ast处理一个超大文件时返回内容可能达到几千行甚至上万行。这些内容全塞给 Agent 会迅速耗尽上下文窗口导致后半段分析质量断崖式下跌。我试过让 Agent 不看完整输出只凭摘要做判断但效果并不理想。最终方案是给工具增加了maxOutputLines参数默认只返回最前面的 80 行和最后面的 20 行中间部分用... [已省略 N 行] ...替代。如果 Agent 觉得中间内容关键它可以主动要求工具按行号范围返回特定片段。这样一来Agent 既能掌控大局又能在需要时聚焦细节上下文窗口的压力明显缓解。5.3 MCP Server 的进程生命周期问题MCP Server 跑时间长了之后我遇到过几次 Trae 调用工具时一直转圈不返回的情况。排查后发现是 Server 里某个工具执行了死循环直接把进程拖死了。MCP 的 stdio 模式下Trae 通过标准输入输出和 Server 通信一旦进程崩溃整个会话就断了。解决办法有几个。一是在每个工具函数里加上超时控制用Promise.race包裹耗时操作超过 30 秒就返回超时错误避免工具无限执行。二是在进程崩溃后让 Trae 自动重启 MCP Server可以在 Trae 的 MCP 配置里打开自动重连。三是用winston之类的库把 MCP Server 的日志写到文件里排查问题时先看日志而不是干瞪眼。5.4 浏览器环境中的干扰因素使用 playwright 加载目标脚本时有一个问题反复出现某些页面代码会检查当前浏览器是否处于自动化控制状态检测到就故意不执行核心逻辑导致exec_dynamic拿不到预期结果。这不是绕什么系统只是在调试自己页面时一些页面会根据运行环境改变行为影响分析稳定性。我的做法是尽量用有头模式运行浏览器因为自动化痕迹没那么明显必要时安装playwright-extra和 stealth 插件来降低自动化特征。但这里要强调这些都只是调试环境的稳定性方案不应该用于绕过访问控制或内容保护机制。如果你分析的是自己有权限的代码多半不需要处理这种检测如果你分析的是没有权限的代码那这个方向本身就有合规问题强烈建议停止。5.5 常见问题速查表问题现象解决方案Agent 结论与工具输出不一致报告里出现源码中不存在的代码片段增加“必须引用工具输出作为证据”的 Prompt 约束上下文窗口被撑爆分析到一半质量下降甚至停止响应工具增加输出行数截断按需分段返回MCP Server 卡死工具调用长时间不返回给工具加超时控制开启 Trae 自动重连浏览器执行结果为空exec_dynamic返回空日志换有头模式或引入 stealth 插件优化环境AST 还原报错escodegen生成代码时报语法错误检查 AST 是否被修改完整必要时退回原始 AST6. 这套智能体的边界与扩展方向6.1 它能自动化的是“体力活”不是“判断力”用了这个智能体一段时间后我最大的感受是它帮我省掉了大量机械性操作但判断力这件事还得靠人。比如“这个混淆函数是不是加密入口”“这段动态生成的代码是否与服务端行为相关”这些语感式的判断AI 目前还做得不够好。我的用法是把智能体当成一个效率放大器它负责快速拉出技术事实我负责结合业务背景做取舍。每次拿到报告后我仍然会挑几个关键片段人工复核一是为了防止幻觉二是为了确保分析方向没有跑偏。随着使用时间变长我会把一些固定的分析结论沉淀成模板逐步减少人工复核的比例但永远保留人工确认这一步。6.2 横向扩展代码资产审计与混淆风险评估这套架构除了做“拿到一份代码分析它”的临时任务还能扩展成一种持续运行的代码资产管理工具。你可以写一个定时脚本每天扫描你的前端工程目录用detect_obfuscation检查哪些文件的混淆强度过高如果发现可疑的eval或new Function调用就自动生成告警。对于维护大型前端应用、特别是使用了大量第三方 SDK 的团队来说这个能力可以直接用来做安全审计兜底。还有一个很实用的方向是回归对比。在升级某个 SDK 之前把旧版和新版分别跑一遍智能体分析流程比对两者的函数调用链和核心逻辑差异从技术层面快速发现行为变化。这个方法比人肉 diff 混淆后的代码高效得多我在一次三方 SDK 升级时靠它提前发现了一个被隐藏的埋点逻辑变更。6.3 合规边界务必只处理你有权分析的代码写到这里必须再强调一次合规问题。JS 逆向这个技术本身是中性的但使用场景一定要严格界定。我所有的示例和实操都基于自己生成的代码、开源项目或自己有权限分析的软件。如果你用这套能力去分析付费商业软件的混淆逻辑、绕过高版本功能的访问控制、或者处理不属于你的系统的签名算法那不仅违反软件许可协议还可能触及法律红线。一个很实用的判断标准是如果这段代码所属权限不在你手里就不要做。做安全研究要找经过授权的漏洞报告渠道做 CTF 题目要用 CTF 平台提供的靶场做 SDK 分析要保证你拥有该组件的合法使用权。这样既保护了自己也让逆向这门手艺能持续良性发展。6.4 最后分享一个实用小技巧把每次分析的有效结论沉淀成 MCP Resource。MCP 协议不仅支持工具调用还支持资源暴露你可以把历次分析报告、常见混淆特征库、AST 还原规则都注册成 Resource。这样 Agent 在处理新任务时先读取历史资源作为经验参考再开始新分析效果比每次从零开始要好得多。这相当于给你的智能体装了一个“记忆库”让它越用越顺手。