This commit is contained in:
youfak 2026-08-27 16:23:29 +08:00
parent 5a94ac454e
commit 4b342ff9c2
3 changed files with 7056 additions and 0 deletions

View File

@ -302,6 +302,180 @@
- 该参考配置不等于 APP 的精确 Aegon/Cronet 指纹。若 A/B 仍返回 705应采集
APP 实际 ALPN/JA3/HTTP2 SETTINGS 后再替换 profile不能据此倒推缺业务字段。
## 2026-08-27 - 签到/签到看视频 H5 链路 → ksjsb.py
- 新抓包 `签到_2026_08_27_14_50_32.har`(428 条)/`签到看视频_2026_08_27_14_51_46.har`
(307 条),App ksNebula/14.6.50.11735、Yoda WebView Chrome/121、userId=5402308233。
- 签到链(全部 Yoda WebView H5,非原生 API):
- `overview/popup` 响应 `data.unionSignInPopup.button.eventTrackingLogInfo`
(deliverOrderId=5118, EXTERNAL_SIGN_POPUP)是 `signIn/report`
`eventTrackingLogInfo` 来源;上报前在 `extParams` 追加 `source`
- `signIn/report` 为 GET + query eventTrackingLogInfo,响应
`reportRewardResult.eventTrackingAwardInfo.awardInfo[].amount` 即奖励。
- 签到看视频链:`signIn/showPopup` 响应按钮(text=去看视频)的
`eventTrackingLogInfo`(deliverOrderId=2044, SIGN_IN_BUTTON_60038_RPC,
businessPriceUniqueId 服务端每次下发)→ `POST
/rest/wd/usergrowth/encourage/matrix/resource/action`
body=`{"actionType":1,"resourceSlotInfo":<slot+source>}`。
- 看视频奖励由第三方 CPX 广告(通义 TONGYI)S2S 回调发放
(`ad.partner.gifshow.com/track/activate?callback=...&event_type=84`),
明文抓包中无客户端领奖请求;`businessPriceUniqueId` 是回调绑定键。
- H5 sig3 纯算链对新端点交叉验证:抓包 query+cookie(document.cookie
编码值,如 `mod=OnePlus%28PJZ110%29`)重建 sign_input,CRC32 与
showPopup/report/popup(含 notificationKey)/signIn_resource/matrix action
(POST+JSON body)共 5 类真实 sig3 内嵌值完全一致;counter 进程级
101→121 单调递增。
- `multi-az-hebei-webh5.kuaishou.com` 是抓包代理的域名映射,真实
Host/Referer/Origin 均为 `nebula.kuaishou.com`
- 交付:`ksjsb.py`(独立运行,不读 HAR;复用 core H5 纯算链)+
`tests/test_ksjsb.py`(8 用例:URL/键序黄金值、CRC 交叉验证、body
逐字节一致、sig3 往返+counter 递增、dry-run 无网络冒烟)。
## 2026-08-27 - 开宝箱/看广告 HAR → ksjsb.py 重写为顺序任务脚本
- 新增抓包: `开宝箱_15_01_21`、`开宝箱看广告_15_02_14`、`看广告得金币+打开
广告详情_15_04_32`(与上午同设备/账号会话,sig3 counter 连续递增到 162)。
- 宝箱冷却规则(treasureBox/info 响应): `remainSeconds=0 & status=3` 可开,
`remainSeconds=300 & status=2` 冷却中; `rewardCount` 当前宝箱金币。
- 开宝箱链: 任务列表宝箱挂件位(dailyTasks[].extParam.eventTrackingLogInfo,
taskId 20022)→ matrix/resource action(ext 追加 threshold/thresholdTaskId/
source)→ treasureBox/report(body 复用 info 响应 eventTrackingLogInfo
(taskId 20035)+source); 响应 `data.title.rewardCount` 即到账金币,
开箱后按钮 linkUrl(base64)=matrixMixedAd 广告跳转参数
(11101/100024064/20346/606,与 main.py AD_FETCH_FALLBACK_SCENE 一致)。
- 看广告得金币: overview/tasks dailyTasks[0](TASK_LIST_17_672_CD_PROGRESSING)
的 eventTrackingLogInfo 先做 matrix 点击上报, 再走原生广告链(拉取/播放/
上报全部在 tm-s.xxpkg.com:13500 隧道内,明文不可见;复用 main.py 已在线
验证的 reward/mixed/ad + ad/task/report 机器)。"打开广告详情"为第三方
广告 SDK 落地行为(meituan/qianwen),不在脚本范围。
- CRC 交叉验证新增 6 端点全对: treasureBox/info(可开/冷却两种状态)、
treasureBox/report(POST+JSON body)、matrix action(宝箱点击/开箱后/
任务点击)。body 键序细节: matrix 点击的 extParams 中 threshold/
thresholdTaskId 在 source 之前(抓包 #115)。
- `ksjsb.py` 重写为无 CLI 顺序任务脚本: 余额 → task_list(广告上下文)→
签到(showPopup 查 todaySigned,未签才 report)→ 开宝箱(冷却检查)→
看广告 random(20,30) 次(冷却等待重试,拉取失败提前结束,每 5 次刷新
任务列表)→ 最终余额。复用 main.KsNebulaClient 全部机器,子类 KsJsB
注册 2026-08-27 新指纹 header 模板(Chrome/121, per-run PxSessionId)。
- `tests/test_ksjsb.py` 重写为 11 用例: 6 个 URL/body 黄金值、7 端点 CRC
交叉验证、sig3 往返+counter 递变、资源位提取、顺序流程 dry-run 冒烟。
## 2026-08-27 - ksjsb.py 改为完全单文件(零项目内依赖)
- 用户要求: 不依赖 main.py/core,以 HAR 为准。重写为单文件(623KB),
唯一依赖 `requests`;零 `main`/`core` import(已验证)。
- 关键工程点: KWSG T1/T2 分组密码表(A=10400/true-mode sign, B=10418
sig3)无法纯算重建,以 base64 内嵌(147456+41216 字节 ×2);其余算法
全部内联: 10400 ZT envelope、10418 digest/state/时间驱动 seed、reward
sign、xfalcon(自定义 IV BLAKE2s + $TE_)、Weapon kaw/kas、KWF AES+
自定义 base64 kww、WebWeapon kws 票据、H5 sig3 envelope+双层 SHA、
API sig/tokensig/signed_api_url、reward strE、广告素材提取/上报 biz。
- 算法黄金值回归(与 core 逐位一致): 4 张表==bin 文件、10418 digest、
reward sign、10400 raw、report encData wrapper、xfalcon value、H5
sign_input/digest field/from_fields、kww、kas、signed_api_url(含
sig3; 需固定 unix_time 才可黄金化,已加透传参数)。
- H5 sig3 counter 依 2026-08-27 抓包改为递增(100+rand 起步,真机
101→162);elapsed=(开机秒<<16)|ms;state=0x10000|16bit 随机;seed
每次运行随机(新页面会话)。
- 传输层差异(已标注 TODO): API 请求用 requests/HTTP1.1,真机为
OkHttp/Cronet;Accept-Encoding 降级 gzip,deflate(无 brotli)。
- `tests/test_ksjsb.py` 只 import ksjsb(17 用例,单文件自洽证明):
6 URL/body 黄金值、6 端点摘要字段交叉验证、5 组算法黄金值、
sig3 往返+counter 递增、资源位/场景提取、dry-run 全流程冒烟。
全套件 289 用例,仍只有 4 个存量失败(test_main 导入、sms_login
captcha auto_solve 签名),无新增。
## 2026-08-27 - 在线首跑复盘 + 诊断日志
- 用户实测(账号 userId=1579452490/vivo): basicInfo/task_list OK;宝箱
matrix 点击 result=6001 但 treasureBox/report **开箱成功 +539 金币**
(证明点击上报非必需);广告 matrix 点击与 reward/mixed/ad 拉取均
result=6001,循环终止;签到 showPopup 无资源位(弹窗已关闭场景)。
- 历史抓包中 matrix/ad fetch 全部 result=1,6001 为脚本请求特有的
业务/风控拒绝;jadx/HAR 中无 6001 定义。候选根因:当日广告任务已完成
(该账号今日已重度使用)/传输指纹(requests HTTP1.1)/uQaTag 为 7 月
旧设备样本。待用诊断日志定位。
- 脚本改进:
1. 每请求 JSONL 诊断日志(out/ksjsb_replay_*.jsonl,完整 URL/body/
响应,仅脱敏 api_st/token 等登录票据;KS_RECORD_FILE=0 关闭)。
2. 签到判定: showPopup.todaySigned -> 弹窗关闭时回退 overview/popup
的 unionSignInPopup 资源位 -> 任务列表 20022/60038 任务在
finishedTaskList 则确认已签。
3. matrix 点击降级 best-effort: 失败一次即停发(避免重复触发风控),
不影响领奖流程。
4. 广告拉取 6001 时输出明确诊断(任务不可用/风控)并终止循环。
5. API body 字段序对齐真机抓包: cs, client_key, videoModelCrowdTag,
os, kuaishou.api_st, uQaTag, token(此前 api_st 在 uQaTag 后)。
## 2026-08-27 - 6001 根因定位(诊断日志 diff)
- 第二次在线跑(任务可用: 看广告任务 status=1 completed=0)仍 6001,
排除"任务已完成"。JSONL diff 发现两处与真机抓包的实质偏差:
1. **API query 多了 `sid` 和 `deviceName`**: 真机 reward/mixed/ad 与
ad/task/report 的 57 键参数集从不包含这两键(全部历史 HAR 实证);
系 main.py `_api_params` 从 cookie 盲目带入的偏差。已移除并加
参数集回归测试。
2. **`uQaTag` 设备错配**: 旧值 `DP:3hX9ONf4GgQ...` 是 7 月 OnePlus
设备的 QoE 采集态标签,现配 vivo 设备身份。2026-07-08 抓包实证
QoE 未采集的合法回退形态 `1##DP:null#ecBl:00#ecPp:--#cmNt:-1#
cmMnsl:-0` 被 result=1 接受且设备无关 —— 已改为默认值,
`KS_UQA_TAG` 可覆盖。7 月 main.py 在线验证通过时账号设备恰好与
uQaTag 同为 OnePlus,佐证设备绑定假设。
- matrix 点击 6001 与此独立(H5 端点,无 uQaTag);保持 best-effort。
- 待用户下次在线验证两处修复是否消除 ad fetch 6001。
## 2026-08-27 - 6001 第三轮: 传输指纹 + 设备一致性修复
- 第二轮修复(去 sid/deviceName、uQaTag null 形态)经日志确认已生效但
ad fetch 仍 6001 —— 排除参数集/标签问题,剩余嫌疑集中在传输指纹与
设备一致性。第三轮修复:
1. API 请求默认走 **OkHttp4/Android10 TLS 指纹 + HTTP/2**
(curl_cffi 0.15 已内联封装, 不可用时回退 requests,
KS_API_TRANSPORT=requests 可强制 A/B)。
2. `X-Client-Info.model` 从 cookie `mod` 括号内型号动态生成
(vivo(V2408A)->V2408A), 修复此前硬编码 PJZ110 与 vivo 设备的
头部错配。
3. 签名参数追加序对齐抓包: sig, __NS_sig3, __NS_xfalcon,
__NStokensig(此前 tokensig 在 xfalcon 前)。
- 黄金值回归: signed_api_url 的 sig/sig3 值不受参数序调整影响(逐位
不变); 19 项单文件测试 + 全套件 291 用例无新增失败。
- 待第四轮在线验证。若 okhttp 指纹下仍 6001, 剩余候选:
strE 内嵌 network_ip(硬编码 172.31.230.147, 可 KS_AD_NETWORK_IP
覆盖为出口公网 IP)、egid↔did 绑定状态、10418 seed/counter 模式。
## 2026-08-27 - 6001 第四轮: 版本对齐 + API 金丝雀探针
- okhttp 指纹 + X-Client-Info 修复后仍 6001。第三轮日志排查发现:
`appver=14.5.50.11631/ver=14.5`(cookie 7 月导出的旧版本号, 真机
当前 14.6.50.11735)与 `kcv=1627`(默认值, cookie 实为 1630)。
- 修复: API `appver/ver` 默认对齐 2026-08-27 抓包版本(14.6.50.11735,
KS_APPVER 可覆盖), `kcv` 改从 cookie 覆盖; strE appInfo.version 随
api_params 自动对齐。
- 新增 **styleTemplate 金丝雀探针**(`/rest/e/load/styleTemplate`,
2026-07 抓包实证 result=1): 同传输/sig3/xfalcon 管线但无鉴权
token、无加密体, 真机形态连 __NStokensig 都不带(已按抓包实现)。
广告步骤先跑探针 —— 探针过而 ad fetch 败 => 投放层拒绝
(api_st/strE/任务绑定); 探针也败 => 传输/签名层。
- fetch_ad 的 JSONL notes 现记录 strE 明文/scene/businessId/
neoParamsLen, 下次失败可直接检查加密体内容。
## 2026-08-27 - 6001 结案: 根因=旧 api_st; gzip 解压为最后一坑
- 用户用 `tools/sms_login_cli.py` 重新登录获得新鲜 api_st/h5_st/设备
(did=ANDROID_63fdacb5388e2572 + 在线注册 egid)并更新 .env 后:
- styleTemplate 探针 HTTP 200 + 12.7KB 模板 —— **实为成功**,此前
`result=None` 是 curl_cffi(okhttp 指纹)手动 `Accept-Encoding: gzip`
时 libcurl 不自动解压导致 `json()` 失败的解析 bug。
- 修复: `_request``\x1f\x8b` 魔数兜底 `gzip.decompress`
(requests 自动解压不受影响)。
- **广告全链路打通**: fetch result=1(16KB gzip 素材, 19.1s)→
按素材时长等待 → report result=1 `neoAmount:497金币` 实际到账。
- 6001 根因链(多因叠加, 逐轮排除): 旧 cookie 的 api_st 过期(H5 用
h5_st 所以全通, API 全挂)+ 传输指纹 + 版本号滞后 + uQaTag 设备
错配 + 参数集多余键。新会话 + okhttp 指纹 + 版本对齐后消除。
- 端到端验证后 19+291 测试回归无新增失败。诊断体系(JSONL 探针/
strE notes)保留, 供后续问题定位。
---
*每执行2次查看/浏览器/搜索操作后更新此文件*
*防止视觉信息丢失*

6519
ksjsb.py Normal file

File diff suppressed because it is too large Load Diff

363
tests/test_ksjsb.py Normal file
View File

@ -0,0 +1,363 @@
# -*- coding: utf-8 -*-
"""ksjsb.py 单文件脚本的行为锁定测试。
import ksjsb 一个项目文件(证明单文件自洽), 黄金值以字面量固化, 来自:
- 2026-08-27 五份抓包(签到/签到看视频/开宝箱/开宝箱看广告/看广告得金币):
URL query 键序编码JSON body真实 sig3 内嵌摘要字段(CRC 交叉验证)
- core/ 算法模块的已知输出(10418/10400/xfalcon/H5 sig3/kww/kas 黄金值)
ksjsb.py 运行时不读取 HAR, 也不依赖 main/core; 本测试同样只依赖 ksjsb
"""
import io
import contextlib
import os
import unittest
import urllib.parse
from ksjsb import (
KsJsB,
build_h5_sign_input,
compact_json,
h5_sig3_digest_field,
parse_h5_sig3,
with_source,
)
# 抓包 cookie 中参与 sign_input 的子集(H5_COOKIE_KEYS, 非敏感设备字段)。
# 真实 WebView 对 document.cookie 的编码值(%28/+ 等)签名, 这里保持原样。
CAPTURED_COOKIE = (
"kpn=NEBULA; kpf=ANDROID_PHONE; userId=5402308233; did=ANDROID_f05497e9cef09a7f; "
"c=OPPO; appver=14.6.50.11735; language=zh-cn; mod=OnePlus%28PJZ110%29; did_tag=0; "
"egid=DFP280DA456E3BBF93A99727CB5A97D727E442E38694913A197D1C201C5CF982; "
"oDid=ANDROID_46a032e0a2af8184; androidApiLevel=36; newOc=OPPO; browseType=3; "
"socName=Qualcomm+Snapdragon+8750; ftt=; abi=arm64; userRecoBit=0; device_abi=arm64; "
"grant_browse_type=AUTHORIZED; iuid=; rdid=ANDROID_741de4351c44850d; "
"oaid=700ED68650EF49ECB686B2AAA9994C1Dca31c779ee5acb7d4c3b03337599a591"
)
# ---- 真实请求 query(去掉 __NS_sig3, 保留原始顺序) ----
SHOW_POPUP_QUERY = (
"sigCatVer=1&source=bottom_guide_first&ladderRedPackGuide=false"
"&bizParams=%7B%22source%22%3A%22bottom_guide_first%22%2C%22ladderRedPackGuide%22%3Afalse%7D"
)
REPORT_QUERY = (
"sigCatVer=1&eventTrackingLogInfo=%7B%22deliverOrderId%22%3A%225118%22%2C%22eventTrackingTaskId%22"
"%3A20022%2C%22resourceId%22%3A%22externalFeed_externalSignPopup%22%2C%22extParams%22%3A%7B%22signIn"
"ThresholdTaskType%22%3A0%2C%22autoSignInSuccess%22%3Afalse%2C%22source%22%3A%22bottom_guide_first%22"
"%7D%2C%22resourceFirstLevelName%22%3A%22EXTERNAL_FEED%22%2C%22resourceSecondLevelName%22%3A"
"%22EXTERNAL_SIGN_POPUP%22%7D"
)
TREASURE_INFO_QUERY = "sigCatVer=1&source=bottom_guide_first"
# ---- 真实 sig3 样本(摘要字段交叉验证) ----
CAPTURED_SIG3 = {
"show_popup": "4151162679c8e853bd1d741e19181571a0bed5eefa41a30358df0e0e08080b0a3515",
"sign_in_report": "9d8dcafaa514348f61c1a9c2c5c474fbd628113d269d7fdf1771d2d2d4d4d7d6e9c9",
"treasure_info": "5b4b0c3c63d2f249a70780040302289a4b2effd11e58b9193b6f1414121211102f0f",
"treasure_report": "8797d0e0bf0e2e957bdb5ed8dfde440eb392cd11c28465c56be7c8c8cececdccf3d3",
"matrix_treasure": "9181c6f6a91838836dcd4fcec9c883c4a4d83813d49273d389d3deded8d8dbdae5c5",
"matrix_task": "d0c087b7e85979c22c8c2c8f8889dcd94f6e33ba97d332924b7d9f9f99999a9ba484",
}
TREASURE_TRACKING = {
"eventTrackingTaskId": 20035,
"resourceId": "externalFeed_externalTreasureChestWidget",
"extParams": {"isServerRecordClickAction": True},
"deliverOrderId": "5128",
"resourceFirstLevelName": "EXTERNAL_FEED",
"resourceSecondLevelName": "EXTERNAL_TREASURE_CHEST_WIDGET",
}
TASKLIST_TREASURE_SLOT = {
"eventTrackingTaskId": 20022,
"resourceId": "externalFeed_externalTreasureChestWidget",
"extParams": {"isServerRecordClickAction": True},
"deliverOrderId": "5128",
"resourceFirstLevelName": "EXTERNAL_FEED",
"resourceSecondLevelName": "EXTERNAL_TREASURE_CHEST_WIDGET",
}
TASKLIST_AD_SLOT = {
"deliverOrderId": "422",
"materialKey": "TASK_LIST_17_672_CD_PROGRESSING",
"eventTrackingTaskId": 17,
"resourceId": "earnPage_taskList_1",
"extParams": {
"isServerRecordClickAction": True,
"businessPriceUniqueId": "EPuAgIDgi5CfChiuBCDBxpyQhDQo2O7EtgM=",
},
"resourceFirstLevelName": "EARN_PAGE",
"resourceSecondLevelName": "TASK_LIST",
}
CAPTURED_OAID = "700ED68650EF49ECB686B2AAA9994C1Dca31c779ee5acb7d4c3b03337599a591"
GOLDEN_TREASURE_BODY = (
'{"imei":"","idfa":"","oaid":"' + CAPTURED_OAID + '","eventTrackingLogInfo":"{\\"eventTrackingTaskId\\":20035,'
'\\"resourceId\\":\\"externalFeed_externalTreasureChestWidget\\",\\"extParams\\":{\\"isServerRecordClickAction\\":true,'
'\\"source\\":\\"bottom_guide_first\\"},\\"deliverOrderId\\":\\"5128\\",\\"resourceFirstLevelName\\":\\"EXTERNAL_FEED\\",'
'\\"resourceSecondLevelName\\":\\"EXTERNAL_TREASURE_CHEST_WIDGET\\"}"}'
)
GOLDEN_MATRIX_TREASURE_BODY = (
'{"actionType":1,"resourceSlotInfo":{"eventTrackingTaskId":20022,'
'"resourceId":"externalFeed_externalTreasureChestWidget","extParams":{"isServerRecordClickAction":true,'
'"threshold":0,"thresholdTaskId":0,"source":"bottom_guide_first"},"deliverOrderId":"5128",'
'"resourceFirstLevelName":"EXTERNAL_FEED","resourceSecondLevelName":"EXTERNAL_TREASURE_CHEST_WIDGET"}}'
)
GOLDEN_MATRIX_AD_BODY = (
'{"actionType":1,"resourceSlotInfo":{"deliverOrderId":"422","materialKey":"TASK_LIST_17_672_CD_PROGRESSING",'
'"eventTrackingTaskId":17,"resourceId":"earnPage_taskList_1","extParams":{"isServerRecordClickAction":true,'
'"businessPriceUniqueId":"EPuAgIDgi5CfChiuBCDBxpyQhDQo2O7EtgM=","source":"bottom_guide_first"},'
'"resourceFirstLevelName":"EARN_PAGE","resourceSecondLevelName":"TASK_LIST"}}'
)
def make_client() -> KsJsB:
return KsJsB(CAPTURED_COOKIE, "", timeout=5, dry_run=True)
def query_without_sig3(url: str) -> str:
return "&".join(p for p in url.split("?", 1)[1].split("&") if not p.startswith("__NS_sig3="))
class UrlAndBodyTests(unittest.TestCase):
def test_show_popup_query_matches_capture(self):
self.assertEqual(query_without_sig3(make_client().sign_in_show_popup().url), SHOW_POPUP_QUERY)
def test_sign_in_report_query_matches_capture(self):
tracking = {
"deliverOrderId": "5118",
"eventTrackingTaskId": 20022,
"resourceId": "externalFeed_externalSignPopup",
"extParams": {"signInThresholdTaskType": 0, "autoSignInSuccess": False},
"resourceFirstLevelName": "EXTERNAL_FEED",
"resourceSecondLevelName": "EXTERNAL_SIGN_POPUP",
}
self.assertEqual(query_without_sig3(make_client().sign_in_report(tracking).url), REPORT_QUERY)
def test_treasure_info_query_matches_capture(self):
self.assertEqual(query_without_sig3(make_client().treasure_box_info().url), TREASURE_INFO_QUERY)
def test_treasure_report_body_matches_capture(self):
self.assertEqual(
compact_json({
"imei": "",
"idfa": "",
"oaid": CAPTURED_OAID,
"eventTrackingLogInfo": compact_json(with_source(TREASURE_TRACKING)),
}),
GOLDEN_TREASURE_BODY,
)
record = make_client().treasure_box_report(TREASURE_TRACKING)
self.assertEqual(query_without_sig3(record.url), "sigCatVer=1")
def test_matrix_treasure_body_matches_capture(self):
resource = dict(TASKLIST_TREASURE_SLOT)
ext = dict(resource["extParams"])
ext.setdefault("threshold", 0)
ext.setdefault("thresholdTaskId", 0)
resource["extParams"] = ext
self.assertEqual(compact_json({"actionType": 1, "resourceSlotInfo": with_source(resource)}), GOLDEN_MATRIX_TREASURE_BODY)
def test_matrix_ad_body_matches_capture(self):
self.assertEqual(compact_json({"actionType": 1, "resourceSlotInfo": with_source(TASKLIST_AD_SLOT)}), GOLDEN_MATRIX_AD_BODY)
class Sig3CrossValidationTests(unittest.TestCase):
"""抓包 query/body + cookie 重建 sign_input, 摘要字段与真实 sig3 内嵌值一致。"""
def test_captured_digest_fields(self):
cases = {
"show_popup": (dict(urllib.parse.parse_qsl(SHOW_POPUP_QUERY, keep_blank_values=True)), None, "GET"),
"sign_in_report": (dict(urllib.parse.parse_qsl(REPORT_QUERY, keep_blank_values=True)), None, "GET"),
"treasure_info": (dict(urllib.parse.parse_qsl(TREASURE_INFO_QUERY, keep_blank_values=True)), None, "GET"),
"treasure_report": ({"sigCatVer": "1"}, GOLDEN_TREASURE_BODY, "POST"),
"matrix_treasure": ({"sigCatVer": "1"}, GOLDEN_MATRIX_TREASURE_BODY, "POST"),
"matrix_task": ({"sigCatVer": "1"}, GOLDEN_MATRIX_AD_BODY, "POST"),
}
cookie = dict(part.split("=", 1) for part in CAPTURED_COOKIE.split("; "))
for label, (query, body, method) in cases.items():
with self.subTest(label=label):
sign_input = build_h5_sign_input(cookie, query=query, body=body, method=method, request_type="json")
calc = h5_sig3_digest_field(sign_input)
captured = parse_h5_sig3(CAPTURED_SIG3[label])["digest_field"]
self.assertEqual(calc, captured)
def test_generated_sig3_roundtrip_and_counter_increments(self):
client = make_client()
query = dict(urllib.parse.parse_qsl(SHOW_POPUP_QUERY, keep_blank_values=True))
s1 = client._h5_sig3(query=query)
s2 = client._h5_sig3(query=query)
f1, f2 = parse_h5_sig3(s1), parse_h5_sig3(s2)
cookie = dict(part.split("=", 1) for part in CAPTURED_COOKIE.split("; "))
expected = h5_sig3_digest_field(build_h5_sign_input(cookie, query=query))
self.assertEqual(f1["digest_field"], expected)
self.assertEqual(f2["digest_field"], expected)
# 2026-08-27 抓包: counter 进程级单调递增(101->162)
self.assertEqual(f2["counter"], f1["counter"] + 1)
self.assertEqual(len(s1), 68)
class AlgorithmGoldenTests(unittest.TestCase):
"""内联算法黄金值(与已逆向 core 实现逐位一致的固化输出)。"""
def test_kwsg_10418_digest(self):
from ksjsb import KWSG_10418_SIG3_HMAC_KEY, KWSG_10418_SIG3_PREFIX_CODE, _kwsg_tables, kwsg_10418_digest24, kwsg_10418_prefix8
t1, t2 = _kwsg_tables("B")
got = kwsg_10418_digest24(
"test-input-中文-10418".encode(), 7, 1787813423, t1, t2,
prefix8=kwsg_10418_prefix8(KWSG_10418_SIG3_PREFIX_CODE, 0x0610FDA7),
hmac_key=KWSG_10418_SIG3_HMAC_KEY,
).hex()
self.assertEqual(got, "56473214b4ef0116181e1d1cecfaa8cc28d88a6e030f0117")
def test_kwsg_10400_raw(self):
from ksjsb import REWARD_SDK, _kwsg_tables, kwsg_10400_raw
t1, t2 = _kwsg_tables("A")
got = kwsg_10400_raw('{"x":"encData-黄金值"}'.encode(), REWARD_SDK, t1, t2, epoch_seconds=1787813423).hex()
self.assertEqual(
got,
"5a54eecde4d4ea6193f79db96e3254547f6c770401437d63823030fad0f3d66671d4550214754963c06e257b78857c73a4fff39f4b37ea8996c1b0878f69cef97898e2bebf872583",
)
def test_xfalcon_value(self):
from ksjsb import xfalcon_value_from_input_bytes
value = xfalcon_value_from_input_bytes(b"/rest/e/reward/mixed/adclient_key=2ac2a76dos=android")
self.assertTrue(value.startswith("HUDR_sFnX+n5uAUNVsMPNK3DOP5wnti1Lc8Axjy5z88T61A==$TE_"))
self.assertEqual(
value.split("$TE_")[1],
"1d029bfdfd210e0c6c7356210e0c6c7356575154566189cefaec418425e348f87e0fdf570c00688c2d0068ad56",
)
def test_kww_generation(self):
from ksjsb import kwf_generate_kww
kww = kwf_generate_kww(3, 1787813423000)
self.assertTrue(kww.startswith("PnGU+9+Y8008"))
self.assertEqual(
kww,
"PnGU+9+Y8008S+nH0U+0mjPf8fP08f+98f+nLlwnrIP9Pl+9rh+/WA+/mD+9PAPAHh+/Z7G/SD8e+DPBLh8fzS8/rU8/SYGAPAP/GMPBr7+0pSGn8DweDUGAPAPepDG/qAG9+0w/LlG/40+Bz0+nzDweLM+0P7GfzDPe4jG0LA+/P=",
)
def test_weapon_kas(self):
from ksjsb import weapon_proof_headers
headers = weapon_proof_headers("https://api.e.kuaishou.com/rest/e/reward/mixed/ad?__NS_sig3=abcd", "kaw-value")
self.assertEqual(headers["kas"], "00418873e6dae68db455e61dca1fdfd426")
def test_signed_api_url_sig3(self):
from ksjsb import Kwsg10418State, signed_api_url
state = Kwsg10418State(session_seed=0x0610FDA7, counter=0x60)
url = signed_api_url(
"/rest/e/reward/mixed/ad",
{"client_key": "2ac2a76d", "os": "android", "ud": "5402308233"},
[("encData", "AAA"), ("kuaishou.api_st", "ST")],
"salt",
state,
unix_time=1787813423,
)
self.assertIn("sig=4df442297f332ac4bce562f7b720229c", url)
self.assertIn("__NS_sig3=8d9ce9cf6f34dacda5c5c6c70c09b185f30351b5d8d4dacc", url)
self.assertIn("__NStokensig=7e2cf6b51f8e8b86ea855ee1ed3e0be550eba81d5ef7effc76a54216fb65f2fa", url)
self.assertIn("__NS_xfalcon=", url)
class ApiParamSetTests(unittest.TestCase):
"""API query 参数集与真机抓包逐键一致(2026-08-27 在线 6001 排查结论)。"""
CAPTURED_AD_QUERY_KEYS = [
"earphoneMode", "mod", "appver", "isp", "language", "ud", "did_tag", "egid",
"thermal", "net", "kcv", "app", "kpf", "bottom_navigation", "ver", "android_os",
"oDid", "boardPlatform", "kpn", "newOc", "androidApiLevel", "slh", "country_code",
"nbh", "hotfix_ver", "did_gt", "keyconfig_state", "cdid_tag", "sys", "max_memory",
"cold_launch_time_ms", "oc", "sh", "deviceBit", "browseType", "ddpi", "socName",
"is_background", "c", "sw", "ftt", "apptype", "abi", "userRecoBit", "device_abi",
"icaver", "totalMemory", "grant_browse_type", "iuid", "rdid", "sbh", "darkMode",
"did",
]
def test_ad_fetch_query_keys_match_capture(self):
client = make_client()
params = client._api_params()
# 真机广告请求: 上述设备键 + sig/__NS_sig3/__NS_xfalcon/__NStokensig
self.assertNotIn("sid", params) # 真机广告请求从不携带(2026-07 抓包实证)
self.assertNotIn("deviceName", params)
expected = set(self.CAPTURED_AD_QUERY_KEYS)
self.assertTrue(expected.issubset(params), f"缺失: {expected - set(params)}")
def test_uqatag_default_is_device_neutral_null_variant(self):
"""uQaTag 默认用 QoE 未采集回退形态(2026-07-08 抓包 result=1 接受)。"""
from ksjsb import API_COMMON_BODY
self.assertEqual(API_COMMON_BODY["uQaTag"], "1##DP:null#ecBl:00#ecPp:--#cmNt:-1#cmMnsl:-0")
client = make_client()
pairs = dict(client._api_body_common())
self.assertEqual(pairs["uQaTag"], API_COMMON_BODY["uQaTag"])
# 字段序对齐真机: cs, client_key, videoModelCrowdTag, os, api_st, uQaTag, token
self.assertEqual(
[k for k, _ in client._api_body_common()],
["cs", "client_key", "videoModelCrowdTag", "os", "kuaishou.api_st", "uQaTag", "token"],
)
class WithSourceTests(unittest.TestCase):
def test_appends_source_and_preserves_key_order(self):
tracking = dict(TREASURE_TRACKING)
out = with_source(tracking)
self.assertEqual(list(out.keys()), list(tracking.keys()))
self.assertEqual(list(out["extParams"].keys()), ["isServerRecordClickAction", "source"])
self.assertNotIn("source", tracking["extParams"])
class TaskSlotTests(unittest.TestCase):
def test_update_ad_contexts_extracts_slots_and_scene(self):
import base64 as b64
link_url = b64.b64encode(
compact_json({"pageId": 11101, "subPageId": 100026367, "posId": 24067, "businessId": 672}).encode()
).decode()
client = make_client()
client._update_ad_contexts({
"data": {
"dailyTasks": [
{"eventTrackingLogInfo": TASKLIST_AD_SLOT, "linkUrl": link_url, "extParam": {"descCountDown": 30000}},
{"extParam": {"eventTrackingLogInfo": TASKLIST_TREASURE_SLOT}},
]
}
})
self.assertEqual(client.task_ad_slot, TASKLIST_AD_SLOT)
self.assertEqual(client.task_treasure_slot, TASKLIST_TREASURE_SLOT)
self.assertEqual(client.ad_fetch_scenes["normal"], (11101, 100026367, 24067))
self.assertEqual(client.ad_task_countdown_ms["normal"], 30000)
class SequentialRunTests(unittest.TestCase):
def test_run_dry_run_completes_all_steps_without_network(self):
from ksjsb import run
env = {"ksck": f"test#{CAPTURED_COOKIE}#salt", "KS_DRY_RUN": "1", "KS_TIMEOUT": "5"}
buf = io.StringIO()
old = {k: os.environ.get(k) for k in env}
os.environ.update(env)
try:
with contextlib.redirect_stdout(buf):
code = run()
finally:
for k, v in old.items():
if v is None:
os.environ.pop(k, None)
else:
os.environ[k] = v
self.assertEqual(code, 0)
text = buf.getvalue()
for marker in ("1/5 查询金币/余额", "3/5 签到", "4/5 开宝箱", "5/5 看广告得金币", "最终余额", "任务执行完成"):
self.assertIn(marker, text)
self.assertIn("开宝箱 dry-run method=POST", text)
self.assertIn("/rest/e/reward/mixed/ad", text)
self.assertIn("/rest/r/ad/task/report", text)
if __name__ == "__main__":
unittest.main()