# 快手极速版 短信登录链路(纯 Python 可复刻路径) > 来源:`login_gateway_plugin-master.apk`(jadx)+ 主 app jadx(`zvl.a` Retrofit 接口) > 日期:2026-07-24 > 关联:`docs/capture_login_chain.md`(运营商一键登录 = 加密 dataRsp,难复刻) ## 核心结论 **短信登录是纯 Python 可复刻的登录路径**:请求为明文 form,响应为明文 JSON,直接返回 `api_st/h5_st/api_client_salt/userInfo`。**无需 pfl/kwsg 解密**(与运营商一键登录的 加密 `dataRsp` 不同)。 唯一非 Python 环节:**接收短信验证码**(需一台能收码的手机/接码服务)--但比运营商 一键登录(需 carrier SDK + SIM + 设备)门槛低得多。 ## 登录 API(Retrofit 接口 `zvl.a`,全部 POST form) ### 1. 发送短信验证码 ``` POST n/user/requestMobileCode # 方法 M,@FormUrlEncoded form: mobileCountryCode "+86" mobile "<手机号>" type 27 # 已存在手机号短信登录 useVoice false needCheck true prefetchPhoneNumber "" requestSource "<来源>" query: + sig / __NS_sig3 / __NS_xfalcon / client_key / os / did / egid / oDid / rdid / ... -> RequestVerifyCodeResponse { result, isCheck, phone[] } ``` ### 2. 验证码登录(拿会话) ``` POST /rest/n/user/login/mobileVerifyCode # 方法 r0,@FormUrlEncoded,@y0n.d Map query: did / egid / oDid / rdid / client_key / os / ... form (动态 map / FieldMap): code / mobile / mobileCountryCode / type=27 / isDegraded=false deviceName / deviceMode / publicKey / raw / secret + sig / __NS_sig3 / __NS_xfalcon -> LoginUserResponse (明文 JSON,见下) ``` 签名细节(2026-07-24 Frida + jadx 确认): - `sig = CPU.getClock(sorted(query + body 非签名字段))` - `__NS_sig3 = KSecurity.atlasSign(encodedPath + sig)` - `__NS_xfalcon = KXGS((sig + __NS_sig3).getBytes(), 2096)` - `encodedPath` 只用于 sig3 与跳过列表判断;`xfalcon` 原始 byte[] **不包含 path**。 相关变体(同接口): - `n/user/login/mobileVerifyCode`(r0)、`n/user/login/token`(s0)、 `n/user/login/mobile`(148)、`n/user/login/mobileQuick`(197,一键)、 `n/user/login/quickLogin`(140)、`n/user/login/preCheck`(205)、 `/rest/n/loginRegister/unified/verify`(258)。 ## 响应:LoginUserResponse(明文,含完整会话) 直接 JSON 字段(`@c` 注解 = JSON key): | 字段 | JSON key | 用途 | |------|----------|------| | mApiServiceToken | `kuaishou.api_st` | **API 会话 token** | | mH5ServiceToken | `kuaishou.h5_st` | **H5 会话 token** | | mNewTokenClientSalt | `kuaishou.api_client_salt` | **client_salt**(__NStokensig 用) | | mobile / mobileCountryCode | `mobile` / `mobileCountryCode` | 登录手机号 | | mPassToken | `passToken` | pass token | | quickloginToken | `quickloginToken` | 一键登录 token | | userInfo / multiUserInfo | `userInfo` / `multiUserInfo` | 用户信息(含 user_id) | | codeKey / codeUri | `codeKey` / `codeUri` | 二维码登录用 | | bindPhoneRequired / canLogin / canQuickLogin / isNewRegisterUser / loginType | 同名 | 登录态标志 | > 响应包装:`Observable>` -> 标准 KS 信封 > `{result:1, error_msg:"", data:{ kuaishou.api_st, kuaishou.h5_st, kuaishou.api_client_salt, ... }}` > **明文,非 dataRsp 加密**。 ## 与运营商一键登录对比 | 维度 | 短信登录 `mobileVerifyCode` | 运营商一键 `quickLogin` | |------|----------------------|----------------------| | 请求 | 明文 form + sig/sig3/xfalcon | 明文 form `provider=N&provider_token=` | | 凭证来源 | 手机号 + 收到的短信码 | carrier SDK token(需 SIM+设备+SDK) | | 响应 | **明文** LoginUserResponse | **加密** `dataRsp`(libpfl,未纯 Python 还原) | | 纯 Python 可行 | **是**(仅缺收码) | 否(carrier token + dataRsp 解密两道黑盒) | ## 复刻流程(纯 Python) ``` 新设备画像(DFP bootstrap 在线注册 did/egid/oDid/rdid,已还原) ↓ POST /rest/n/user/requestMobileCode (mobile=手机号, did=新设备, sig/sig3/xfalcon) ↓ 服务端发短信 [人工/接码] 收到验证码 ↓ POST /rest/n/user/login/mobileVerifyCode (query=设备, body=code/type=27/publicKey/raw/secret/sig/sig3/xfalcon) ↓ 明文响应 LoginUserResponse -> 取 kuaishou.api_st / kuaishou.h5_st / api_client_salt / userInfo ↓ 写入 .env / cookie_dict,后续任务链即可用新设备+新会话跑 ``` ## 待确认 / 风险 1. **host**:`/rest/n/user/login/...` 走 aegon 网关,确切 host 需实测确认 (候选:`api2.kuaishou.com` / `apissl.ksapisrv.com` / `api.ksapisrv.com`)。 静态未见明文 base URL(`zvl.a` 经 `pnm.b.b(1559932927)` DI 创建,base URL 在网络模块)。 2. **签名**:高概率走已还原的 `sig/__NS_sig3/__NS_xfalcon`(运营商登录 flow 221 实测带这些, 同一网络层),验码 FieldMap 中已补 `publicKey/raw/secret` 账号保护字段。 3. **请求是否带 encData**:`@FormUrlEncoded` + flow 221 明文先例 -> 判定明文 form,无 encData。 待实测确认。 4. **风控**:新设备 + 短信登录可能触发设备校验/短信频控/异地登录提示,需小流量验证。 5. **收码**:必须能接收短信(自有手机或接码平台),这是 SMS 登录固有限制。 ## 关键文件索引 - Retrofit 接口:`out/jadx/sources/zvl/a.java`(`M` 发码,`r0` mobileVerifyCode) - `LoginUserResponse`:含 `kuaishou.api_st`/`h5_st`/`api_client_salt`/`userInfo` - `RequestVerifyCodeResponse`:`{result, isCheck, phone[]}` - `login_gateway_plugin-master.apk`:仅 carrier 一键登录(联通/电信/移动 SDK + AuthModel), **无短信路径** -> 短信登录在主 app。 - 主 app 调用点:`com.yxcorp.login.bind.*`(ChangePhoneFragment 等) ## 实现(已完成,2026-07-24) - `core/sms_login.py`: - `login_api_params(profile)` 设备+app 参数(静态协议字段 + `device_profile_cookie_fields` 覆盖)。 - `signed_login_url(path, params, body_pairs, state, base_url, t1, t2)` 签名 URL (`sig` / `__NS_sig3` / `__NS_xfalcon`,**无 `__NStokensig`**,登录态未建立)。 - `request_mobile_code(...)` -> `RequestVerifyCodeResponse`(发码)。 - `login_by_code(...)` -> `LoginSession{api_st, h5_st, client_salt, user_id, mobile, pass_token}`; `login/*` 为 Retrofit `@FieldMap`,签名字段放在 form body,URL query 只放设备参数。 - `parse_login_user_response(data)` 解析明文 `LoginUserResponse`。 - sig3 使用进程级 `Kwsg10418State`:seed 按 native `srand(time) -> rand()+1` 现场生成,验证码重放、发码和登录共享同一递增 counter。 - `tools/sms_login_cli.py`:CLI 闭环(全新设备画像 -> 在线 DFP 注册 -> 发码 -> 输入码 -> 会话)。每次执行都不读取或保存历史设备、抓包字段及区域票据。 - CLI 只提供 `--mobile`、`--code`、`--base-url`、`--transport`;短信类型、 超时、sig3、region 和验证码重试策略均由内部固定。 ## 待实测确认(用 CLI 跑一次真机即可) ```bash # 真实流程(在线注册全新设备 -> 发码 -> 输入码 -> 会话) uv run python -m tools.sms_login_cli --mobile <手机号> # 如果发码请求超时但手机已收到短信,可跳过发码继续验码: uv run python -m tools.sms_login_cli --mobile <手机号> --code <收到的验证码> # 可选覆盖登录 host: uv run python -m tools.sms_login_cli --mobile <手机号> --base-url https://api.ksapisrv.com # 可选切换 HTTP 传输: uv run python -m tools.sms_login_cli --mobile <手机号> --transport okhttp4-android10 ``` `requestMobileCode` 或 `mobileVerifyCode` 同时返回 `result=705` 和 HTTPS `error_url` 时,CLI 自动尝试验证码求解,并在拿到 `captchaToken` 后重放原请求。 每个阶段内部最多重试 2 次。HTTP 求解未拿到 `captchaToken` 时直接停止,不启动 Playwright、Edge/Chrome 或系统浏览器。 当前 `14.5.50.11631` 匿名登录 query 已按 APP 样本对齐 `kcv=1630`,并补齐 `language/ud/bottom_navigation/is_background/icaver/darkMode/ftt`。这些字段参与签名和 服务端设备上下文判断,不能继续使用旧版本默认值。 登录阶段的 `raw/publicKey/secret`、设备画像、手机号密文和 `passport_account_image` 在当前 CLI 流程内固定,验证后重试时保持原请求正文一致。 当请求表现为 `status=0 body={}` 时,CLI 会额外打印底层 `error` 字段。该状态属于 传输失败,不会误判为 705,也不会进入验证重试。 实测需确认: 1. **host**:默认 `apissl.ksapisrv.com`;若 `result!=1`/网络错误,再换 `api2.kuaishou.com` / `api.ksapisrv.com` / `api.e.kuaishou.com`。 2. **session_seed/counter**:seed 是每进程动态值,不是固定常量;CLI 已纯 Python 复现 native PRNG,并从启动后基线 `counter=0x5f` 开始让所有请求连续递增。 CLI 不提供固定 seed/counter 的复现入口。 3. **响应明文**:`mobileVerifyCode` 响应应直接含 `kuaishou.api_st`/`h5_st`/`api_client_salt`。 4. **userInfo.user_id 字段名**:解析已兼容 `user_id/userId/eid/uid`。 5. **验证码位数 / type 归属**:复核 `PhoneVerifyParams` 与 `zvl.a.B(map)` 后确认, APP 6 位码对应“手机验证页”路径:`requestMobileCode(type=6)` -> `n/user/verify/mobile`,返回 `ActionResponse`,不是直接登录拿会话。 用户实测 `type=1` 文案为“仅用于注册”,不用于登录;CLI 直接登录固定使用 `type=27`。 登录成功后 CLI 会自动输出可直接写入 `.env` 的 `ksck="账号#完整 Cookie#client_salt"`,无需额外参数。