基于实战经验重构 JSVMP 分析框架: - SKILL.md: 新增路径选择决策树(路径 A 算法追踪 vs 路径 B 环境伪装) - SKILL.md: 路径 B「环境伪装六步法」完整方法论(采集→对比→分级→补丁→验证→端到端) - SKILL.md: 新增场景 10「JSVMP + jsdom 环境伪装」、解法模式 E - SKILL.md: 标注三板斧适用边界与局限,经验法则扩充至 22 条 - references/jsdom-env-patches.md: 新增 jsdom 环境补丁知识库(Function.toString 三层防御 / plugins 完整模拟 / DOM 布局属性 / 30+ API 存根) - cases/: 新增经验库系统(模板 + 首个 JSVMP 实战案例含 58 项环境检测差异全表) - references/jsvmp-analysis.md: JSVMP 专项分析详细指南 - references/troubleshooting.md: 常见问题排查指南 - templates/python-request/: Python 项目模板 - 各 references 文档增强、Python 技术栈支持、MCP v1.1 适配(44→52 工具) Made-with: Cursor
229 lines
8.7 KiB
Markdown
229 lines
8.7 KiB
Markdown
# 错误排查指南
|
||
|
||
## 请求失败排查流程
|
||
|
||
### 排查顺序(必须按序执行)
|
||
|
||
当协议脚本请求返回非预期结果时,**禁止盲目大改**,按以下 6 步逐项排查:
|
||
|
||
```
|
||
步骤 ① Cookie/Session 失效
|
||
│
|
||
├─ 排查方法:
|
||
│ · 浏览器 F12 → Application → Cookies 获取最新 Cookie
|
||
│ · 对比脚本使用的 Cookie 与浏览器当前 Cookie
|
||
│ · 检查是否有 HttpOnly Cookie 漏掉(Network 面板看完整 Cookie)
|
||
│
|
||
├─ MCP 辅助:
|
||
│ · get_cookies → 获取 Camoufox 中的 Cookie
|
||
│ · evaluate_js(expression="document.cookie") → 获取 JS 可见 Cookie
|
||
│
|
||
└─ 常见原因:Cookie 过期、登录态失效、缺少 HttpOnly Cookie
|
||
|
||
步骤 ② 前置请求遗漏
|
||
│
|
||
├─ 排查方法:
|
||
│ · Network 面板查看主请求之前的所有请求
|
||
│ · 重点关注:/api/init、/token、/config 等路径
|
||
│ · 检查前置请求的响应中是否有 Token、Session ID
|
||
│
|
||
├─ MCP 辅助:
|
||
│ · start_network_capture → 触发操作 → list_network_requests
|
||
│ · 按时间排序,找到主请求之前的接口
|
||
│
|
||
└─ 常见原因:漏掉预热接口、Token 获取接口、验证码初始化接口
|
||
|
||
步骤 ③ 时间戳绑定
|
||
│
|
||
├─ 排查方法:
|
||
│ · 对比浏览器请求的时间戳与脚本生成的时间戳
|
||
│ · 检查精度:秒(10位)vs 毫秒(13位)
|
||
│ · 检查是否有时间窗口限制(如签名有效期 30 秒)
|
||
│
|
||
├─ 常见坑:
|
||
│ · Python time.time() 返回浮点数,需要 int()
|
||
│ · Node.js Date.now() 返回毫秒
|
||
│ · 服务端可能校验请求时间与服务器时间的差值
|
||
│
|
||
└─ 修复方法:确保签名计算和请求发送使用同一个时间戳值
|
||
|
||
步骤 ④ Header 缺失或错误
|
||
│
|
||
├─ 排查方法:
|
||
│ · 逐项对比浏览器 Request Headers 与脚本 Headers
|
||
│ · 重点检查:Content-Type、Referer、Origin、Accept
|
||
│ · 检查自定义 Header(X-开头的、大小写敏感的)
|
||
│
|
||
├─ 常见坑:
|
||
│ · Header 顺序可能影响(极少数站点)
|
||
│ · sec-ch-ua 等 Client Hints Header
|
||
│ · Accept-Encoding 不支持 br 但发了 br
|
||
│
|
||
└─ 修复方法:完整复制浏览器 Headers,逐个删减确认必要项
|
||
|
||
步骤 ⑤ 环境校验
|
||
│
|
||
├─ 排查方法:
|
||
│ · Hook navigator/screen/canvas 等环境 API
|
||
│ · 检查签名计算是否包含环境指纹
|
||
│ · 对比有无环境值时的请求结果
|
||
│
|
||
├─ MCP 辅助:
|
||
│ · get_fingerprint_info → 查看 Camoufox 指纹
|
||
│ · search_code(keyword="navigator|screen|canvas") → 搜索环境检测代码
|
||
│
|
||
└─ 关键原则:先验证是否真正参与服务端校验,再决定是否补全
|
||
|
||
步骤 ⑥ 频率限制
|
||
│
|
||
├─ 排查方法:
|
||
│ · 降低请求频率重试(间隔 3-5 秒)
|
||
│ · 检查响应中的限流相关字段(rate limit、retry-after)
|
||
│ · 检查是否返回了 429 状态码
|
||
│
|
||
└─ 修复方法:增加请求间隔、使用代理 IP、添加随机延迟
|
||
```
|
||
|
||
---
|
||
|
||
## 签名值不一致排查
|
||
|
||
### 逐步对比法
|
||
|
||
当计算出的签名值与浏览器实际值不匹配时:
|
||
|
||
```
|
||
对比链路(脚本值 vs 浏览器值):
|
||
|
||
环节 1:原始输入参数
|
||
├─ 参数名是否完全一致(大小写、下划线)
|
||
├─ 参数值是否完全一致
|
||
└─ 是否有隐藏参数(空值但参与签名的参数)
|
||
|
||
环节 2:参数排序与拼接
|
||
├─ 参数排序规则(字典序、自定义顺序、原始顺序)
|
||
├─ 拼接分隔符(& / 空 / 其他)
|
||
├─ 是否包含 key=value 中的 key 名称
|
||
├─ 空值参数是否参与拼接
|
||
└─ URL 编码是否在拼接前/后执行
|
||
|
||
环节 3:时间戳
|
||
├─ 精度:秒 (10位) vs 毫秒 (13位)
|
||
├─ 类型:字符串 vs 数字
|
||
└─ 时区:UTC vs 本地时间
|
||
|
||
环节 4:随机串
|
||
├─ 长度是否匹配
|
||
├─ 字符集:hex / alphanumeric / 自定义
|
||
└─ 生成方法:Math.random vs crypto.randomBytes
|
||
|
||
环节 5:密钥/盐值
|
||
├─ 密钥值是否正确(注意空格、换行、编码)
|
||
├─ 密钥是否是硬编码还是动态获取
|
||
└─ 是否有 IV / Salt(AES 加密场景)
|
||
|
||
环节 6:中间摘要
|
||
├─ 多次哈希时逐层对比中间值
|
||
└─ 编码格式:hex(小写) vs hex(大写) vs base64
|
||
|
||
环节 7:最终输出
|
||
├─ 编码方式:hex / base64 / 自定义
|
||
├─ 大小写:hex 小写 vs 大写
|
||
└─ 是否有额外处理(截断、拼接前缀等)
|
||
```
|
||
|
||
### 快速定位技巧
|
||
|
||
```
|
||
技巧 1:二分法
|
||
如果有中间值可对比,先对比中间值,
|
||
确定偏差发生在前半段还是后半段
|
||
|
||
技巧 2:固定输入法
|
||
在浏览器和脚本中使用完全相同的:
|
||
- 时间戳(硬编码一个固定值)
|
||
- 随机串(硬编码一个固定值)
|
||
- 页码(相同页码)
|
||
然后对比签名结果,排除动态因素干扰
|
||
|
||
技巧 3:打印拼接字符串
|
||
在签名函数的哈希/加密调用之前,
|
||
打印完整的输入字符串,逐字符对比
|
||
|
||
技巧 4:MCP 辅助对比
|
||
- set_breakpoint_via_hook(target_function="签名函数")
|
||
- get_breakpoint_data → 获取浏览器端的真实入参和返回值
|
||
- 与脚本端逐项对比
|
||
```
|
||
|
||
---
|
||
|
||
## 常见错误与解决方案
|
||
|
||
### HTTP 状态码
|
||
|
||
| 状态码 | 常见原因 | 排查方向 |
|
||
|--------|---------|---------|
|
||
| 403 Forbidden | Cookie 失效 / Header 缺失 / IP 被封 / TLS 指纹 | 步骤 ①④⑤⑥ |
|
||
| 412 Precondition Failed | 签名校验失败 / 缺少前置请求 | 签名对比 + 步骤 ② |
|
||
| 429 Too Many Requests | 频率限制 | 步骤 ⑥ |
|
||
| 500 Internal Server Error | 参数格式错误 / 数据类型不匹配 | 检查请求 Body 格式 |
|
||
| 200 但数据为空 | 签名正确但参数错误 / 页码越界 / 缺权限 | 检查业务参数 |
|
||
|
||
### 加密相关
|
||
|
||
| 问题 | 可能原因 | 解决方法 |
|
||
|------|---------|---------|
|
||
| MD5 结果不一致 | 编码问题(UTF-8 vs ASCII) | 确认输入字符串的编码方式 |
|
||
| AES 解密失败 | 模式错误(CBC vs ECB)/ Padding 错误 / IV 错误 | 逐一确认模式、Padding、IV |
|
||
| Base64 结果多余字符 | URL-safe Base64 vs 标准 Base64 | 检查是否需要 `+/` → `-_` 替换 |
|
||
| HMAC 结果不一致 | 密钥编码问题 / 算法类型错误 | 确认密钥是字符串还是 hex bytes |
|
||
| RSA 加密失败 | 公钥格式错误 / PKCS1 vs OAEP | 检查公钥格式和填充方案 |
|
||
|
||
### 环境相关
|
||
|
||
| 问题 | 可能原因 | 解决方法 |
|
||
|------|---------|---------|
|
||
| Node.js vm 沙箱报错 | 缺少 DOM API(document/window) | 参考 `environment-patch.md` 补环境 |
|
||
| Python execjs 报错 | Node.js 未安装 / JS 代码有语法错误 | 检查 Node.js 环境、验证 JS 代码 |
|
||
| WASM 加载失败 | 缺少 imports / 内存不足 | 检查 WASM imports 并补全 |
|
||
| Cookie 设置不生效 | domain 不匹配 / path 不匹配 | 确认 Cookie 的 domain 和 path |
|
||
|
||
---
|
||
|
||
## Python 特有问题
|
||
|
||
| 问题 | 原因 | 解决方法 |
|
||
|------|------|---------|
|
||
| `requests` 请求被拒 | TLS 指纹被识别 | 换用 `curl_cffi`(支持浏览器指纹模拟) |
|
||
| `hashlib.md5()` 报错 | Python 3.9+ FIPS 模式限制 | 使用 `hashlib.md5(usedforsecurity=False)` |
|
||
| `execjs` 执行慢 | 每次创建新的 JS 运行时 | 编译后复用 context:`ctx = execjs.compile(js_code)` |
|
||
| `pycryptodome` 与 `pycrypto` 冲突 | 同时安装了两个库 | `pip uninstall pycrypto && pip install pycryptodome` |
|
||
| 中文编码问题 | 签名包含中文字符 | 使用 `urllib.parse.quote()` 或确认 UTF-8 编码 |
|
||
| `httpx` HTTP/2 连接失败 | 缺少 h2 依赖 | `pip install httpx[http2]` |
|
||
|
||
---
|
||
|
||
## Node.js 特有问题
|
||
|
||
| 问题 | 原因 | 解决方法 |
|
||
|------|------|---------|
|
||
| `crypto` 模块不可用 | 使用了浏览器打包版本 | 确认运行环境是 Node.js 而非浏览器 |
|
||
| `vm` 沙箱超时 | JS 代码有死循环 | 设置 `timeout` 选项 |
|
||
| `axios` 被 WAF 拦截 | 默认 User-Agent | 自定义完整浏览器 UA |
|
||
| HTTP/2 请求失败 | 证书验证失败 | `rejectUnauthorized: false`(调试用) |
|
||
|
||
---
|
||
|
||
## 排查工具速查
|
||
|
||
### MCP 工具
|
||
|
||
| 排查场景 | MCP 工具 | 用法 |
|
||
|---------|---------|------|
|
||
| 对比请求差异 | `start_network_capture` + `get_network_request` | 在 Camoufox 中捕获真实请求,与脚本请求对比 |
|
||
| 获取真实签名值 | `set_breakpoint_via_hook` + `get_breakpoint_data` | 在签名函数设伪断点,捕获真实入参和返回值 |
|
||
| 验证环境检测 | `get_fingerprint_info` + `check_detection` | 确认哪些环境项参与校验 |
|
||
| 追踪调用链 | `get_request_initiator` | 从请求直接定位到签名函数 |
|
||
| 实时对比 | `evaluate_js` | 在浏览器中执行还原后的签名函数,与脚本输出对比 |
|