ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI 赋能 JS 逆向:MCP+Skill+autoDecoder 全自动化落地加密自动破解|TaoToken 统一 Key 接入实战

AI 赋能 JS 逆向:MCP+Skill+autoDecoder 全自动化落地加密自动破解|TaoToken 统一 Key 接入实战 1. 从抓包到自动解密JS 逆向加密参数自动破解的真实痛点做前端安全测试的同学大概率都遇到过这种场景登录接口的password字段不是明文sign参数每次请求都在变token里塞了一堆时间戳和随机数。你打开 DevTools在 Sources 面板里翻了几十个混淆过的文件好不容易定位到一个$.md5(...)结果发现外面还套了两层encrypt再往下追发现是动态生成的函数名。人工逆向的链路就是这样定位入口靠经验还原逻辑靠耐心写注册代码靠手速任何一环卡住整个流程就得推倒重来。我试过纯手工的 JSRPC 方案浏览器里注入Hlclient本地起 Flask 做转发Burp 挂 autoDecoder 插件理论上能跑通。但问题在于定位加密函数、写注入代码、写 Flask 代理、配 autoDecoder 规则这四步每一步都要手动完成而且换个站点就得重来一遍。真正消耗时间的不是加密算法本身而是这些重复的胶水代码。这篇要讲的落地链路核心思路是把「定位入口 → 生成注入代码 → 构建代理 → 配置 Burp」这四个环节交给 AI 来编排。具体做法是用 MCPModel Context Protocol把 Chrome DevTools 的能力暴露给 AI 客户端再用 Skill 规范约束 AI 的输出格式让它按模板生成 JSRPC 注入代码和 Flask 代理最后通过 autoDecoder 把加密参数的回填自动化。整个过程中人只需要提供目标 URL 和要分析的参数名剩下的代码生成和配置由 AI 完成。适合谁看有 Burp Suite 使用基础、了解 JSRPC 基本概念、想把这套流程从「半自动」推到「高自动」的安全测试人员。如果你完全没接触过 JSRPC建议先理解「浏览器里注册函数、本地通过 WebSocket 调用」这个基本模型再往下看配置部分会顺畅很多。需要提前说明的是本文涉及的所有技术手段仅用于授权的安全测试和学习交流请遵守《中华人民共和国网络安全法》等相关法律法规不要对未授权的目标使用。2. TaoToken 统一 Key 接入给 MCP 调用链配一个稳定的凭据通道这套方案里有一个容易被忽略但很关键的问题AI 客户端要调用 MCP 服务MCP 服务要调用浏览器浏览器里的 JSRPC 要连本地 FlaskFlask 要调 JSRPC 的 HTTP 接口。整条链路上AI 侧的模型调用需要 API Key而不同工具、不同客户端对 Key 的管理方式各不相同。如果你同时用 Codex、Cline、Claude Code 这几个工具每个都要单独配 Key、单独管额度切换的时候还得改配置文件很容易出现「这个工具能用、那个工具报 401」的情况。TaoToken 在这里的角色是统一凭据通道。它提供一个兼容 OpenAI 风格的 API 入口你可以把同一个 Key 配到不同的 AI 客户端里MCP 相关的模型调用都走这个通道。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置的时候直接用这个。具体到这套 JS 逆向方案TaoToken 主要用在两个地方。第一是 AI 客户端的模型调用比如 Codex 在执行 Skill 流程时需要调用模型来生成 JSRPC 代码和 Flask 代理这个调用走 TaoToken 的 API 通道。第二是如果你用 Cline 或 Claude Code 做辅助分析这些工具的 Base URL 也统一指向 TaoToken避免多个 Key 混用导致的额度分散和鉴权混乱。配置方式上以 Codex 为例它的配置文件在~/.codex/config.toml你需要在这个文件里同时配置 MCP 服务器和模型 API 通道。MCP 部分后面会详细讲这里先说 API 通道的配置逻辑把base_url指向 TaoToken 的 API 地址api_key填你在控制台生成的 Keymodel填你要用的模型 ID。这样 Codex 在调用模型时就走 TaoToken 的通道而不是直连其他服务。如果你用的是 Cline 这类 VS Code 插件配置入口在插件的设置面板里同样是三个字段Base URL、API Key、Model ID。Base URL 填https://taotoken.net/apiKey 从控制台获取Model ID 根据你实际使用的模型填写。Cline 的 MCP 配置是在cline_mcp_settings.json里这个文件的位置取决于你的系统macOS 一般在~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/下面。这里有一个实操中容易踩的坑MCP 服务和模型 API 是两条独立的通道MCP 服务本身不需要 TaoToken 的 Key它走的是本地 stdio 或 SSE 通信。TaoToken 的 Key 只用于模型调用。所以你在配置的时候不要把 Key 填到 MCP 服务器的配置里那样不会生效反而会让 MCP 启动失败。另外如果你需要长期跑这套自动化流程建议用 Coding Plan 来管理额度而不是按次调用。因为一次完整的 JS 逆向分析可能涉及多轮模型调用定位入口一轮、生成 JSRPC 代码一轮、生成 Flask 代理一轮、排错可能还要几轮。按次计费的话额度消耗不好预估Coding Plan 更适合这种连续编码场景。控制台地址是 https://taotoken.net/console API Keys 管理在 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 。3. 可复制配置MCP 服务器、Skill 定义与 autoDecoder 规则文件这一节给出完整的配置文件你可以直接复制到对应路径下使用。配置分三块Codex 的 MCP 服务器配置、Skill 的三个 Markdown 文件、以及 autoDecoder 的规则说明。3.1 Codex MCP 服务器配置Codex 的配置文件在~/.codex/config.toml如果你之前没有这个文件手动创建即可。MCP 服务器的配置用 TOML 格式写注意command和args的写法[mcp_servers.chrome-devtools] command npx args [-y, chrome-devtools-mcplatest]如果你用的是 Windowscommand可能需要写成npx.cmd或者用完整路径。配置完成后用codex mcp list检查是否生效看到chrome-devtools出现在列表里就说明配置正确。启动 MCP 服务之前需要先以调试模式启动 Chrome并且指定一个独立的用户数据目录避免和日常使用的 Chrome 实例冲突/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \ --remote-debugging-port9222 \ --remote-debugging-address0.0.0.0 \ --user-data-dir/tmp/chrome-debug-profileWindows 下的命令类似把 Chrome 的路径换成C:\Program Files\Google\Chrome\Application\chrome.exe参数保持一致。启动后访问http://127.0.0.1:9222/json/version能看到版本信息就说明调试端口已经打开。3.2 Skill 定义文件Skill 的作用是约束 AI 的输出格式让它按你定义的模板生成代码而不是自由发挥。这套方案需要三个 Markdown 文件放在 Codex 能读取到的 skills 目录下。主技能文件js-reverse-automation.md负责整体流程控制内容如下--- name: js-reverse-automation description: 通过 MCP 连接浏览器为目标自动化搭建 JS 环境定位签名/加密函数入口生成 JSRPC 代码创建 Flask 代理并输出 Burp autoDecoder 配置。 --- # JS 逆向自动化技能 ## 补充模板引用 使用时先阅读本主技能再按需加载补充模板JSRPC.md、flask.md、burp-autodecoder.md。 ## 需要收集的输入 - 目标 URL 或功能需求 - 需要分析的加密参数名 - Fetch 请求示例可选 ## 需要交付的输出 1. JSRPC 注入代码 2. Flask 代理服务 3. Burp autoDecoder 配置说明 4. 完整的加密/签名逻辑代码 5. 验证记录 ## 工作流程 ### 阶段一初始化与连接 通过 MCP 连接浏览器加载目标页面。 ### 阶段二分析与入口定位 触发签名流程抓取调用栈定位加密函数。 ### 阶段三生成与注入 JSRPC 基于模板生成注入代码注册 action验证调用。 ### 阶段四构建服务端代理 生成 Flask 代理启动服务并做健康检查。 ### 阶段五集成与交付 输出 autoDecoder 配置端到端验证。JSRPC 注入模板JSRPC.md提供通用的函数拦截和远程调用代码--- name: jsrpc-injection-template description: 提供通用 JSRPC 注入模板用于定位签名/加密函数入口并对外注册 action。 --- # JSRPC 注入工具模板 ## 代码模板 js var client new Hlclient(ws://127.0.0.1:12080/ws?groupfaustonameburp); var JSRPC_CONFIG { actionName: generate_sign, entry: { type: global, path: signFunction }, bindThis: null, async: false, normalizeInput: function(param) { return param; }, normalizeOutput: function(result) { return result; }, onError: function(err) { return ERROR_ Date.now(); } }; function getByPath(root, path) { if (!root || !path) return null; var parts path.split(.); var cur root; for (var i 0; i parts.length; i) { cur cur[parts[i]]; if (!cur) return null; } return cur; } function resolveEntry(config) { if (config.entry.type global) return window[config.entry.path]; if (config.entry.type object) return getByPath(window, config.entry.path); if (config.entry.type resolver typeof config.entry.resolver function) { return config.entry.resolver(); } return null; } client.regAction(JSRPC_CONFIG.actionName, function(resolve, param) { try { var fn resolveEntry(JSRPC_CONFIG); if (typeof fn ! function) throw new Error(签名/加密函数未找到); var input JSRPC_CONFIG.normalizeInput(param); var ctx JSRPC_CONFIG.bindThis || null; var result fn.call(ctx, input); if (JSRPC_CONFIG.async result typeof result.then function) { result.then(function(res) { resolve(JSRPC_CONFIG.normalizeOutput(res)); }).catch(function(err) { resolve(JSRPC_CONFIG.onError(err)); }); return; } resolve(JSRPC_CONFIG.normalizeOutput(result)); } catch (error) { resolve(JSRPC_CONFIG.onError(error)); } });Flask 代理模板 flask.md 负责连接 JSRPC 和 Burp markdown --- name: flask-jsrpc-proxy-template description: 面向 Burp autoDecoder 的 Flask 代理服务模板。 --- # Flask JSRPC 代理工具模板 ## 代码模板 python from flask import Flask, request import requests import json import logging logging.basicConfig(levellogging.DEBUG) app Flask(__name__) JSRPC_URL http://127.0.0.1:12080/go JSRPC_GROUP fausto JSRPC_ACTION generate_sign SIGN_FIELD sign TIMEOUT 5 app.route(/encode, methods[POST]) def handle_encode(): data_body request.form.get(dataBody, ) data_headers request.form.get(dataHeaders, ) if not data_body: return data_headers \r\n\r\n\r\n\r\n data_body if data_headers else data_body try: json_data json.loads(data_body) except json.JSONDecodeError: return data_headers \r\n\r\n\r\n\r\n data_body if data_headers else data_body old_sign json_data.pop(SIGN_FIELD, None) params_for_jsrpc { group: JSRPC_GROUP, action: JSRPC_ACTION, param: json.dumps(json_data, ensure_asciiFalse, separators(,, :)) } try: jsrpc_response requests.get(JSRPC_URL, paramsparams_for_jsrpc, timeoutTIMEOUT) jsrpc_response.raise_for_status() result jsrpc_response.json() new_sign result.get(data, ) except Exception as e: app.logger.error(fJSRPC 调用失败: {e}) new_sign json_data[SIGN_FIELD] new_sign new_data_body json.dumps(json_data, ensure_asciiFalse, separators(,, :)) if data_headers: new_headers data_headers.replace( fContent-Length: {len(data_body)}, fContent-Length: {len(new_data_body)} ) return new_headers \r\n\r\n\r\n\r\n new_data_body return new_data_body if __name__ __main__: app.run(host0.0.0.0, port8888, debugTrue)autoDecoder 配置说明 burp-autodecoder.md 给出 Burp 插件的参数 markdown --- name: burp-autodecoder-config description: Burp autoDecoder 插件配置说明。 --- # Burp autoDecoder 配置 ## 接口配置 - 接口地址http://127.0.0.1:8888/encode - 请求方式POST - 参数名dataBody、dataHeaders ## 加密字段 - 字段名sign根据实际目标调整 - 加密方式JSRPC 远程调用 ## 注意事项 - Flask 服务需先启动 - JSRPC 注入代码需在浏览器控制台执行 - Content-Length 会自动更新3.3 autoDecoder 规则文件autoDecoder 的配置在 Burp 插件界面里完成核心是设置接口地址和参数名。接口地址填http://127.0.0.1:8888/encode请求方式选 POST参数名填dataBody和dataHeaders。加密字段根据目标站点调整比如password或sign。如果你需要把配置持久化autoDecoder 支持导出和导入配置文件格式是 JSON。一个典型的配置片段如下{ interfaceUrl: http://127.0.0.1:8888/encode, method: POST, bodyParam: dataBody, headerParam: dataHeaders, encryptFields: [password], enabled: true }这个 JSON 可以直接在 autoDecoder 的配置界面导入省去手动填写的步骤。4. 验证请求从抓包到自动解密的完整动作配置完成后需要跑一次完整的验证流程确认从抓包到自动解密整条链路是通的。这里用一个登录接口的password字段加密作为例子演示每一步的操作和预期结果。4.1 启动 JSRPC 服务JSRPC 的服务端是一个独立的可执行文件启动后监听12080端口。启动命令根据你的系统选择对应的二进制文件./jsrpc-server -port 12080启动后看到WebSocket server listening on :12080就说明服务已经就绪。这个服务负责接收浏览器端的 WebSocket 连接和本地的 HTTP 调用请求。4.2 在浏览器中注入 JSRPC 客户端打开目标页面按 F12 进入 DevTools切换到 Console 面板。首先需要注入 JSRPC 的客户端库JsEnv_Dev.js这个文件的内容直接粘贴到 Console 里执行。执行成功后页面上下文里会多出一个Hlclient对象。接着注入 AI 生成的 JSRPC 注入代码。以password字段的 MD5 加密为例AI 生成的代码大致如下var client new Hlclient(ws://127.0.0.1:12080/ws?groupfaustonameburp); var JSRPC_CONFIG { actionName: generate_password_md5, entry: { type: resolver, resolver: function() { if (window.$ typeof $.md5 function) return $.md5; if (window.jQuery typeof jQuery.md5 function) return jQuery.md5; return null; } }, bindThis: null, async: false, normalizeInput: function(param) { if (param typeof param object) { if (param.password ! null) return String(param.password); if (param.pwd ! null) return String(param.pwd); } return String(param null ? : param); }, normalizeOutput: function(result) { return result; }, onError: function(err) { return ERROR_ Date.now(); } }; function resolveEntry(config) { if (config.entry.type resolver typeof config.entry.resolver function) { return config.entry.resolver(); } return null; } client.regAction(JSRPC_CONFIG.actionName, function(resolve, param) { try { var fn resolveEntry(JSRPC_CONFIG); if (typeof fn ! function) throw new Error(md5 function not found); var input JSRPC_CONFIG.normalizeInput(param); var result fn.call(JSRPC_CONFIG.bindThis || null, input); resolve(JSRPC_CONFIG.normalizeOutput(result)); } catch (error) { resolve(JSRPC_CONFIG.onError(error)); } });这段代码的关键是entry.type设为resolver因为目标站点的 MD5 函数可能挂在$或jQuery上用 resolver 动态查找比写死路径更稳。注入后在 Console 里执行$.md5(111111)应该返回96e79218965eb72c92a549dd5a330112这是 MD5 的标准结果。4.3 验证 JSRPC 调用在浏览器里注入成功后用 curl 测试 JSRPC 的 HTTP 接口是否正常curl http://127.0.0.1:12080/go?groupfaustoactiongenerate_password_md5param111111预期返回{data:96e79218965eb72c92a549dd5a330112,code:0}如果返回的data字段是 MD5 值说明 JSRPC 的注册和调用都正常。如果返回ERROR_开头的内容说明 resolver 没找到 MD5 函数需要检查页面是否加载了 jQuery 和 jquery.md5.js。4.4 启动 Flask 代理并测试Flask 代理的代码由 AI 根据模板生成针对password字段做了适配。启动命令python flask_proxy_password.py启动后监听8888端口。用 curl 模拟 autoDecoder 的请求curl -X POST http://127.0.0.1:8888/encode \ -H Content-Type: application/x-www-form-urlencoded \ --data-urlencode dataBodyusername111111password111111code1234role000002预期返回的password字段应该变成 MD5 值username111111password96e79218965eb72c92a549dd5a330112code1234role000002这一步验证的是 Flask 能否正确解析表单格式的请求体调用 JSRPC 拿到加密结果再回填到原始请求里。4.5 配置 Burp autoDecoder 并抓包验证打开 Burp Suite加载 autoDecoder 插件。在插件配置界面里接口地址填http://127.0.0.1:8888/encode请求方式选 POST参数名填dataBody和dataHeaders。加密字段填password。配置完成后在浏览器里触发一次登录请求Burp 拦截到请求后autoDecoder 会自动把password字段替换成加密后的值。你可以在 Burp 的 HTTP History 里看到原始请求的password111111被替换成了password96e79218965eb72c92a549dd5a330112而服务器返回的是登录成功的响应。整个链路验证通过浏览器注入 JSRPC → 本地 JSRPC 服务接收调用 → Flask 代理转发 → autoDecoder 回填加密参数 → 服务器正常响应。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth 报错这套方案涉及多个组件出错的时候报错信息往往指向不明确。下面列出几个高频错误和对应的排查方向。5.1 401 Unauthorized这个报错通常出现在 AI 客户端调用模型的时候。如果你在 Codex 或 Cline 里看到401首先检查 TaoToken 的 API Key 是否配置正确。Key 要填在模型 API 通道的配置里不是 MCP 服务器的配置里。其次检查 Base URL 是否写成了https://taotoken.net/api注意末尾不要多加斜杠也不要带 UTM 参数。如果 Key 确认没问题但还是 401可能是 Key 的额度用完了或者被禁用。登录控制台 https://taotoken.net/console 检查 Key 的状态和剩余额度。另外有些客户端会把 Key 缓存在本地修改配置后需要重启客户端才能生效。5.2 local proxy failed这个报错一般出现在 MCP 服务启动的时候。chrome-devtools-mcp需要通过 CDP 协议连接 Chrome如果 Chrome 没有以调试模式启动或者调试端口被占用就会报local proxy failed。排查步骤先确认 Chrome 是否带--remote-debugging-port9222参数启动。然后访问http://127.0.0.1:9222/json/version如果能返回 JSON 说明端口正常。如果返回连接拒绝说明 Chrome 没启动或者端口不对。另外如果你之前已经开了一个 Chrome 实例再启动一个带调试端口的实例会失败需要先完全退出 Chrome或者用--user-data-dir指定不同的用户数据目录。5.3 reading choices 报错这个报错通常出现在模型返回的内容格式不符合预期的时候。比如你让 AI 生成 JSRPC 代码但它返回了一段解释性文字而不是代码块客户端在解析choices字段时就会报错。解决办法是在 Skill 定义里明确要求输出格式。比如在js-reverse-automation.md里加上「所有代码必须放在js 或python 代码块里不要输出额外的解释文字」。如果已经报错了可以重新发起一次请求或者在提示词里强调「只输出代码不要解释」。5.4 OAuth 相关报错如果你用的是 Claude Code 或类似工具可能会遇到 OAuth 认证失败的问题。这类工具默认走 OAuth 流程获取访问凭据但如果你配置了自定义的 Base URLOAuth 流程可能不兼容。解决办法是改用 API Key 认证而不是 OAuth。在 Claude Code 的配置里把认证方式从 OAuth 切换成 API KeyBase URL 填 TaoToken 的地址Key 填控制台生成的 Key。具体配置位置在~/.claude/settings.json或项目的.claude/settings.json里字段名是apiKey和baseUrl。5.5 JSRPC 调用返回 ERROR_如果 JSRPC 的 HTTP 接口返回ERROR_开头的内容说明注入的 resolver 没找到目标函数。排查方向在浏览器 Console 里手动执行typeof $.md5如果是undefined说明页面没有加载 jquery.md5.js或者 MD5 函数挂在了别的对象上。这时候需要调整 resolver 的逻辑把可能的路径都列进去。另外如果目标函数需要this上下文比如crypto.sign这种对象方法需要在JSRPC_CONFIG.bindThis里指定window.crypto否则调用会失败。5.6 autoDecoder 不生效如果 Burp 抓到的请求里password字段还是明文说明 autoDecoder 没有正确拦截。检查几个点autoDecoder 插件是否启用接口地址是否填对加密字段名是否和目标请求里的字段名一致。另外autoDecoder 只对匹配的字段生效如果请求体是 JSON 格式但字段名写的是pwd而不是password就不会被替换。还有一个容易忽略的点Flask 代理的Content-Length更新逻辑。如果加密后的字符串长度和原始长度不一样但Content-Length没更新服务器会返回 400 或者截断请求。检查 Flask 代码里是否有Content-Length的替换逻辑确保它和实际请求体长度一致。6. 把凭据管理和自动化链路分开TaoToken 在这套方案里的实际位置跑完整个流程后回头看这套方案的结构其实可以分成两层一层是 JS 逆向的自动化链路包括 MCP、Skill、JSRPC、Flask、autoDecoder另一层是 AI 调用的凭据管理也就是 TaoToken 负责的部分。这两层是解耦的自动化链路本身不依赖特定的 API 通道你可以用任何兼容 OpenAI 风格的服务来替换。但实际用下来把凭据管理统一到 TaoToken 有几个好处。第一是切换工具的时候不用重新配 KeyCodex、Cline、Claude Code 都用同一个 Key省去了重复配置的麻烦。第二是额度集中管理不会出现这个工具额度用完了、那个工具还有余额但没法调的情况。第三是排查问题的时候链路更清晰401 就是 Key 的问题local proxy failed 就是 MCP 的问题不会混在一起。如果你要长期跑这套自动化流程建议把 Coding Plan 用起来。因为一次完整的 JS 逆向分析可能涉及多轮模型调用按次计费的话额度消耗不好预估。Coding Plan 的入口在 https://taotoken.net/coding-plan 接入文档在 https://taotoken.net/doc API Keys 管理在 https://taotoken.net/api-keys 。模型对话的调试入口在 https://taotoken.net/chat 可以用来快速验证 Key 是否可用。最后说一个实操中的经验Skill 文件的内容不是越多越好。我一开始把所有的模板都塞进主技能文件里结果 AI 在生成代码的时候会混淆不同模板的字段比如把 Flask 的SIGN_FIELD写到 JSRPC 的配置里。后来改成主技能只负责流程控制具体模板拆成独立文件按需加载生成质量明显提升。如果你也遇到 AI 输出格式不稳定的情况可以先检查 Skill 文件的结构是否清晰。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进