乐于分享
好东西不私藏

AI 逆向 App 接口逆向实战:某瓣推荐流 _sig 是怎么还原出来的?

AI 逆向 App 接口逆向实战:某瓣推荐流 _sig 是怎么还原出来的?

AI 逆向 App 接口逆向实战:某瓣推荐流 _sig 是怎么还原出来的?


很多同学学了 Frida、Jadx、IDA 之后,卡在同一个问题上:

“工具都会用,但怎么把它们串成一条完整的分析链路?”

这篇文章我就用一个真实练习案例,把这条链路完整走一遍:目标是豆瓣 Android 客户端 recommend_feed 接口中的 _sig 参数。

我会重点讲三件事:

  1. _sig 到底怎么生成(算法、拼串、密钥来源)

  2. 为什么很多人算出来总是错(常见误区)

  3. 遇到 native 反调试时,如何稳定拿到运行期证据

声明:本文仅用于授权环境下的安全研究与逆向学习,不用于任何未授权用途。


一、目标接口与问题定义

目标接口是:

GET https://frodo.douban.com/api/v2/elendil/recommend_feed

请求里有两个关键参数:

  • _sig

  • _ts

如果 _sig 不对,服务端会直接拒绝或返回异常数据。所以问题就很明确:还原 _sig 生成逻辑并实现自动化请求。


二、先定策略:静态 + 动态 + 复现

我这次的思路是三段式:

  1. Jadx 静态追调用链先找到业务 API 到签名函数的完整路径。

  2. Frida 动态抓关键变量把“猜测”变成“证据”,拿到运行期的 secret / sign_text / sig

  3. Python 落地复现实现一份能自动生成 _sig/_ts 并拉到真实数据的请求脚本。

这三段缺一不可。只做静态,容易被混淆误导;只做动态,没有全局上下文;不落地脚本,价值不稳定。


三、Jadx 里最关键的调用链

1)业务入口

在业务 API 里能定位到:

  • e6.e.Q(...) 组装了 elendil/recommend_feed 请求。

2)签名注入点

进一步追到网络层拦截器:

  • q9.c 会在请求发出前自动追加 _sig 和 _ts

这一步很关键:说明不是每个业务接口自己算签名,而是统一网络层处理。

3)签名核心逻辑

最终到 q9.b

  • q9.b.b(Request):从请求头取 Authorization

  • 去掉 Bearer 前缀,把 token 传给 q9.b.a(...)

  • q9.b.a(...) 拼出签名原文,再调用 lb.c.a(...)

而 lb.c.a(...) 里就是标准:

  • HmacSHA1

  • Base64.encodeToString(..., NO_WRAP)

到这里算法基本就明牌了。


四、最容易踩坑的点:密钥不是 apikey

很多人第一反应是拿 apikey 去做 HMAC,这会直接算错。真实链路里用于 HMAC 的是 n1.c()(client_secret)。

在初始化阶段能看到:

  • 网络配置注入了 n1.a() 和 n1.c()

  • 其中 n1.c() 被签名逻辑使用

结论:

  • apikey:普通公共参数

  • apiSecret(n1.c()):HMAC 密钥

这也是为什么很多“看起来只差一点”的脚本始终过不去。


五、_sig 真实公式(可直接复现)

设:

  • method = GET

  • path = /api/v2/elendil/recommend_feed

  • enc_path = Uri.encode(path) => %2Fapi%2Fv2%2Felendil%2Frecommend_feed

  • token = Authorization 去掉 Bearer 前缀

  • ts = 秒级时间戳

签名原文:

sign_text = method + "&" + enc_path + "&" + token + "&" + ts

签名值:

_sig = Base64( HMAC_SHA1(secret, sign_text) )_ts  = ts

六、对拍:把猜测变成证据

我拿了一组固定样本对拍:

sign_text:GET&%2Fapi%2Fv2%2Felendil%2Frecommend_feed&42c40114121c2cc6c84c033c36548652&1774482261

复算结果:

qwj/PE66ctfqRqEbSZv48N8RSQQ=

与抓包 _sig 完全一致。这说明算法、拼串规则、密钥来源全部闭环。


七、反调试分析:为什么一挂 Frida 就闪退

继续分析 libmsaoaidsec.so,能看到典型反调试痕迹:

  • ptrace

  • prctl

  • /proc/self/maps

  • /proc/%d/cmdline

  • /proc/%d/task

动态时也能观察到高频 prctl(15) 和 /proc 路径访问。所以不处理 anti-frida,脚本经常刚挂上就掉。

我的处理策略是 libc 层统一拦截:

  • ptrace/prctl

  • open/openat/fopen/opendir/readlink

  • strstr/strcmp

把“检测输入”改掉,比硬 patch 某个函数偏移更稳。


八、最终落地成果

这次练习最后产出了两类可复用资产:

1)请求复现脚本

  • main.py

  • 自动生成 _sig/_ts

  • 发起 recommend_feed 请求并解析数据

实测结果:

  • HTTP 200

  • 成功拿到 items 数据并打印标题/摘要/链接

2)动态分析脚本

  • 一体化脚本(anti-frida + _sig hook + probe)

  • 可用于后续版本回归验证(升级后看看签名链路是否变化)


九、这次实战的几个关键经验

  1. 先找统一拦截器,再看业务 API很多签名不在业务层,而在网络层统一处理。

  2. “能跑通”不等于“分析对了”一定要做固定样本对拍,才能确认每一步都正确。

  3. 密钥来源比算法更重要HMAC 算法常见,真正难点在 secret 的来源和初始化时机。

  4. anti-frida 要做成策略,不要做成魔法偏移偏移会变,行为特征更稳定。


十、结语

这次案例本质上不是“会不会写 HMAC”,而是:

你能不能把静态、动态、复现三条线真正闭环。

如果你也在做 App 协议逆向,建议你给每次练习都做一份“可执行报告”:

  • 链路图

  • 核心证据

  • 对拍样本

  • 可运行脚本

长期坚持,进步会非常快。