ksjsb/findings.md
2026-07-30 20:25:56 +08:00

18 KiB
Raw Blame History

发现与决策

需求

  • 用户要求先了解一个黑盒测试比赛。
  • 当前工作区:ksjsb
  • 工具目录:D:\decode-tools

研究发现

  • 工作区初始可见文件:
    • ksjsb.apk:约 108 MB核心 Android 目标。
    • log-sdk.ksapisrv.com_2026_07_08_12_16_09.har:约 11 MB。
    • nebula.kuaishou.com_2026_07_08_14_16_43.har:约 164 MB。
    • 2026-07-09-065602-ddecode-toolsfrida.txt:约 340 KB疑似 Frida 输出。
    • sign_layers_all.log:约 1.7 KB疑似签名链路日志。
    • 1.txt:约 11 KB内容待确认。
  • D:\decode-tools 已存在 Android/逆向相关工具:
    • apktool
    • dex-tools-v2.4
    • jadx-1.5.5
    • jadx-gui-1.5.5-with-jre-win
    • GDA4.04
    • ghidra_10.4_PUBLIC
    • ghidra_11.3.2_PUBLIC
    • IDA_Pro_7.7_Portable
    • IDA_Pro_8.3
    • ida93sp2
    • jeb-pro-3.19.1.202005071620
    • x64dbg
    • dnSpy-net-win64
    • ILSpy_selfcontained_8.2.0.7535-x64
  • out 目录并非空目录,已有大量前序分析成果:
    • CURRENT_STATE.md
    • FINDINGS.md
    • FINDINGS_VM.md
    • ks_sign.py
    • 多个 probe_*.jskste_*.logxfalcon_*.log
    • jadxjadx16manifestsodex 子目录
  • 1.txt 明确给出目标:逆向快手极速版看广告接口的 9 个签名/加密参数, 包括 sig__NS_sig3__NS_xfalcon__NStokensigencDatasign,以及 egidoDiddid 设备指纹, 最终转换成 Python 纯算。
  • 1.txt 中的关键链路:
    • sigfmm/{a,b,c,e,i}.getSig(明文),后端疑似 doCommandNative
    • __NS_sig3fmm/g: k.b(sig1, path)doCommandNative(10418) / libkwsgmain
    • __NS_xfalconfmm/{a,b,c,e}: k.a(path, str),涉及 kste VM / libksxgs
    • __NStokensigfmm/g: k.d(sig2, token),记录中判断为 sha256(sig2+token)
    • encData/sign:疑似 libpfl_crypto 的 AES/HMAC 类逻辑
  • sign_layers_all.log 显示已在设备 PJZ110 上对 com.kuaishou.nebula 及多个子进程进行了 Frida attach 但该文件本身只有 attach/detach 记录,未见具体签名结果。
  • 当前核心样本 SHA256
    • ksjsb.apkEBA577EF45CBA1715CC8C5562022AD5D4B10C1C442578F1C41BD3F05CCC33183
    • log-sdk...har7FBCA534370DA5D63694BBC75E341D91A9E12ED97229C04DFB58FA7AEF64754C
    • nebula...har57F5A07645C6758761E43102567AB8DE45423C364744351BE695D8A7A098FADE
  • APK 初筛结论:
    • 包名:com.kuaishou.nebula
    • 版本:14.5.50.11631versionCode=11631
    • 入口:com.yxcorp.gifshow.HomeActivity
    • Applicationcom.yxcorp.gifshow.App
    • SDKminSdk=21targetSdk=30
    • 19 个 dex约 100 个 arm64 native so混淆强。
    • 不是传统单一壳入口;更像大型业务包 + 自研安全/插件/热修复体系。
    • 高关注 solibkwsgmain.solibksxgs.solibksse.solibw.solibpfl_crypto.solibsnow.solibshadowhook.so
    • Manifest 高关注:allowBackup=false,未显式 debuggable 网络安全配置允许明文流量debug-overrides 信任用户证书。
  • HAR 初筛结论:
    • log-sdk...har681 entries方法分布 CONNECT=395POST=146GET=139
    • nebula...har1156 entries方法分布 CONNECT=567GET=511POST=76
    • 高频业务 endpoint
      • POST adlog.e.kuaishou.com/rest/nebula/log/ad/photo/action
      • POST log-sdk.ksapisrv.com/rest/wd/common/log/collect/misc2
      • GET nebula.kuaishou.com/rest/n/nebula/activity/earn/...
      • POST /rest/e/reward/mixed/ad 各 HAR 中各 1 条。
    • 未发现 Authorization header认证/会话主要在 Cookie 与 query/body。
    • 高频签名/状态字段:sig__NS_sig3__NS_xfalcon__NStokensigegiddidoDidrdidkuaishou.api_stclient_keycold_launch_time_ms
    • 原样短期重放可能可行;改参稳定重放必须重算签名。
  • 已验证的本地算法/脚本:
    • out/ks_sign.py 可验证 sig__NStokensig10400 encData10418 reward sign10418 __NS_sig3 的多组样本。
    • out/analyze_reward_samples.py 共解析 3 条 /rest/e/reward/mixed/ad POST 样本,sig_reward_form 全部匹配。
    • out/test_live_reward_log.pyout/test_reward_sig.pyout/test_build_reward_request.py 等单测均通过。
  • 当前算法完成度:
    • sig = MD5(sorted(query + formBody, skip sig/sig2/__NS*) + "772867c19925")
    • __NStokensig = SHA256(sig + clientSalt)
    • encData = Base64(kwsg 10400 raw)strE -> 0x266fc block -> inner ZT -> outer ZT
    • body.sign = kwsg 10418 true-mode(strE, sdk=95147564-...)
    • __NS_sig3 = kwsg 10418 false-mode(path + sig)
    • __NS_xfalcon reward/API 输入链路已接入本地 core 计算。
    • reward 广告拉取 strE 已能按当前账号/设备动态生成,并接入 本地 encData/sign/sig/sig3/tokensig/xfalcon
    • 轻量在线验证中,普通广告/签到广告/宝箱广告拉取均返回 HTTP=200 result=1 msg=OK,说明动态 reward body 已解决 换设备后的 ANTISPAM_REQUEST/INVALID_REQUEST
    • did/oDid/rdid 已可本地生成;egid/cloud did 已可通过 DFP bootstrap 在线注册并覆盖;仍需继续处理 H5 __NS_sig3/kww 的 动态生成。

