当前位置:首页>python>Python 纯算还原快手签名,17 个接口实测通过,关键过程复盘

Python 纯算还原快手签名,17 个接口实测通过,关键过程复盘

  • 2026-10-11 05:45:42
Python 纯算还原快手签名,17 个接口实测通过,关键过程复盘

前言

这次做的是一个移动端接口签名还原项目:目标不是把 native 当黑盒服务一直挂着,而是把核心签名逻辑拆出来,做到本地纯算。

最终结果:sig 和 __NS_sig3 都完成了本地计算,文档中的 17 个未登录接口用纯算签名实际请求,全部返回 HTTP 200,响应 body 都是 result=1。

这篇文章只复盘思路和关键判断,不展开可直接复现的代码、常量和完整参数。

一、目标与工具

1.1 目标

项目开始时,请求里主要卡在两个字段:

字段
目标
结果
sig
从 native 调用还原到本地算法
已完成
__NS_sig3
从 UniDbg 调用还原到本地算法
已完成
动态字段
判断是否硬编码、时间戳或状态字段
已定位来源
接口验证
判断纯算签名是否可用
17 个接口通过

这类项目最重要的不是“能不能跑一次”,而是要弄清楚每个字段到底由什么决定。

1.2 工具链

工具
作用
jadx
Java 层调用链定位
UniDbg
native 函数模拟和 trace
Python
算法复现、回归测试、请求验证
trace log
对齐寄存器、栈字段、内存读写

整体路线是:

Java 调用链定位
        |
        v
UniDbg 跑通 native 输出
        |
        v
trace 中间字段
        |
        v
Python 复现算法
        |
        v
样本回归 + 接口验证

二、请求流程分析

2.1 签名链路

请求大致分成两层签名:

query + form body
        |
        v
按客户端规则拼接明文
        |
        v
生成 sig
        |
        v
path + sig
        |
        v
生成 __NS_sig3
        |
        v
发送接口请求

几个关键点:

点
说明
sig
 输入
来自 query 和 body 的规则化拼接
__NS_sig3
 输入
来自最终 path 和 sig
__NS_xfalcon
本批未登录接口不依赖
设备参数
参与请求输入,但不是签名算法本身

2.2 参数类型要分清

逆向时很容易把“测试参数”和“算法参数”混在一起。这里我把它拆成三类:

类型
举例
是否算法核心
算法常量
native 中的固定常量、白盒表
是
动态状态
nonce、counter
是,但不是硬编码
业务样例参数
photoId、userId、keyword、page-code
不是

后面实际验证时,业务参数可以替换;签名算法会根据新请求重新计算。

三、核心逆向过程

3.1 sig 的还原

sig 的入口在 Java 层比较容易定位,最终会走到 native 方法。

通过 UniDbg trace 可以看到它内部是一个标准摘要计算流程,并且 update 输入由两部分组成:

请求明文 + native 内固定补充片段

这一步还原完成后,sig 就不需要再调用 UniDbg。

验证方式是多组输入输出回归:

样本
结果
空字符串
本地输出与 native 一致
短字符串
本地输出与 native 一致
长字符串
本地输出与 native 一致
实际接口明文
本地输出可用于请求

3.2 __NS_sig3 的结构

__NS_sig3 比 sig 复杂很多。它不是简单 hash,同一个输入多次调用,输出也会变化。

trace 后拆成几层:

path + sig
        |
        v
输入相关摘要
        |
        v
白盒变换
        |
        v
CRC 类压缩字段
        |
        v
拼 raw payload
        |
        v
checksum + 编码

最终输出是固定长度的 hex 字符串。

我把 raw payload 拆成几段:

字段
作用
magic
固定标识
nonce
初始化状态字段
flag
固定标记
input hash
和 path + sig 强相关
counter
进程内自增计数
tail
固定尾部字段
checksum
raw payload 校验

这里最关键的是区分两类字段:

字段
是否由输入决定
input hash
是
nonce
否,来自初始化状态
counter
否,来自进程内递增状态

3.3 输入相关 hash 的还原

输入相关部分经过了几步:

input bytes
        |
        v
HMAC/SHA 类摘要
        |
        v
按 block 分组
        |
        v
whitebox transform
        |
        v
压缩成 4 字节字段

这部分是 __NS_sig3 能否纯算的核心。因为只要输入相关字段错,服务端就会直接判签名失败。

我的做法是:

  1. 用 UniDbg trace 抽出中间 block。
  2. dump native 中用到的变换表。
  3. 在 Python 中实现相同变换。
  4. 用多组样本对齐中间值,而不是只对最终结果。

最终可以做到:

path + sig -> input hash

完全本地计算。

3.4 nonce 和 counter 的定位

一开始 nonce/counter 很容易被误判为时间戳。后来通过内存读写确认:

字段
实际来源
本地处理
nonce
初始化状态结构中的 4 字节字段
本地初始化一次
counter
native 全局计数器自增
Python 进程内自增

这两个字段不是硬编码。

默认请求时:

