跳到正文
← 返回博客
LAB / 实验

签名正确,为什么 API 仍然不给真实数据?

一次把签名、会话信誉、TLS / H2 指纹和行为环境逐层拆开的逆向对照实验。

在 Web 逆向中,最危险的结论之一是:“签名已经一致,所以请求链路已经还原。”

签名正确只能证明某段算法输入与输出对齐。它不能自动证明会话有效、传输层指纹可信,也不能证明服务端会把当前客户端视为真实用户。当保护系统把多层信号一起用于决策时,只盯着请求参数会把相关性误当成因果。

先定义“正确”是什么

分析 TikTok Web API 与 Shop 验证码链路时,我把结果拆成几个不同层级:

  1. JavaScript 或 WASM 的关键中间值是否一致;
  2. 最终签名或密文是否与真实样本字节级一致;
  3. 使用相同会话发送后,HTTP 响应结构是否一致;
  4. 返回内容是否包含真实业务数据;
  5. 同一请求在不同 TLS / H2 环境下是否发生变化。

这些问题不能合并成一个“成功 / 失败”布尔值。否则算法正确但数据为空时,很容易继续错误地修改算法。

把运行环境缩到最小

面对 JSVMP、多层 SDK 和浏览器环境检测,我不会一开始就补完整浏览器。更稳定的方法是先记录真实运行路径,再提取最小闭包。

对于 JavaScript SDK,可以在 Node vm 中逐步补齐真正访问到的对象。对于虚拟机保护,则通过 opcode trace 记录操作数、寄存器变化和关键 payload。每增加一项环境对象,都要能够解释它为什么存在。

验证码链路中的 Emscripten crypto.wasm 也采用类似方式处理:先确认导出函数和内存布局,再还原 SHA-512 双链 KDF 与 AES-256-GCM 参数。离线实现最终在 4/4 个真实样本中与浏览器密文达到字节级一致。

到这里,可以确认算法层已经收敛。但真实数据仍然可能没有打开。

使用单变量 A/B 拆开门禁

接下来需要把可能影响服务端判断的变量分开:

  • 签名:固定会话和传输环境,只替换签名实现;
  • 会话:固定签名与传输环境,只替换 Cookie 和相关令牌;
  • 请求头:保持其他变量不变,逐项对照客户端提示字段;
  • 传输层:使用相同请求内容,分别通过普通客户端和 Chrome 风格 JA3 / HTTP2 指纹发送;
  • 行为环境:比较真实浏览器上下文与纯 HTTP 调用结果。

我使用 cycletls 或 curl_cffi 控制传输层特征,并保留每轮请求、响应和变量组合。只有当一个变量变化后结果稳定重复变化,才把它列入真正门禁。

对照结果表明,真实数据并不是由 X-Bogus 或 X-Gnarly 单独决定。会话信誉以及 Akamai 所观察到的 TLS / H2 指纹,同样参与了结果判断。

为什么这一步能节省时间

如果没有分层验证,常见路径是继续修改补环境、反复寻找“遗漏字段”,甚至推翻已经字节对齐的算法。这样的投入不会增加证据,只会增加变量。

单变量实验的作用,是告诉我们下一步应该继续研究哪一层,也明确哪些层已经可以停止修改:

算法层:字节一致,冻结
会话层:显著影响结果,继续观察
传输层:显著影响结果,建立模拟
业务层:以真实数据返回作为最终验证

交付不应该停在研究笔记

最终成果也不只是“找到了原因”。我会把稳定部分沉淀为脱机签名模块、SSR 抽取工具和自动验证 CLI:输入真实样本,工具可以分别展示算法结果、传输环境和业务响应,让后续变化能够快速定位。

逆向工程的价值不在于猜中一次,而在于建立一个可以重复证明、可以发现回归、可以移交给下一位工程师的过程。

签名正确当然重要,但它只是证据链中的一环。真正可靠的结论,必须解释从字节到传输、从会话到业务数据的完整路径。