技术决策

决策 理由
优先做被动侦察 当前已有 APK、HAR、Frida 日志,足以先建立证据图
并行拆分子任务 APK、HAR、日志、工具链互不依赖可独立读取分析
主线从 out/CURRENT_STATE.md 等已有成果恢复 避免重复劳动,当前证据显示已有较深入的 xfalcon/kste 分析
暂不主动请求生产接口 当前阶段目标是了解和复核;主动重放可能消耗账号/服务端状态

验证结果

  • python out\ks_sign.py:通过,输出多项 [OK]
  • python out\test_reward_sig.py:退出码 0。
  • python out\test_live_reward_log.py4 tests OK。
  • python out\test_analyze_xfalcon_te.py3 tests OK。
  • python out\test_build_reward_request.py1 test OK。
  • python out\test_extract_kste_vmobj_log.py1 test OK。
  • python out\analyze_reward_samples.py:解析 3 条 reward 样本, sig_reward_form ok=True__NS_sig3_calc ok=True;其中一条 tokensig 不匹配当前 CLIENT_SALT,判断为不同账号/会话上下文。

遇到的问题

问题 解决方案
当前目录不是 Git 仓库 不使用 Git 作为变更依据
.codex\skills\.system\using-superpowers 不存在 改读 .agents\skills\using-superpowers

2026-07-27 - passport_account_image 纯算结论

  • passport_account_image 由客户端 Engine.pr(99999, 0, mf.a().toString().length()*2, json) 确定性生成,不是 /f/a/p 服务端签发或回写票据。
  • 基础层是 VIMG_ + Base64固定头、Modified UTF-8 长度与正文经 0x55 XOR 再进入自定义状态的 ChaCha20-IETF。
  • $AI_ 由基础层文本经 16-word XOR 折叠、自定义 IV BLAKE2s 压缩和固定 H2 混合生成;跨 64-word 块使用累计 word counter。
  • 21 组不同长度输入与 arm64 libweapon.so Unicorn oracle 完全一致。
  • CLI 默认按当前 DeviceProfile 和现场时间纯算,不读取 app-fields也不依赖 Frida、抓包、APK、ELF 或 Unicorn 运行时。

