5.6 KiB
region_ticket 静态逆向结论
结论
region_ticket 不是 sig、__NS_sig3 或 DFP 一类的客户端计算结果。
APK 将它建模为服务端响应顶层 region.ticket 中的不透明票据,按用户保存到
DefaultPreferenceHelper 的 <uid>_Region,后续请求再把它放入 Cookie。
因此,纯 Python 的正确实现是“接收、持久化、复用”,不是本地生成:
- 解析每个普通 API JSON 响应的顶层
region。 - 有
ticket时按region.uid保存;uid为空时使用当前用户 ID。 - 后续请求注入
Cookie: region_ticket=<ticket>; __NSWJ=<value>。 - 账号切换时按 UID 隔离,不能把一个账号的票据当作设备级常量。
静态调用链
任意普通 API JSON 响应
-> ResponseDeserializer.deserialize()
-> 读取顶层 region.uid / region.name / region.ticket
-> ylm.d.mRegion
-> u0a.f.buildObservableBeforeRetry()
-> doOnNext(com.kwai.framework.network.regions.c)
-> regions.c.accept()
-> o2a.c.c(region, "New region received")
-> DefaultPreferenceHelper[<uid>_Region] = Region JSON
下一次请求
-> v0a.c.c(sceneName)
-> q01.g.l0()
-> NetworkAccessParams.e.l0()
-> o2a.c.b(Region.class).ticket
-> Cookie: region_ticket=<ticket>; __NSWJ=<value>
关键证据
1. IOC 实现绑定
pnm.b.b(-1479227965)在生成的IOCProviderImpl中绑定到com.kwai.framework.network.access.params.e。q01.g.l0()的实际实现读取o2a.c.b(Region.class),返回Region.b(), 即mTicket;不存在加密、哈希或 native 调用。- Region scheduler 的 IOC ID
1013182224绑定到o2a.e。
证据文件:
out/region_ticket_jadx/IOCProviderImpl.javaout/region_ticket_jadx/NetworkAccessParamsE.javaout/region_ticket_jadx/RegionSchedulerProviderE.java
2. 响应下发与统一持久化
ResponseDeserializer 只读取响应 JSON 的顶层字段:
{
"region": {
"uid": "...",
"name": "...",
"ticket": "RT_..."
}
}
u0a.f 把 regions.c 注册为普通 API 的全局 doOnNext 处理器。
regions.c 检查 ylm.d.k(),非空时直接调用:
o2a.c.c(response.k(), "New region received");
这说明没有唯一的 region_ticket 申请接口。任何使用统一响应包的普通 API
都可以顺带下发或轮换 Region。
3. 本地存储
o2a.c 使用 DefaultPreferenceHelper:
读取键: <QCurrentUser.id>_Region
写入键: <region.uid>_Region
回退键: <QCurrentUser.id>_Region
值格式: {"uid":"...","name":"...","ticket":"RT_..."}
未登录时当前 UID 回退为 0。如果响应明确带 region.uid,写入时优先使用
该 UID,所以账号票据不会天然复制到另一个 UID。
4. Cookie 注入
v0a.c 先组合 region_ticket、kuaishou.api_st、__NSWJ,再序列化为
Cookie 请求头。region_ticket 为空时该项被省略,不会生成占位值。
5. RegionInfo 不是票据来源
o2a.e 会从 SharedPreferences 的 RegionInfo 或 raw 资源加载 API 区域路由。
APK 资源 raw/0x7f100082 只有 api_group_host_list 和 api_mapping,没有
ticket。它负责 follow/nearby 的区域 host 选择,不负责签发 Region 票据。
HAR 审计
新增 tools/analyze_region_ticket.py,结构化审计以下三类事件:
- 响应 JSON 出现顶层或嵌套
region.ticket; - 响应头出现
Set-Cookie: region_ticket=...; - 请求 Cookie 携带
region_ticket。
对仓库全部 7 个 HAR、8334 个 entry 的结果:
响应 region.ticket: 0
响应 Set-Cookie: 0
请求 region_ticket: 362
唯一票据: 12
票据长度: 全部 76
票据形态: RT_ + 73 个十六进制字符
12 个样本的 73 个十六进制位置均有变化,没有固定版本位或可见字段边界。 这与“服务端不透明票据”一致,但仅凭格式不能反推出生成算法。
现有 HAR 的抓包窗口开始时票据已经存在:例如历史窗口的第一批 startup、spot、 reddot 请求已经携带同一票据。因此“首个携带票据的请求”不是签发接口,这批 HAR 不能确定首次下发发生在哪个 endpoint。
脱敏审计产物位于:
out/region_ticket_har_small.jsonout/region_ticket_har_20260708_1416.jsonout/region_ticket_har_20260710.jsonout/region_ticket_har_20260726.jsonout/region_ticket_har_ip_20260726.jsonout/region_ticket_har_cdn_20260724.json
对短信登录 CLI 的影响
当前 CLI 已支持 --region-ticket、KS_REGION_TICKET 和 app-fields 注入,
但当前 sms_device_latest.json 不包含该状态。静态链给出的实现边界是:
- DFP 注册不会本地生成
region_ticket。 passport_account_image、account_security 和请求签名也不会生成它。- CLI 应在 checker、发码、登录及后续普通 API 响应中检查顶层
region,收到后 更新当前 Session Cookie 并按 UID 保存。 - 在首次收到服务端 Region 之前,只能不带该 Cookie;不能构造一个等价票据。
缺少 region_ticket 会造成 APP/CLI 请求上下文差异,可能增加风控评分,但静态证据
不足以把 705 单独归因于它。要验证因果,应在同一设备画像、同一手机号、同一网络
下只改变该 Cookie,比较 checker 和 mobileVerifyCode 的结果。
复现命令
uv run python -m tools.analyze_region_ticket `
ks.har `
nebula.kuaishou.com_2026_07_10_17_15_37.har `
--out out/region_ticket_audit.json
uv run python -m unittest tests.test_analyze_region_ticket