你要是被一段动态混淆的 JS 逼到周五晚上还在点心点上怀疑人生大概率能理解我为什么要搭这个智能体。这里说的“逆向”不是灰产黑产的活儿而是很朴素的一件事线上报错堆栈里全是_0x...符号脚本到底在干什么以前我靠手工跟栈后来我把 Trae 当大脑、用 MCP 给它接上一套 JS 分析工具让 AI 来自动拆解还原。这篇文章就是搭这套东西的完整记录适合做前端安全分析、混过代码维护、历史遗留代码审计的同学参考。先声明一句下文所有样本都来自我自己的测试工程或者拿到了明确授权的代码审计场景。1. 先搞清楚动态混淆到底在混淆什么1.1 混过之后的代码为什么机器跑得动人读不懂动态混淆不是简单的压缩打包它的核心是破坏“人类可读的语义层”同时保证“机器可执行的行为层”不变。最常见的几类手段基本是组合拳一起上。第一标识符替换。所有变量名、函数名、属性名被替换成_0x1b2c、_0x3f4a这类无意义编号遇到重名冲突就加一层自执行函数隔离。第二字符串加密。源码里所有字符串常量被集中到一个大数组里运行时通过一个解码函数按下标取出有的还会加移位运算、RC4/AES 变体。第三控制流平坦化。一个完整函数被改造成一个while(true) switch(state)的形态真实逻辑被拆散到各个case分支里分支之间靠状态变量跳转人眼顺着代码读根本读不出“从哪开始、到哪结束”。第四死代码注入和 Opaque Predicate混入大量永远不执行或恒真恒假的条件分支增加分析噪音。第五反调试与环境监测检测 DevTools 是否打开、检测navigator.webdriver、检查堆栈调用来源一旦发现异常立刻走乱逻辑甚至死循环。这套组合拳下来代码确实还能跑因为运行时 JavaScript 引擎会老老实实把解密函数执行一遍、把分发器按状态变量一步步推进。但人眼去看源码就像是拿到一本被撕成上千张纸条的故事书每张纸条上只有半句话还额外混了几百张其他书的纸条。机器有规则可以重新排序人是真的看不下去。1.2 为什么“手工跟栈”这条路越来越走不通前几年遇到混淆代码我的第一反应是打开 DevTools在可疑入口下断点然后一层一层跟调用栈。这个方法没到完全不能用但越来越费劲核心原因有三点。一是动态语言特性太刁钻。JS 里大量存在eval、new Function、闭包内变量共享、Promise 异步链你断点打下去下一步执行流可能已经跳到几十层闭包之外而且栈帧里的变量名全无意义根本不知道当前在算什么。二是混淆工具在对抗式升级。字符串数组加解码函数还不够有些工具会把数组下标做二次变换把_0x123[0x45]变成_0x123[_0x456(0x1)]你刚还原一层里面又套一层。三是时间成本完全不可控。我统计过自己的工单一个中等规模的混淆样本纯手工跟栈平均要 4 到 8 小时如果中间遇到反调试重新启动浏览器、重新绕过检测再加 2 小时起。还有一点很关键反混淆是“先理解再改代码”的活但人在长时间盯着混淆代码后注意力会严重下降。我踩过不止一次坑把明明该换成字符串字面量的节点替换错位置导致后续分析方向全错。手工跟栈不是不能干而是产出质量不稳定这就是我想用智能体替代“体力部分”的直接原因。2. 为什么是 Trae MCP不是我选了方案是方案替我做了选择2.1 先说 MCPAI 的“USB-C”接口MCP 全称是 Model Context Protocol模型上下文协议。你可以把它理解成给 AI 配了一个标准化的“外设接口”之前 AI 只能聊天、写代码但现在通过这个协议AI 可以调用外部工具、读取外部资源、获取系统提示词就像电脑插上 USB-C 就能接显示器、键鼠、硬盘一样。MCP 定了一套基于 JSON-RPC 2.0 的通信规范底层走 stdio 或 SSE/HTTP工具方实现一个 ServerAI 客户端通过协议去发现和调用这些 tools。我最早接触 MCP 时也犯过嘀咕觉得“这不就是函数调用吗”。后来真想明白了函数调用是给模型预设定死的几个函数MCP 是把“让模型动态发现工具清单并调用”这件事标准化了。你的 Agent 今天只有一个分析工具明天想加一个浏览器自动化工具只需要再注册一个新的 MCP Server模型不用改客户端不用改。这个“可插拔”属性对做 JS 分析这种工具链极长的场景非常关键。2.2 Trae 的 Agent 编排能力解决了什么Trae 是字节跳动推出的 AI IDE国内版访问 trae.cn。其实我用过的 AI 编程工具不少选 Trae 的原因不是它代码生成最强而是它的 Agent 模式比较适合“多步任务编排”。普通的 AI 补全是“你问我答”但逆向分析本质上是一个流水线任务先摸底、再还原、再验证、再解释每一步的输出都是下一步的输入。Trae 的 Agent 模式可以一次性接收“帮我把这段混淆代码还原成可读逻辑并对比输出是否一致”这种复合指令然后自己拆解、调工具、检查中间结果、再修正。更实用的是它对工作区的感知能力。它会自动读取项目文件结构、读取.ast/目录下的中间产物、修改文件、生成报告。这个过程其实是“记忆外置”AI 的上下文窗口虽然长但也不能无限装代码让工具把中间产物写进工作区文件Agent 按需读取比把几万行混淆代码全塞进对话里靠谱得多。2.3 整体架构我的“三件套”设计我搭的这套智能体严格说是三部分协作Trae 负责编排和推理自建 MCP Server 负责提供 JS 分析能力本地工作区负责存放中间产物。整个过程大概是这样我输入指令Trae 的 Agent 先分析意图调用我注册在 MCP Server 里的analyze_snippet工具做第一轮摸底拿到 AST 摘要后Agent 判断是否需要深度还原调用run_deobfuscate_pipeline跑反混淆流程还原出来的中间代码会被写到工作区workspace/report/下最后如果需要动态验证再调用browser_run工具在隔离浏览器里执行一遍比对输出。这个设计里 MCP Server 不是一个大而全的“分析器”而是被拆成多个小工具每个工具只做一件事输出结果要做“摘要化”避免把原始 AST 全量 JSON 砸给模型。你想想一个几千行的 JS 文件AST JSON 动辄几百 KB别说模型处理不了通信都要超时。所以每个工具的输出都必须是面向“模型可消费”的精炼信息而不是面向“人类可阅读”的完整数据。这一条是我搭完第一版后踩坑踩出来的后面细说。3. 实操从零搭一个 JS 反混淆智能体3.1 第一步准备好基础环境你需要准备的东西不多但版本要注意。Node.js 20 以上用来跑反混淆管线和 Babel 相关工具Python 3.11 以上用来跑 MCP Server 和 Playwright 控制脚本Trae 客户端装最新版本确保 Agent 模式可用最后是 Playwright 的浏览器内核建议单独安装 Chromium避免和系统浏览器版本冲突。我建议把反混淆工具链和 MCP Server 分两个目录维护我自己的目录结构是这样js-analyzer/ server/ server.py # MCP Server 入口 tools/ analyze.py # AST 摘要工具 deobfuscator.py # 反混淆流水线 sandbox.py # 隔离执行工具 env_patch.py # 环境消毒器 pipeline/ parse.js # Babel 解析封装 string_decrypt.js # 字符串解密替换 flat_restore.js # 控制流平坦化还原 workspace/ samples/ # 待分析样本 report/ # 中间产物与报告这样划分的好处是 MCP Server 只做“工具暴露”不关心具体算法反混淆的真实逻辑全在pipeline/里可以单独用命令行调试等调稳了再接进来。如果你一开始就把算法逻辑全写进 MCP Server后面每次改算法都要重启 Server非常痛苦。3.2 第二步写一个自建 MCP Server我用的是官方 Python SDK 里的 FastMCP 封装比手写 JSON-RPC 处理省太多事。核心代码其实很短注册三个工具就够起步from mcp.server.fastmcp import FastMCP mcp FastMCP(js-analyzer) mcp.tool() def analyze_snippet(code: str) - str: 解析JS片段返回结构化摘要函数列表、可疑混淆特征、变量名统计。 # 内部调用 babel/parser 解析再做特征扫描 return summarize_ast(code) mcp.tool() def run_deobfuscate_pipeline(code: str, stage: str all) - str: 执行反混淆流水线string_decrypt / flat_restore / rename / diff_verify。 # 阶段可选返回中间代码和对比结果 return run_pipeline(code, stage) mcp.tool() def browser_run(entry_file: str, context: dict {}) - str: 在Playwright隔离环境执行JS返回console输出与返回值用于差分验证。 # 使用独立的浏览器上下文记录执行日志 return run_in_browser(entry_file, context) if __name__ __main__: mcp.run()别看代码短工具设计才是关键。我一开始工具只设了一个“全自动分析”但模型调用时经常不知道该传什么参数、返回结果也五花八门。后来改成“小工具 明确入参出参”每个工具的 description 都写清楚“什么时候用、传什么、返回什么”模型的调用准确率明显提升。特别是stage参数的设计模型可以只跑某一个阶段比如先只做字符串解密拿到结果后再决定要不要跑控制流平坦化而不是一上来就把所有变换全做了。3.3 第三步把 Server 挂进 Trae配置 MCP Server 的方式取决于 Trae 的版本我当前的客户端支持读取项目下的mcp.json文件。格式大概是下面这样{ mcpServers: { js-analyzer: { command: python, args: [-m, server.server], env: {} } } }注意command一定得是全局可用的启动命令args里的路径要基于server/目录的 Python 模块路径。保存后回到 Trae在 Agent 面板里应该能看到js-analyzer这个 Server 显示已连接。验证方式很简单直接对 Agent 说“列出你当前可用的工具”它会告诉你analyze_snippet、run_deobfuscate_pipeline、browser_run已经就绪。一个容易被忽略的坑Trae 启动 MCP Server 时用的环境变量和终端里不一样。如果python命令在终端能用但 Server 连不上多半是 PATH 问题。我后来直接在env里写死了 Python 解释器的绝对路径和PYTHONPATH连接稳定多了。3.4 第四步反混淆管线的几个关键实现细节搭框架容易真正花时间的都在pipeline/里。我挑三个核心模块讲讲。第一个是字符串解密。思路很直接先定位字符串数组的定义和取值函数然后在受限沙箱里把解密函数执行一遍生成“调用表达式 - 明文”的映射表最后用 Babel 遍历 AST把所有符合模式的调用节点替换成字符串字面量。关键点在于“受限沙箱”要处理好依赖有些解密函数会引用外部变量比如数组本身是全局的你要把整个作用域链一起模拟出来。我写了一个简化版本const parser require(babel/parser); const traverse require(babel/traverse).default; const generate require(babel/generator).default; // 1. 找出数组定义和解密函数 // 2. 在 Node 里构造近似运行环境 // 3. 生成 map: 节点原始文本 - 明文 // 4. 替换 AST 中的 CallExpression traverse(ast, { CallExpression(path) { const original generate(path.node).code; if (strMap.has(original)) { path.replaceWith(t.stringLiteral(strMap.get(original))); } } });第二个是控制流平坦化还原。这一块没有银弹不同混淆器生成的分发器结构不一样但核心规律相同都会有一个主导的分发变量、一个while/switch主循环、以及每个 case 末尾对分发变量的重新赋值。我的做法是先识别出主循环节点把每个 case 里的操作块完整保留然后根据“状态变量新值”的关系构建一个转移图最后把“分支跳转”展开成顺序逻辑。注意不要试图把整个混淆函数还原成和原始代码一模一样那是几乎不可能完成的任务能还原出可读的执行顺序就足够分析签名逻辑了。第三个是环境消毒器。动态执行混淆代码时最烦人的不是逻辑复杂而是反调试和环境检测。我的sandbox.py里会在执行前注入一堆 mock 对象给window补上chrome对象、删掉navigator.webdriver属性、改写Function.prototype.toString返回一个安全结果、把setInterval和requestAnimationFrame拦截掉防止死循环。这样 80% 的混淆样本能在一个“看起来像浏览器、实际是沙箱”的环境里正常跑起来。3.5 实战样本一段“签名计算”的还原过程说一个真实的还原过程样本来自我自己的测试工程是一段被压平的签名计算逻辑。原始文件大概两千行所有函数名都是_0x开头字符串用五组大数组加两个解码函数管理主计算逻辑被改成了while switch(0x1a2b)的分发器形态。传统做法里我要先通过接口调用找到签名入口然后在 DevTools 里打断点一帧一帧看状态变量怎么跳。这次我完全让智能体来走流程。第一轮我向 Trae 的 Agent 发出指令“分析samples/login_sign.js先告诉我这个文件有哪些可疑特征。”Agent 调用analyze_snippet返回结果里标出“字符串数组 5 组、解码函数 2 个、疑似控制流平坦化函数 1 个、反调试调用 2 处”。这个摸底大概十几秒就出来了。第二轮我让 Agent 执行run_deobfuscate_pipeline的string_decrypt阶段。工具返回了映射表片段和替换后的代码块。Agent 看完后自动总结“签名函数中_0x4f2a(0x2c)实际指向字符串sha256_0x4f2a(0x5d)指向timestamp。”到了这一步字符串层的迷雾基本消除了。第三轮我再让它跑flat_restore把那个两千行的分发器函数还原成顺序逻辑。还原后发现真实计算过程其实是取timestamp、拼上固定 salt、调sha256得到摘要、再拼上nonce生成签名。这里的顺序逻辑用眼睛已经能读懂了。最后一步是差分验证。我让 Agent 调用browser_run分别执行原始文件和还原后生成的“逻辑复刻文件”传入同样的输入参数比对输出结果是否一致。一致通过说明还原逻辑没跑偏。整个过程大概 40 分钟中间还有几轮问答修正但我的参与基本是“提需求、看报告”没有手动跟过一处堆栈。这套流程跑通之后我对“智能体能干活”这件事彻底改观了。4. 踩坑记录这些问题我都是一个个试出来的4.1 MCP Server 反复连接失败第一个遇到的大坑是 Trae 一直显示js-analyzer未连接。排查后发现有三种情况第一种是python命令在 Trae 的启动环境里 PATH 不对解决方式是在env里写死解释器绝对路径第二种是 Server 启动时输出了一些日志到 stdout污染了 MCP 的 JSON-RPC 通信流解决方式是把所有日志都导向 stderr 或文件第三种是args里的模块路径写错python -m server.server的启动目录必须是项目根目录不是工具目录。强烈建议第一次配置时先在终端手动执行一遍启动命令看能不能正常输出{jsonrpc:...}握手信息。4.2 AST 全量 JSON 直接把上下文撑爆我第一版工具的输出是“完整 AST JSON”第一次跑一个两千行样本直接输出了几百 KB 内容Trae 的上下文窗口一下子被打满后面的对话基本没法用了。后来我把所有工具的输出改成“摘要模式”只返回函数列表、可疑节点位置、变量名统计、字符串映射表的抽样片段。模型接受到的信息量少了一个数量级但决策质量反而更高因为它不用在噪音里翻找关键点了。4.3 解密函数不是纯函数字符串解密阶段最大的意外是某些解密函数不是纯计算它依赖自由变量、甚至依赖Math.random()或时间戳。这就导致你在沙箱里单独执行解密函数结果可能和原运行环境不一致。我的处理方式是分阶段降级能模拟执行的就模拟执行模拟不了的就直接看代码逻辑把解密函数本身用人工方式解析出来再手工查映射。这个过程不能贪快加密方式多变的样本必须留出人工兜底的口子。4.4 反调试把浏览器运行环境搞崩动态执行时最怕反调试有些样本会主动检测navigator.webdriver检测到之后在后续逻辑里故意制造死循环或者抛出异常。我一开始直接用普通浏览器执行结果页面直接卡死。后来老老实实写环境消毒器并且每次执行都放到一个全新的 Playwright 上下文里跑完就销毁避免状态残留。还有一个技巧执行前先静态扫描代码里的toString检测和constructor访问点提前 patch比运行时被动拦截更稳。4.5 “AI 过度自信”问题这是我在语义还原阶段最警惕的问题。模型在解释一段还原代码时有时会给出一种逻辑自洽但实际错误的解释偏偏表达得非常肯定。我的对策是引入“强制差分验证”任何自动还原的代码必须有“原执行输出 vs 还原执行输出”的对比记录不一致的情况下AI 的解释一律标记为“待验证”。我还在 MCP Server 里加了一个diff_verify阶段工具只输出“通过/不通过”和差异片段不输出模型拟合的解释。这样即便模型产生幻觉也不会被当成结论用。下面把我遇到的高频问题整理成一张速查表问题现象可能原因解决方式MCP Server 未连接PATH 不对 / stdout 被污染写死解释器路径日志转文件工具返回内容过大输出全量 AST改成摘要模式解密结果与线上不符解密函数非纯函数降级人工解析保留映射表动态执行卡死反调试触发环境消毒器 全新浏览器上下文AI 解释错误但很笃定幻觉强制差分验证 标记待验证5. 这套智能体的边界以及我现在的用法搭好这套东西之后我现在的日常工作节奏变化很明显。遇到混淆样本先让智能体跑一遍摸底再针对性地看它生成的中间产物最后只在“语义理解”和“方案确认”这两步亲自上手。它把 80% 的机械劳动消化掉了剩下 20% 的专家判断仍然需要人来完成。你要接受一个现实动态混淆的还原没有 100% 全自动的可能至少现阶段没有。工具的价值是把你的时间从“体力跟栈”换到“决策审查”上。我也在考虑扩展它的能力边界比如加一个“对抗样本”工具自动生成不同混淆配置的测试集用来检验还原链路的覆盖率或者在 MCP Server 里挂一个开源漏洞库查询工具让 Agent 在还原代码后能自动关联已知风险模式。但不管怎么扩展有一条原则我不会变所有自动化分析都必须在授权和合规的范围内进行我只对自有代码资产和明确授权的审计目标做这类还原。技术能力是用来提升效率的不是用来突破边界的。最后分享一个我现在仍然保留的小习惯每次还原结束后我会把“原始输入样本、中间产物、最终报告、差分验证记录”打包存成一个带日期的目录。这个习惯帮我建立了自己的分析语料库后来再遇到新的混淆变种直接拿同类样本的还原路径去参考比从零开始快得多。如果你也想搭这套东西我建议不要一开始追求工具多先把我上面说的三个小工具跑通拿一个真实样本走完整流程感受到“从全手动变成半自动”的差别你就知道下一步该往哪个方向优化了。
阅读完成 · 觉得有帮助?