资源

  • D:\decode-tools
  • ksjsb.apk
  • 两个 HAR 文件
  • 已有 Frida/签名日志

capture/ 登录链路 + APP 初始化实测2026-07-23

  • 抓包:capture/ Reqable body-only 导出1738 文件,无 URL/header/method 仅 req/res body请求体多为 kwsg 加密)。
  • 抓的是全新装机did=ANDROID_f05497e9cef09a7f(与 .env 账号 e8dfd2f16b618053 不同06:32 冷启动,启动时 uid=0(未登录)。
  • 混入少量酷狗(com.kugou.androidflow 466/467噪声主体快手 nebula。
  • APP 初始化(已定位):
    • DFP 设备注册 flow 189productName=NEBULA&ts&deviceInfo=<urlenc> = gdfp_report/unified_fetch,已还原于 core/dfp_forms.py)。
    • 配置拉取 flow 171public_param{uid:0,...}+request_info[{config}]
    • 域名-IP 路由表 flow 272/274domains[]{domain,iplist}(含 api.e.kuaishou.comid.kuaishou.com非登录)。
  • 登录方式 = 运营商一键登录(非手机号短信):
    • flow 221登录提交请求体明文 + 已还原签名): provider=11 & provider_token=<protobuf> & session_id & sig & __NS_sig3 & __NS_xfalcon & client_key=2ac2a76d & os=android
    • provider_token base64 解码 = protobuf {kpn="NEBULA", did="ANDROID_f05497e9cef09a7f", ts, nonce},由运营商一键登录 SDK 签发,绑定设备 did。
    • sig/__NS_sig3/__NS_xfalcon 全部可用已还原算法复算。✓
    • 响应:{"result":1,"bind_interval_ms":604800000}(设备绑定 7 天); 会话在 flow 222 {"dataRsp":"<576B加密>","result":1} 内。
  • 会话票据 = libpfl_crypto 层,未纯 Python 还原
    • dataRsp576Bhead8=7a1c41a6...不属于已还原的 kwsg 10400 ZT envelopehead8=5a54eecd...,见 core/enc_data.py:ZT_OUTER_CONFIGS)。
    • 历史 Pfl.decryptBinaryNative 用的是 native 桥,纯 Python 解密未还原。
    • api_st/h5_st 也可能下发在响应 headerbody-only 抓包不可见)。
  • 交叉验证 out/FINDINGS.md:2476-2504:直接换 H5 did/egid -> signIn/reporttreasureBox/report 返回 result=50 签名验证失败 H5 真正缺口是 kwssectoken/kwscode/kwfv1/kwwYoda/KsGuard/WebView 运行时票据),不在 HTTP body 抓包kww 实为请求 header
  • "新设备纯 Python 登录"三阻塞点: ① 运营商 provider_token(外部黑盒,需 SIM+设备); ② dataRsp libpfl 解密(未还原纯 Pythonkwssectoken/kwscode/kwfv1/kwwWebView 运行时票据H5 换设备必 result=50
  • 详见 docs/capture_login_chain.md

视觉/浏览器发现

  • 暂无。

2026-07-27 - region_ticket 静态逆向

  • pnm.b.b(-1479227965) 绑定到 com.kwai.framework.network.access.params.e;其 l0() 仅从 DefaultPreferenceHelper[<uid>_Region] 读取 Region.ticket
  • ResponseDeserializer 从所有统一响应的顶层 region 读取 uid/name/ticketu0a.f 注册的全局 regions.c 随后调用 o2a.c.c(region, "New region received") 持久化。
  • v0a.c 把非空 ticket 序列化为请求 Cookie 的 region_ticket;没有 客户端生成、签名或 native 计算链。
  • RegionInfo 的 APK 预置资源只有 API group/host 路由映射,没有 ticket。
  • 全部 7 个 HAR 共 8334 entries响应 region.ticket=0、响应 Set-Cookie=0、请求 Cookie 命中 362共 12 个唯一票据,全部为 RT_ + 73 hex、总长 76。抓包开始时状态已经存在未覆盖首次签发。
  • 结论:纯 Python 应实现服务端 Region 的接收、按 UID 保存和复用;没有 本地等价生成算法。缺少该 Cookie 是 APP/CLI 差异,但不能据此单独解释 705。
  • 详见 docs/region_ticket_static_chain.md

2026-07-28 - keyconfig 与登录 query 必须分开建模

  • APP 最终登录请求的 query 不包含 client_key/os;这两个字段由登录接口的 form body 承载,因此 login_api_params() 已正确排除它们。
  • system/keyconfig 与登录接口只共享设备参数子集。APP 抓包显示 keyconfig query 必须包含 client_key=2ac2a76dos=android
  • 此前 refresh_region_ticket() 直接复用 login_api_params(),登录参数对齐后 意外导致 keyconfig 丢失上述两个字段,服务端 HTTP 200 但响应不含 Region。
  • 已新增独立 keyconfig_api_params(),仅为 keyconfig 补回 client_key/os 同时保持 oaid/countryCode/sid/deviceName 不进入该 query。
  • APP 成功样本没有调用 /rest/zt/pass/refresh/anonymousToken,该接口不是当前 短信登录链路的前置条件。
  • 修复只解释最新日志中的 region_ticket present=False。历史实测在已有有效 Region、KAW/KAS、VIMG 时仍可能返回 705,该部分仍属于服务端设备风险判定, 不能用本次 keyconfig 修复宣称已经解决。

2026-07-28 - 705 验证完成后的 captcha_token 重放

  • jlm.a.execute() 是 APP 的通用 Retrofit Call 包装器;其字段 f176918d 非空时会把值以 captcha_token 追加到 FormBody/MultipartBody 然后执行被包装的原请求。
  • 浏览器链路先由 kSecretApiVerify 返回 captchaToken,再用该 token 调用 /rest/wd/captcha/verify 完成 challenge 绑定。APP 随后不是只同步 Cookie 而是把同一个 token 注入原 API 表单并重新经过签名拦截器。
  • CLI 旧实现已经捕获 token_complete_captcha_handoff() 只返回布尔值, token 在 checker/login 重试前被丢弃;因此重试请求仍没有 captcha_token 服务端返回新的 705/key。
  • mobile_checker()login_by_code() 及其路径包装器现已支持 captcha_token,该字段参与 sig/__NS_sig3/__NS_xfalcon/KAS 的重新计算。
  • system 浏览器模式改为启动独立系统 Edge/Chrome 并通过本地 CDP 观察响应, 保留可见人工滑动窗口,同时能回传 token 和 Cookie。

2026-07-28 - CAPTCHA WebView 设备身份绑定

  • 用户实测已经证明 captcha_token 被捕获并注入原接口,但每次重放仍得到新的 705/key因此“缺少 captcha_token”只是先前缺口不是当前剩余根因。
  • APP 静态链路在 WebView 导航前通过 CookieInjectManager 注入公共参数。 com/yxcorp/gifshow/webview/cookie/f.smali 明确把 sys/appver/did 设为高优先级, und/b.smali 的公共列表还包含 kpn/kpf/userId/c/ver/language/countryCode/mod/net
  • CLI 的临时 Edge/Chrome 画像此前没有预注入 Cookie验证码页会建立 web_* DID /rest/wd/captcha/verify result=1 只证明 Web 挑战成功,不能证明返回 token 与 原 API 的 ANDROID_* DID 属于同一身份。
  • 验证浏览器现会在首次导航前注入 APP 同形态匿名身份 Cookie并在成功响应后 校验浏览器 DID 必须仍等于设备画像 DID。页面若改写为 web_* 或删除 DID CLI 会停止重放并报告身份不一致,避免继续生成无效的新 challenge key。
  • 当前没有证据表明还缺某个普通 HTTP 请求头APP 每次重试本来就会重新生成 X-REQUESTID。后续实测应以 device_identity=matched 和原接口不再返回新 705 作为联合成功条件。

2026-07-28 - 10418 进程状态连续性

  • 最新实测已经达到 /rest/wd/captcha/verify result=1captchaToken 回传、 39 个 Cookie 同步和 device_identity=matched,但重放仍生成新的 705 key 因此普通 header、滑块求解和 Web/Android DID 不一致均不再是首要假设。
  • 代码审计发现每个短信端点都会调用 _sig3_state(session_seed) 创建新对象, 导致 checker、验证码重放、发码和登录的 10418 counter 全部重复从 1 开始。
  • APP 捕获证明 10418 是进程级全局状态:新进程首次可见 counter 约为 0x60 后续请求严格递增seed 也不是固定 0x5d7e742b
  • 捕获的 session_seed=0x6f5d0faa 可由进程启动秒 1785049292 经 Android bionic/BSD srand(time); rand()+1 精确复现,确认了动态 seed 的来源。
  • CLI 现为每次运行创建一份新鲜 Kwsg10418State,默认从启动后基线 0x5f 开始,并在整条短信链共享;--session-seed--sig3-counter 可用于样本回放。

2026-07-28 - 705 重放结构与实际出站诊断

  • 最新实测中动态 sig3 seed/counter 已启用,浏览器绑定仍为 result=1,但 API 重放继续返回新 705因此 sig3 状态重置不是当前剩余根因。
  • APP 的 705 链通过 retryWhen 重新订阅,并在每次订阅时克隆 Retrofit Call NetworkSequenceIdInterceptor 还会重新生成 X-REQUESTID。CLI 每次重放生成 新请求 ID 与 APP 一致,不能复用首次请求 ID。
  • 705 Activity 回调只把 RETURN_RESULT 写入 jlm.a 的 token 字段;随后 jlm.a.execute() 只向原 FormBody 追加 captcha_token。没有 CAPTCHA 专用 query、header 或 cookie因此继续猜测额外验证码字段缺少静态依据。
  • _do_post() 现从 requests.Response.request 读取实际出站请求,脱敏记录: header 名、body 字段顺序、Cookie 名、captcha token 长度/摘要、请求 ID、 sig3 seed/counter 和 HTTP 版本。不会记录手机号、Cookie 值或 token 明文。
  • 浏览器绑定成功日志也打印同算法的 token SHA-256 短摘要;它应与随后 PreparedRequest 中的摘要一致,从而直接验证 token 交接未被替换或损坏。
  • 当前 CLI 使用 requests,通常通过 HTTP/1.1 和 Python/OpenSSL 指纹访问APP 使用 OkHttp/BoringSSL 且可协商 HTTP/2。若下一次诊断确认 token/body/Cookie 均正确但仍为 HTTP/1.1,传输指纹将成为优先假设,但尚未用同请求 A/B 实测证明。

2026-07-28 - 705 传输层 A/B

  • 用户最新日志证明浏览器绑定 token 与实际 POST 中的 captcha_token 摘要完全 一致Region、KAW/KAS 和动态 sig3 counter 也都存在;应用层交接已闭环。
  • 静态确认 APP 的 OkHttp 默认协议顺序是 HTTP/2、HTTP/1.1,且 Aegon 的 CronetInterceptor 可能接管请求并动态选择 QUIC/H2/TCP所以 APP 不是简单的 “固定 OkHttp HTTP/2”。
  • 登录通用链不会添加 Accept。原 requests.Session 自动携带的 Accept: */* 是已确认的出站差异。
  • 新增显式 --transport okhttp4-android10 A/B 路径,使用 curl_cffi 官方文档的 OkHttp 4 Android 10 JA3/Akamai 参考配置,并优先协商 HTTP/2默认仍保留 requests,便于同一业务输入做单变量比较。
  • HTTP/2 适配层移除 framing 层禁止的 Connection,关闭 curl_cffi 浏览器默认 headers保留现有表单原始字节、CookieJar、签名和验证码 token。
  • 该参考配置不等于 APP 的精确 Aegon/Cronet 指纹。若 A/B 仍返回 705应采集 APP 实际 ALPN/JA3/HTTP2 SETTINGS 后再替换 profile不能据此倒推缺业务字段。

每执行2次查看/浏览器/搜索操作后更新此文件 防止视觉信息丢失