1. 某游快爆小程序 sign 参数逆向从抓包到复现的完整链路某游快爆小程序在请求游戏论坛帖子列表时会带一个 32 位十六进制字符串的 sign 参数典型 MD5 hex 特征。这个参数由客户端 JS 动态生成服务端会校验缺了或者算错就直接返回验签失败。本文要解决的就是怎么在不硬啃混淆代码的前提下借助 AI 辅助分析和 MCP 工具链把 sign 的生成逻辑定位出来、还原成可运行的 Python 脚本并且用真实请求验证闭环。适合谁看做过一点抓包、想入门小程序逆向的同学已经在用 Claude Code、Cursor、Qoder 这类 AI 客户端想把抓包工具和调试工具接进 AI 工作流的同学以及被 sign、token、加密参数卡住、想找一套标准分析路径的同学。整条链路的核心思路是——把抓包工具和调试工具通过 MCP 协议暴露给 AI让 AI 自己去读请求、解包源码、定位签名函数人只负责下指令和验证结果。我试过把抓包记录直接贴给 AI 让它猜算法效率很低真正跑通之后发现关键不在于模型多强而在于工具链有没有接对。下面按“前置准备 → 配置 → 验证 → 排障”的顺序拆开讲每一步都给可复制的配置和命令。2. 前置准备TaoToken 统一 Key 与 MCP 工具链的角色分工在动手之前先把工具链的角色理清楚不然配置到一半容易乱。整条链路里有四个角色抓包工具负责拿到真实请求包括 URL、请求体、请求头和响应小程序调试工具负责解包 wxapkg、拿到明文 JS 源码、必要时 Hookwx.*APIAI 客户端负责串联所有工具、执行分析推理模型服务负责提供推理能力这里用 TaoToken 统一 Key 接入一个 Key 打通对话、编码、Agent 场景不用在多个平台之间来回切。TaoToken 在这里的作用是给 AI 客户端提供稳定的模型调用入口。它的 API 地址是https://taotoken.net/api兼容主流 OpenAI 风格的调用方式所以 Claude Code、Cursor、Qoder 这类客户端都能直接配。官网在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册后在控制台生成 API Key 即可。需要提前准备的东西抓包工具Reqable自带 MCP 服务器能让 AI 直接读取抓包记录、生成 curl、重放请求。小程序调试工具First基于 Frida 注入微信进程 CDP 协议桥接支持 wxapkg 解密解包、wx.*API 捕获、路由枚举自带 MCP 服务器默认端口 4554。AI 客户端Qoder也可以换成 Claude Code、Codex、Cursor配置方式大同小异。逆向技能包reverse-skill给 AI 提供标准逆向工作流避免它乱跑。TaoToken API Key用于模型调用。注意小程序逆向和 APP 逆向不一样没有 so 层原生保护wxapkg 解包后基本是明文 JS盐值经常直接硬编码在源码里难度低一个量级。这也是为什么这套 AI MCP 的链路在小程序场景下特别顺。3. 可复制配置settings.json 与 config.toml 骨架这一节是全文最核心的部分配置对了后面才跑得动。分三块TaoToken 接入、MCP 客户端配置、技能包安装。3.1 TaoToken 统一 Key 接入先在 TaoToken 控制台生成 API Key然后按你用的客户端配置。以 Claude Code 为例环境变量方式最省事export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的TaoToken密钥如果你用的是支持config.toml的客户端比如某些 Codex 风格配置骨架如下[model] provider taotoken base_url https://taotoken.net/api api_key sk-你的TaoToken密钥 model claude-sonnet-4-5 [agent] max_tokens 8192 temperature 0.2温度建议调低一点逆向分析要的是稳定复现不是发散创意。模型选择上涉及长上下文源码分析时优先选上下文窗口大的型号解包后的app-service.js动辄上万行窗口不够会截断。3.2 MCP 客户端配置骨架MCP 配置一般放在客户端的settings.json或连接器配置里。Reqable 的 MCP 是本地可执行文件方式{ mcpServers: { reqable: { command: D:\\App\\reqable\\Reqable\\mcp-server.exe, args: [] } } }Reqable 的 MCP 支持三个可选参数--host指定 Reqable 所在设备 IP默认127.0.0.1--port指定代理端口默认自动获取通常是 8888--scope控制注册哪些工具minimal只注册常用工具默认all注册全部。First 的 MCP 是 SSE 方式配置更简单{ mcpServers: { first-miniapp: { url: http://127.0.0.1:4554/sse } } }把这两段合并进同一个settings.json的mcpServers字段即可。导入后在客户端的连接器页面确认三个能力都开启本机执行能力、reqable抓包数据、first-miniapp小程序调试。3.3 安装 reverse-skillreverse-skill 是一个逆向技能路由包封装了几十条逆向工作流覆盖 APK 逆向、JS 签名、协议分析等方向兼容 Claude Code、Codex、Cursor。安装方式很直接把仓库地址发给 AI让它装进自己的 skills 目录并加载。它的价值在于给 AI 一个基本逆向思路不会一上来就胡乱跑。虽然它本身没有专门的小程序逆向模块但前端 JS 逆向部分思路相通拿来用完全够。3.4 First 的版本匹配First 对微信版本有要求仓库主页有 WMPF 版本支持列表目前推荐微信 4.1.10。版本不匹配会导致 Frida 注入失败。启动 First 后点击启动它会自动注入微信运行日志里出现you can now open any miniapps并给出一段 DevTools 调试链接就说明注入成功。4. 验证请求sign 参数定位与结果比对工具就绪后打开 Reqable在微信里打开目标小程序进入游戏论坛滑动一下列表。回到 Reqable 能看到一条 POST 请求项目内容请求地址https://m.3839.com/miniapp/api.php?mapictopicasection_topicv1.1请求方式POSTapplication/x-www-form-urlencoded接口作用获取某个游戏版块section_id的帖子列表按编辑时间排序、分页关键参数sign32 位十六进制字符串典型 MD5 hex 特征请求体参数包括version、timestamp、type、token、uid、section_id、topic_list_type、sort、last_id、cursor、screen_id。我们要分析的就是 sign。4.1 一句话下指令在 AI 客户端里输入加载 reverse-skill使用 reqable 熟悉请求使用 first-miniapp 尝试破解 sign 参数。然后等结果。AI 做的事分两步先通过 first-miniapp 对小程序解包拿到 JS 源码再通过 reqable 读取抓包记录拿到真实参数两者对照后在源码里定位签名函数。4.2 签名函数还原签名函数定位在app-service.js第 10562 行是一个叫l()的通用请求封装函数所有接口请求都走它。核心逻辑格式化后如下var l function (e) { // 盐值硬编码在源码里 var r miniapp_kgb77Mdac046167ec7bd4d3cde27ad6NBV; // 基础参数 {version, timestamp} 登录态 {type, token, uid} 业务参数 var c Object.assign( { version: 1.0.0, timestamp: parseInt(Date.now() / 1e3) }, t, ...业务参数 ); delete c.sign; // 删掉旧的 sign c h(c); // 空值参数置空本例参数全非空可跳过 // 按 key 字典序排序拼成 keyvaluekeyvalue 的串 var u Object.keys(c), l u.sort().map(e e c[e]).join(); l r; // 末尾直接拼盐 c.sign Hash(md5).update(l).digest(hex); // 整串做 MD5 formPost(e, c); // 表单提交 };翻译成人话把除 sign 外的所有参数按参数名排序拼成keyvaluekeyvalue的串末尾加上盐整串做 MD5。4.3 用真实参数复算验证拿抓包里的真实参数复算cursor0last_id0screen_id0section_id11631sortedit_timetimestamp1787542341token【脱敏】topic_list_typealltype1uid【脱敏】version1.0.0 miniapp_kgb77Mdac046167ec7bd4d3cde27ad6NBV得到e0a1d6b18a3ecc8f4f63c2c8f82b438a与抓包里的 sign 完全一致。到这里算法就闭环了。4.4 Python 复现脚本让 AI 把算法用 Python 复现脚本骨架如下token/uid 已脱敏# -*- coding: utf-8 -*- 小程序 API 签名复现脚本 签名算法 1. 收集参数基础参数 {version, timestamp} 登录态 {type, token, uid} 业务参数 2. 删除值为空字符串 / null 的参数 3. 按参数名(key)字典序升序排序 4. 拼成 k1v1k2v2... 的字符串 5. 字符串末尾直接拼接盐值 6. 整串做 MD5输出 32 位小写 hex就是 sign import hashlib SALT miniapp_kgb77Mdac046167ec7bd4d3cde27ad6NBV params { version: 1.0.0, timestamp: 1787542341, type: 1, token: 【脱敏替换为真实值】, uid: 【脱敏替换为真实值】, section_id: 11631, topic_list_type: all, sort: edit_time, last_id: 0, cursor: 0, screen_id: 0, } captured_sign e0a1d6b18a3ecc8f4f63c2c8f82b438a def calc_sign(data: dict, salt: str): 复现小程序端签名逻辑返回 (sign, 拼接串) raw .join(f{k}{data[k]} for k in sorted(data.keys())) raw salt sign hashlib.md5(raw.encode(utf-8)).hexdigest() return sign, raw if __name__ __main__: sign, raw_string calc_sign(params, SALT) print(f参与签名的参数个数: {len(params)}) print(拼接串(排序后 kvkv 盐):) print(raw_string) print(f本脚本算出的 sign: {sign}) print(f抓包里的真实 sign: {captured_sign}) if sign captured_sign: print( MATCH: 算法复现成功sign 与抓包完全一致) else: print( 不匹配请检查参数是否与抓包一致、是否有空值参数被过滤)运行后 11 个参数参与签名脚本算出的 sign 和抓包里的真实 sign 一模一样。4.5 完整请求验证为了严谨换一个游戏版块完整模拟一次带自己 sign 的请求。本接口的游戏标识参数是section_id11631 代表香肠派对93 代表原神。请求部分核心逻辑import gzip import hashlib import json import time import urllib.request import urllib.parse SALT miniapp_kgb77Mdac046167ec7bd4d3cde27ad6NBV API_URL https://m.3839.com/miniapp/api.php?mapictopicasection_topicv1.1 params { version: 1.0.0, timestamp: str(int(time.time())), type: 1, token: 【脱敏替换为真实值】, uid: 【脱敏替换为真实值】, section_id: 93, # 换游戏11631 - 93 topic_list_type: all, sort: edit_time, last_id: 0, cursor: 0, screen_id: 0, } headers { Host: m.3839.com, User-Agent: (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541a35) XWEB/19977), Content-Type: application/x-www-form-urlencoded, Referer: https://servicewechat.com/wxcce6440ef3727704/10/page-frame.html, Accept: */*, xweb_xhr: 1, Accept-Encoding: gzip, deflate, br, Accept-Language: zh-CN,zh;q0.9, } if __name__ __main__: sign, raw_string calc_sign(params, SALT) params[sign] sign print(f[1] 签名计算完成: sign{sign}) body urllib.parse.urlencode(params) req urllib.request.Request(API_URL, databody.encode(utf-8), headersheaders, methodPOST) print(f[2] 发送请求: POST {API_URL}) with urllib.request.urlopen(req, timeout15) as resp: status resp.status raw resp.read() if resp.headers.get(Content-Encoding, ).lower() gzip: raw gzip.decompress(raw) raw raw.decode(utf-8, errorsreplace) print(f[3] 响应状态码: {status}) data json.loads(raw) print(f code: {data.get(code)} msg: {data.get(msg)}) result data.get(result) or {} posts result.get(data) or [] print(f 返回帖子数: {len(posts)}) if posts: first posts[0] print(f 首条帖子: id{first.get(id)} f内容{str(first.get(content))[:40]} 版块sid{first.get(sid)})运行输出code: 100、msg: ok、返回帖子数 10、首条帖子版块sid93服务端验签通过返回的正是原神版块数据。5. 本篇常见错排查配置和验证过程中容易踩的坑集中列一下按出现频率排序。Frida 注入失败First 启动后日志没有出现you can now open any miniapps。九成是微信版本不匹配去 First 仓库主页对照 WMPF 版本支持列表目前推荐微信 4.1.10。版本对了还失败检查微信是否完全退出后重启Frida 注入需要干净的进程环境。MCP 连接器显示未连接Reqable 的 MCP 是本地可执行文件方式路径里如果有中文或空格容易出问题建议放在纯英文路径下。First 的 SSE 地址是http://127.0.0.1:4554/sse确认 First 的 MCP 页面已经开启服务端口没被占用。sign 复算不匹配先检查参数是否和抓包完全一致尤其是timestamp这种动态值复算时要用抓包那一刻的值不能用当前时间。其次检查是否有空值参数被过滤——小程序端有个h()函数会把空字符串和 null 置空本例参数全非空可以跳过但换接口时要注意。最后检查排序是不是严格字典序Object.keys().sort()是默认字符串排序Python 的sorted()行为一致但如果你手动拼串容易漏掉某个参数。请求返回验签失败sign 算对了但服务端不认通常是请求头缺了小程序环境标识。Referer和User-Agent里的MiniProgramEnv、WindowsWechat、WMPF这些字段是风控识别依据缺了可能被拦。照抄抓包里的请求头最稳。响应体解析乱码响应是 gzip 压缩的urllib不会自动解压需要手动判断Content-Encoding再gzip.decompress。用requests库的话它会自动处理但要注意有些客户端会带br编码需要额外装brotli。不同接口盐值不一样同一个源码里login 接口的签名用的不是这个盐而是另一个值且 key 是倒序拼接。不同接口的签名格式可能不同不要拿一个盐套所有接口。定位签名函数时优先找通用请求封装函数所有接口都走它的那种。6. 把这条链路固化成你自己的分析模板整套流程跑下来最有价值的不是某个具体的盐值而是这条可复用的路径抓包看参数结构 → 反编译搜md5/sign关键词 → 提取函数上下文 → 用真实请求复算闭环验证。小程序场景下这条路径尤其顺因为 wxapkg 解包后就是明文 JS盐值经常直接硬编码。如果你想把这条链路用到自己的项目上建议先把工具链配稳TaoToken 统一 Key 负责模型调用一个 Key 打通对话和编码场景省去多平台切换的麻烦Reqable 和 First 的 MCP 配置一次后面每个新目标都能直接复用。API Key 在控制台生成接入文档里有各客户端的详细配置示例模型对话入口可以用来快速验证模型连通性长期做编码和 Agent 任务的话 Coding Plan 更划算。配置过程中卡在 MCP 连接或者 sign 复算不匹配优先去 API Keys 和接入文档页对照检查想先确认模型调用是否正常用模型对话跑一条简单请求最快。工具链稳了剩下的就是重复“下指令 → 等结果 → 验证”这个循环泡杯茶的功夫一个 sign 就还原完了。