9.8 KiB
9.8 KiB
capture/ 登录链路 + APP 初始化 实测分析
来源:
capture/Reqable body-only 抓包导出(1738 文件) 时间:2026-07-23 06:32 起(session B,全新装机 + 登录) 目标设备:did=ANDROID_f05497e9cef09a7f(与.env账号设备e8dfd2f16b618053不同 = 全新装)
0. 抓包格式与固有限制
- 文件命名:
{ts_ms}-{flowId}-{part}-{type}.reqable,flowId/ts决定时序。 - 类型:
req_raw-body(请求体,常为加密{"data":"..."}或明文 form)、res-raw-body(原始响应,chunked/gzip 二进制)、res-extract-body(已解码响应 JSON,主要可读源)、req-extract-body(解码请求体)。 - 无 URL/header/method 文件。端点只能从 body 内容 + 响应推断。
- 混入少量酷狗(
com.kugou.android,flow 466/467)噪声;主体是快手 nebula。 - 请求体多为 kwsg 加密;关键字 grep(
mobile/smsCode)命中的多是日志上报(flow 577 几百段=批量 log),非登录。
1. APP 初始化序列(session B,06:32 起,已登录前 uid=0)
| flow | 时间 | 端点(推断) | 关键字段 |
|---|---|---|---|
| 13 | 06:32 | 冷启动首批请求 | — |
| 171 | 06:48 | 配置拉取 | public_param{uid:0,did,kpn:NEBULA,kpf,app_version} + request_info[{config_name:config}] |
| 189 | ~06:xx | DFP 设备注册 | productName=NEBULA&ts=...&deviceInfo=<urlenc>(= gdfp_report/unified_fetch,已还原于 core/dfp_forms.py) |
| 272/274 | ~08:44 | 域名-IP 路由表 | domains[]{domain,iplist{ipv4,ipv6}}(含 api.e.kuaishou.com、id.kuaishou.com 等,非登录) |
DFP bootstrap(设备 did+egid 在线注册)在登录前完成,与
tools/new_device.py已还原链路一致。✓
2. 登录链路(核心发现)
登录方式 = 运营商一键登录(provider-token),非手机号短信
flow 221(08:18,登录提交,请求体明文 + 已还原签名):
provider=11
&provider_token=CgZORUJVTEESGEFORFJPSURfZjA1NDk3ZTljZWYwOWE3ZhjdzY/JgMgdIKy0l/v4Mw==
&session_id=eab9a270-1d55-45a2-97dd-f4f32f9c3eb8
&cs=false&os=android&client_key=2ac2a76d
&sig=6d37a2436f910601ef23cd2b6fd212f2
&__NS_sig3=544530168b10f35b1f1c1f1ecddd17765822656c010d0315
&__NS_xfalcon=HUDR_sFnX+...==$TE_253aa3c5c5193634544b6e193634544b...
provider_token base64 解码为 protobuf:
field1(string) = "NEBULA" # kpn
field2(string) = "ANDROID_f05497e9cef09a7f" # did
field3(varint) = 0xddcd8fc980c81d # 时间戳类
field4(varint) = 0xacb497fbf833 # nonce/凭证
即 {kpn, did, ts, nonce}——由运营商一键登录 SDK(如移动认证/极光一键登录)签发,绑定设备 did。
provider=11= 认证 provider 编码(一键登录)。- 请求签名
sig/__NS_sig3/__NS_xfalcon全部用已还原算法可复算。✓
登录响应
- flow 221 响应:
{"result":1,"bind_interval_ms":604800000}→ 登录成功,设备绑定有效期 7 天(604800000 ms)。 - flow 222(08:19,疑似 getLoginUser/refreshToken 后续)响应:
{"dataRsp":"<576B base64>","result":1,"error_msg":""}result=1= 成功。会话票据(api_st/h5_st/user_id/client_salt)封装在dataRsp加密块内。
3. 会话票据加密层(关键)
dataRsp(576B)head8 = 7a1c41a6 6f7ee464。- 不属于已还原的 kwsg 10400 ZT envelope(其
head8为5a54eecd...,见core/enc_data.py:ZT_OUTER_CONFIGS)。 - 判定:登录会话走
libpfl_crypto层(Pfl.decryptBinaryNative,FINDINGS 历史"解密方向"用的是 native 桥,纯 Python 解密未还原)。 - 对比:reward/ad 的
encData= kwsg 10400(已还原纯 Python 加密);登录dataRsp= libpfl(未还原纯 Python)。 - 另:
api_st/h5_st也可能下发在响应 header(body-only 抓包看不到)——子代理此前同样怀疑此点。
4. 与既有结论的交叉验证
out/FINDINGS.md:2476-2504已实测:直接换 H5 did/egid →signIn/report、treasureBox/report返回result=50 签名验证失败。H5 真正缺口是kwssectoken/kwscode/kwfv1/kww票据组(Yoda/KsGuard/WebView 安全层)。- 本次 capture 未见
kwssectoken/kwscode/kwfv1/kww在 body 中签发 → 这组票据是 WebView/JS 运行时产物或 header,HTTP body 抓包不包含其生成。kww在真实 App 中确为请求 header(见main.py:H5_HEADERS.kww),本抓包无 header → 不可见。
5. "新设备纯 Python 登录"的阻塞点(结论)
| 阻塞点 | 性质 | 现状 |
|---|---|---|
① 运营商 provider_token |
外部黑盒(运营商一键登录 SDK,需 SIM+设备) | 无法纯 Python 生成;本抓包只看到它被消费 |
② dataRsp 会话解密 |
libpfl_crypto envelope(head8 7a1c41a6) |
未纯 Python 还原(历史仅 native 桥) |
③ kwssectoken/kwscode/kwfv1/kww |
Yoda/KsGuard/WebView 运行时票据 | 不在 HTTP body 抓包;H5 写请求换设备必 result=50 |
KS 侧请求签名 sig/__NS_sig3/__NS_xfalcon |
已还原 | ✓ 可复算登录请求 |
| DFP 设备注册 | 已还原 | ✓ 新设备可在线注册 |
含义
- "新设备 + 复用已有账号 token 跑任务"(A 路线):API 侧可行(已验证 200);H5 侧被 ③ 卡死(result=50),除非补 kws* 票据(需 WebView JS 逆向,不在 HTTP 抓包内)。
- "新设备 + 全新登录拿设备绑定 token"(B 路线):被 ①② 卡死。① 要么复刻运营商一键登录 SDK(极难、需 SIM),要么改走手机号+短信登录(本抓包未捕获该路径,需另抓);② 需还原 libpfl_crypto 纯 Python 解密。
6. 关键 flow 索引
| flow | 文件前缀 | 用途 |
|---|---|---|
| 13 | 1784817239557610-13-* |
冷启动 |
| 171 | 1784817240305029-171-1-* |
配置拉取 |
| 189 | 1784817240121*-189-1-* |
DFP 设备注册(deviceInfo) |
| 213 | 1784817244968189-213-1-* |
登录前加密请求(3798B,待解) |
| 221 | 1784817247*-221-1-* |
登录提交(provider_token,明文+签名) |
| 222 | 1784817247*-222-1-* |
登录后续响应(dataRsp 会话,加密) |
| 272/274 | 1784817247*-27[24]-1-* |
域名-IP 路由表 |
7. 下一步可选
- 解密 flow 222
dataRsp:还原 libpfl_crypto 纯 Python 解密(参照EmbeddedAesKeyHex+Pfl.decryptBinaryNative的 native 逻辑),取出api_st/h5_st/user_id/client_salt明文,确认是否设备绑定。 - 抓手机号+短信登录路径:作为 ① 的替代(更可纯 Python 复刻),需真机抓一次短信登录 HAR。
- WebView JS 逆向 kws 票据*:解 ③,才能真正让新设备跑 H5 写请求。
8. pfl_crypto 静态逆向进展(dataRsp 解密,2026-07-23)
已拿到(新硬证据)
- 内嵌 AES key 已静态还原(此前 FINDINGS 认为需 runtime frida):
EmbeddedAesKeyHex@libpfl_crypto.so:0x6249c(lazy singleton,guard @0x111f78,storage @0x111fa0)。- 构造器
0x62550:分配 64 字节,key[i] = blobA[i] ^ blobB[i % 32](i=0..63)。blobA@0x182c8(64B),blobB@0x18308(32B,循环)。
- 还原结果:
027e5393fd36a4ba1b6cf52094edb4aeffcc55d175492d87ed7df271eb691ef0(64 hex = AES-256 key)。 - 诱饵
WrongEmbeddedAesKeyHex@0x625e4(blobs0x18328/0x18368)= 真 key 首字节 XOR0x10->127e...。
AesDecrypt@0x62fbc(1728B):hex-decode key 串(校验 len 0x40=AES-256 / 0x20=AES-128),随后对密文做自定义字节重排(memcpy @ 偏移0xc与end-0x10),再调内部 cipher。- 排除 kwsg-10400:dataRsp
head8=7a1c41a6...,用 4 个已知 kwsg xor_key 解包后 inner magic 均 ≠dec0adde;且7a1c41a6非 kwsg head8(5a54ee..)-> dataRsp 不是 kwsg 10400 envelope,是 pfl 自有格式。 - 设备绑定已硬证据(无需解密 dataRsp):
- 登录请求 flow 221
provider_token= protobuf{kpn=NEBULA, did=ANDROID_f05497e9cef09a7f, ts, nonce}-> 登录凭证构造上就绑设备 did。 probe3.log常规任务请求携带klinkToken(同结构 protobuf,含 .env 设备 did)-> 设备绑定 token 随请求流转。out/FINDINGS.md实测:换 H5 did ->result=50 签名验证失败。
- 登录请求 flow 221
仍阻塞(需动态捕获)
- cipher 是 standard AES(硬件实现):
libpfl_crypto.so用 ARM 硬件aese/aesd/aesmc/aesimc(全 .so 共 1769 条),故无软件 S-box/rcon/T-table。 - 用还原 key(real/decoy)对 dataRsp 试遍 ECB/CBC/CTR/CFB/OFB(AES-128/256,
IV@首16/末16/0/12/16/32,ct 全/[16:]/[16:-16]/[32:] 等)均乱码 ->
dataRsp 不是简单
AesDecrypt(还原key, dataRsp):- 要么 dataRsp 用的 key 非 EmbeddedAesKeyHex(会话派生 / RSA 包裹 / 另一静态 key);
- 要么缺
Pfl.decryptBinaryNative的第二个参数 envelope(body-only 抓包看不到, IV/envelope 可能来自该 arg 或响应 header); - 要么 dataRsp 走的不是 pfl(网络层另解)。
AesDecrypt@0x62fbc字节重排:把密文拆[0:12]/[12:N-16]/[N-16:N]三段 std::string,再调硬件 AES(0x9492c等 + 循环)。- 要拿 dataRsp 明文,需动态捕获真正用的 (key, envelope, IV, plaintext)。
已写探针
out/probe_pfl_login.js:登录时 hookPfl.decryptBinaryNativepfl::crypto::AesDecrypt+EmbeddedAesKeyHex,命中api_st/h5_st/user_id即打印明文。
结论
- 设备绑定问题已有硬答案 = 是(请求侧 provider_token 内嵌 did + H5 换设备 result=50)。
- dataRsp 明文(api_st/h5_st/user_id 具体值)需动态跑
out/probe_pfl_login.js抓取;key 已静态还原但 dataRsp 非该 key 直解,缺 envelope/session key。属 "确认响应侧 token 是否也带 did 校验"的锦上添花项,不改变绑定结论。