nonce:本地随机初始化
counter:每次签名递增

只有在做 trace 回归时,才会固定这两个值,用来复现某一条 native 输出。

四、其他技术点

4.1 为什么有些接口返回数据少

17 个接口都能签名通过,但不是每个接口都会返回丰富数据。有些接口只返回轻量字段或空列表。

为了确认这不是纯算签名的问题,我做了纯算和 UniDbg 对比:

接口类型
纯算结果
UniDbg 结果
结论
评论接口
有评论数据
有评论数据
签名正常
用户资料
有资料数据
有资料数据
签名正常
搜索建议
有 suggest 数据
有 suggest 数据
签名正常
部分热词/榜单
返回空列表或轻量字段
返回一致
参数上下文不足

如果纯算和 UniDbg 同参数返回一致,就说明问题不在签名,而在业务上下文。

这类上下文包括:

上下文
影响
ussid
搜索链路会用
llsid
feed/详情链路会用
recoReportContext
推荐流会用
channel id
频道接口会用
page-code
页面场景会用

4.2 设备字段不是算法硬编码

请求里有大量设备字段。它们会参与签名明文,但它们不是算法本身。

可以这样理解:

设备字段变化
    -> sig_plain 变化
    -> sig 变化
    -> __NS_sig3 变化

这说明签名是跟请求内容绑定的,不代表算法里硬编码了某个设备。

项目里也做过设备随机化测试:保留身份字段,只随机硬件字段时,常用接口可以稳定返回。

五、完整实现流程

5.1 工程结构

最后落地时,我把签名封装成统一入口:

模块
作用
sig 计算模块
负责请求主签名
sig3 计算模块
负责 native 安全签名
状态模块
维护 nonce/counter
请求器
统一构造 query/body/header
回归测试
对齐 native 样本和 live 结果

调用流程:

build request
      |
      v
build sig_plain
      |
      v
calc sig
      |
      v
calc sig3(path + sig)
      |
      v
send request

5.2 回归策略

我没有只看最终接口能不能过,而是做了三层回归:

层级
验证内容
算法样本
本地输出和 native 输出一致
dry-run
17 个接口都能构造签名请求
live 请求
17 个接口实际返回 result=1

这能避免一种常见问题:接口偶然过了,但算法其实没有完全对齐。

六、实测结果

6.1 单接口结果

评论首屏接口纯算签名请求结果:

指标
结果
HTTP
200
X-KsResult
1
body result
1
评论数量
30
commentCount
878
签名来源
pure

6.2 17 个接口结果

接口名
HTTP
body result
数据情况
评论首屏
200
1
有评论数据
评论翻页
200
1
有评论数据
作品基础信息
200
1
返回轻量字段
feed 预取信息
200
1
有 feedInfo
用户资料
200
1
有 userProfile
用户作品流
200
1
有 feeds
用户评论列表
200
1
有列表
搜索首页
200
1
有热词/频道
搜索建议
200
1
有 suggest
搜索用户
200
1
有 mixFeeds
搜索趋势
200
1
列表为空
搜索历史
200
1
未登录历史为空
搜索预置词
200
1
列表为空
搜索联想词
200
1
列表为空
详情页热词
200
1
轻量字段
搜索新热榜
200
1
轻量字段
热门频道
200
1
轻量字段

总结果:

指标
结果
测试接口数
17
HTTP 200
17/17
body result=1
17/17
纯算签名
17/17
和 UniDbg 对照
可疑接口结果一致

七、AI 辅助心得

7.1 AI 做了什么

这次 AI 主要帮在三个地方:

  1. trace 整理:从大量日志里归纳寄存器、栈字段和内存读写关系。
  2. 假设验证:快速区分字段是输入相关、状态相关,还是固定协议字段。
  3. 工程收敛:把零散脚本整理成可回归的纯算实现。

效率提升最大的地方是日志阅读。native trace 很容易陷入细节,AI 适合先把证据链梳成结构,再由人判断关键路径。

7.2 正确用法

这类项目里,我不会让 AI 直接猜算法,而是这样用:

  • 先跑出确定样本。
  • 每个中间值都要求对齐。
  • 每个字段都要解释来源。
  • 纯算结果要和 UniDbg 对照。
  • 接口返回异常时,先区分签名问题和业务上下文问题。

AI 的价值不是替代逆向判断,而是把证据整理速度拉起来。

总结

这次逆向的核心收获:

  1. sig 已从 native 调用还原成本地算法。
  2. __NS_sig3 已拆成输入 hash、白盒变换、动态状态、外层编码四部分。
  3. nonce/counter 不是硬编码,分别来自初始化状态和进程内自增。
  4. 纯算签名实际请求 17 个接口全部返回 result=1。
  5. 数据少的接口和 UniDbg 对比一致,问题在业务上下文,不在签名。

真正有价值的不是某一段代码,而是完整的验证链路:定位、trace、还原、回归、请求验证。只要这条链路闭环,后面扩展接口就不会靠猜。

最新文章

随机文章