luckfun233 commited on
Commit
ee56648
·
1 Parent(s): 5db4eb7

feat: 更新Web端接口文档,补充数美风控升级后的协议细节与失败分类说明

Browse files
docs/shumei-device-id-research.md CHANGED
@@ -1,9 +1,18 @@
1
  # 数美(Shumei)SDK 逆向研究报告
2
 
3
  > 研究目标:DeepSeek Web端登录所需的 device_id 生成算法
4
- > 研究时间:2026-06-09
5
  > 项目位置:d:\ds2api
6
 
 
 
 
 
 
 
 
 
 
7
  ---
8
 
9
  ## 一、SDK 基本信息
@@ -39,6 +48,8 @@ appId (测试环境): wx58f1d4bc57bc28fd / wxd4679ecc940ea6be
39
 
40
  ## 三、device_id 生成完整流程
41
 
 
 
42
  ```
43
  ┌─────────────────────────────────────────────────────┐
44
  │ 1. 初始化 │
@@ -680,3 +691,173 @@ Python 工具依赖 `pycryptodome`(`pip install pycryptodome`)。
680
  `response/fragments/-1` 下的 `{p:"status", v:"FINISHED"}` 是**片段级状态**,
681
  不是整条流结束;把它当成 `response/status` 会让联网模式在搜索阶段提前终止、
682
  正文为空。片段类型与解析规则见 `deepseek-recall-research.md` 附录 C。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # 数美(Shumei)SDK 逆向研究报告
2
 
3
  > 研究目标:DeepSeek Web端登录所需的 device_id 生成算法
4
+ > 研究时间:2026-06-09(初版,旧协议) / 2026-09-25(推翻重写,见 §15–§17)
5
  > 项目位置:d:\ds2api
6
 
7
+ > ⚠️ **阅读指引(重要)**
8
+ > 本文档第一至第十四节记录的是 **2026-06 的旧版协议**(ConfusionInfo + protocol=-1/4),
9
+ > 该协议已于 2026-09 被数美废弃,照其实现在服务端会得到 `1902`、登录会得到
10
+ > `RISK_DEVICE_DETECTED`。**当前生效的协议与项目实现请看:
11
+ > [§15 新版协议](#十五2026-09-风控升级新版-sdkprotocol279)、
12
+ > [§17 风控结构详解(系统视角)](#十七新版风控结构详解系统视角)、
13
+ > [§16 复现手册](#十六复现手册工具链--坑位清单)。**
14
+ > 旧章节仅作历史参考与"变化对比"用。
15
+
16
  ---
17
 
18
  ## 一、SDK 基本信息
 
48
 
49
  ## 三、device_id 生成完整流程
50
 
51
+ > ⚠️ 本节为**旧协议**流程(2026-06)。新版流程见 §15.4,系统视角总览见 §17.1。
52
+
53
  ```
54
  ┌─────────────────────────────────────────────────────┐
55
  │ 1. 初始化 │
 
691
  `response/fragments/-1` 下的 `{p:"status", v:"FINISHED"}` 是**片段级状态**,
692
  不是整条流结束;把它当成 `response/status` 会让联网模式在搜索阶段提前终止、
693
  正文为空。片段类型与解析规则见 `deepseek-recall-research.md` 附录 C。
694
+
695
+ ---
696
+
697
+ ## 十七、新版风控结构详解(系统视角)
698
+
699
+ > 本节回答"这套风控到底是怎么运作的":谁校验谁、校验什么、失败了怎么表现。
700
+ > 协议字段级的细节在 §15,本节讲结构与语义。
701
+
702
+ ### 17.1 参与方与数据流
703
+
704
+ 风控不是"数美单独一道题",而是 **数美签发设备身份 → DeepSeek 侧据此判定风险** 的两段式:
705
+
706
+ ```
707
+ ① 前端(或本项目的 Go 实现)
708
+ │ 采集环境指纹 + 本地 SMID
709
+ ▼
710
+ ② 数美 deviceprofile/v4 ── 校验「数据完整性 + 指纹合理性」
711
+ │ 通过 → 签发 SMID(deviceId)
712
+ │ 不通过 → 1902(数据不合法,需重做)
713
+ ▼
714
+ ③ device_id = "B" + SMID ── 存入 localStorage 复用,登录时随请求体提交
715
+ ▼
716
+ ④ DeepSeek /api/v0/users/login ── 校验「device_id 是否为数美真实签发 + 是否已被标记」
717
+ │ 通过 → biz_code=0,返回 user.token
718
+ │ 不被信任 → biz_code=11 / RISK_DEVICE_DETECTED
719
+ ▼
720
+ ⑤ 拿到 token 后的所有业务接口(会话、PoW、completion、文件…)
721
+ ```
722
+
723
+ 两个关键推论:
724
+ - **②失败(1902)必然导致④失败**。设备身份拿不到,登录就没有可信 device_id。
725
+ - **④失败不一定代表②失败**。设备可能签发成功但已被风控标记(账号维度/设备维度),
726
+ 此时唯一可控手段是换一个设备身份(本项目即"重签一次")。
727
+
728
+ ### 17.2 端点与主机映射
729
+
730
+ | 用途 | 端点 | 说明 |
731
+ |------|------|------|
732
+ | 设备画像上报(Web) | `https://fp-it-acc.portal101.cn/deviceprofile/v4` | DeepSeek web 端使用,本项目走这条 |
733
+ | 数美备用主机族 | `fp-it.fengkongcloud.com` 等 | 换主机无效,报文格式一致;Android 原生 SDK 走独立协议 |
734
+ | 登录 | `https://chat.deepseek.com/api/v0/users/login` | 请求体含 `device_id` + `os` |
735
+ | 设备注册/轮换 | `check_device`(登录后调用) | 真实客户端会调,含 `rotate` 语义(本项目未实现) |
736
+
737
+ ### 17.3 device_id 生命周期与失败分类
738
+
739
+ ```
740
+ [无 device_id] --签发--> [已签发 SMID 落盘] --复用--> 每次登录沿用同一个
741
+ │
742
+ └--登录返回 RISK_DEVICE_DETECTED--> 重签一次并覆盖
743
+ ```
744
+
745
+ | 现象 | 层级 | 含义 | 处理 |
746
+ |------|------|------|------|
747
+ | 数美返回 `code=1100` | ② | 画像合法,返回 `detail.deviceId` | 取 `"B"+deviceId` 使用 |
748
+ | 数美返回 `code=1902` | ② | 数据不合法(协议号/字段集/tn/编码不对) | 修正构造逻辑后重试(本项目自动重试 3 次) |
749
+ | 登录 `biz_code=0` | ④ | 设备可信,登录成功 | 保存 `user.token` |
750
+ | 登录 `biz_code=11` / `RISK_DEVICE_DETECTED` | ④ | 设备身份不可信 | 重签 device_id 后重试一次 |
751
+ | 登录 `missing login token` | ④ | `biz_code=0` 但 `biz_data.user.token` 为空 | 通常也是风控拦截的表现,按风险失败处理 |
752
+
753
+ > 注意:`missing login token` 与 `RISK_DEVICE_DETECTED` 是**同一根因的两种表现**,
754
+ > 前者出现在响应结构异常(如 android 端),后者出现在业务码明确拒绝时(如 web 端)。
755
+
756
+ ### 17.4 完整字段清单(80 项)
757
+
758
+ 明文的 58 项 + ��密的 22 项。加密字段的密钥见 §15.2 与
759
+ `internal/shumei/confusion.go` 的 `fieldDESKey`。
760
+
761
+ **① 基础字段(9 项,身份与版本)**
762
+
763
+ | 字段 | 明文值 | 加密 |
764
+ |------|--------|------|
765
+ | `protocol` | `279` | 明文 |
766
+ | `cm` | organization `P9usCUBauxft8eAmUXaZ` | ✓ |
767
+ | `og` | appId `default` | ✓ |
768
+ | `ki` | `web` | ✓ |
769
+ | `version` | `3.0.0` | 明文 |
770
+ | `ek` | sdkver `3.0.0` | ✓ |
771
+ | `yp` | subVersion `1.0.0` | ✓ |
772
+ | `xy` | rtype `all` | ✓ |
773
+ | `kd` | 服务端下发 SMID(首次为空) | 明文 |
774
+
775
+ **② 时间与随机标识(8 项)**
776
+
777
+ | 字段 | 含义 | 加密 |
778
+ |------|------|------|
779
+ | `aw` / `jt` | 随机 UID(UUID v4),`jt` 与 `smid` 同源 | ✓ / ✓ |
780
+ | `hk` / `xv` | 采集时间戳(毫秒,两者相差约 1ms) | ✓ / ✓ |
781
+ | `yi` | 页面加载至今的毫秒数 | ✓ |
782
+ | `t` | 采集时刻(毫秒时间戳) | 明文 |
783
+ | `collectTime` | 采集耗时(毫秒) | 明文 |
784
+ | `smid` | 本地 smidV2(`yyyyMMddHHmmss`+md5(uid)+…) | 明文 |
785
+
786
+ **③ 环境指纹 — 加密的 11 项**
787
+
788
+ | 字段 | 含义 | 本项目取值策略 |
789
+ |------|------|----------------|
790
+ | `xo` | userAgent | 与头一致的 Chrome/Edge UA |
791
+ | `tt` | platform | `Win32` |
792
+ | `lp` | clientSize(视口/屏幕尺寸串) | 由分辨率常量拼装 |
793
+ | `vx` | res(分辨率 + 色深 + dpr) | `1920_1080_24_1` |
794
+ | `vr` | canvas 指纹 | 由 uid 派生的稳定 8 位 hex |
795
+ | `sc` | plugins | `-` |
796
+ | `eb` | referer | `https://chat.deepseek.com/` |
797
+ | `wl` | 当前 url | 登录页地址 |
798
+ | `ic` | 时区偏移(分钟) | `-480` |
799
+ | `fu` | status 位串(Flash/cookie/DevTools 等) | `0010` |
800
+ | `er` | tn 完整性校验值 | 由其余字段算出 |
801
+
802
+ **④ 环境指纹 — 明文的 13 项**
803
+
804
+ | 字段 | 含义 | 取值 |
805
+ |------|------|------|
806
+ | `cdp` | Chrome DevTools Protocol 探测 | `1` |
807
+ | `maxTouchPoints` | 触摸点 | `0` |
808
+ | `connectionRtt` | 网络 RTT | `50` |
809
+ | `cpucount` / `fk` | CPU 核数(两个字段同值) | `8` |
810
+ | `battery` | 电池状态对象 | `{charging:1, level:1}` |
811
+ | `incognito` | 无痕模式 14 项探测结果 | 结构化对象 |
812
+ | `ag` | `new Date().toString()` | JS 日期串 |
813
+ | `qg` | 空串 md5 + 65 个 `1` 的指纹串 | 固定格式 |
814
+ | `kv` | IANA 时区名 | `Asia/Shanghai` |
815
+ | `qm` | 颜色/渲染探测 | `{red:"0"}` |
816
+ | `uu` | 触摸能力对象 | `{maxTouchPoints,touchEvent,touchStart}` |
817
+ | `tn` | **document 能力列表**(见下注) | 字符串数组 |
818
+
819
+ **⑤ 其余数值/空串字段(39 项)**
820
+
821
+ `ah`(-1)、`bq`(1)、`cq`(1)、`dn`(0)、`documentExist`(1)、`fe`(1)、`ff`(-1)、
822
+ `fj`(1040)、`gc`、`gy`(0)、`hb`、`hz`、`je`、`jk`、`jo`(0)、`kf`(1920)、`kj`(0)、
823
+ `ks`(1)、`kw`、`ld`、`lr`(0)、`mu`(0)、`ov`、`po`、`pp`(0)、`qa`(0)、`qx`、`rq`([])、
824
+ `ts`(1)、`uo`(-1)、`up`(0)、`uq`(1)、`uy`、`vn`(943)、`vy`(-1)、`xd`({})、`ym`([])、
825
+ `zl`(0)、`zx`(1920)。
826
+
827
+ > 分组核对:① 9 + ② 8 + ③ 11 + ④ 13 + ⑤ 39 = **80 项**(= JSON 实际字段数)。
828
+ > 另有 `yu` 是唯一的**例外项**:值为 `undefined`,不写入 JSON,
829
+ > 但在 tn 拼接中贡献字符串 `undefined`,因此参与 tn 的字段共 81 项。
830
+
831
+ > ⚠️ **字段名冲突**:`tn` 在新协议里是"document 能力列表"(数组,明文),
832
+ > 而**旧协议**的 `tn` 是"完整性校验值"。新版完整性字段改名为 `er`。
833
+ > 读老代码/老文档时务必分清。
834
+
835
+ ### 17.5 服务端校验点
836
+
837
+ 数美与 DeepSeek 校验的不是"指纹是否与真实浏览器逐位一致",而是这些自洽性:
838
+
839
+ | 校验项 | 说明 | 本项目如何满足 |
840
+ |--------|------|----------------|
841
+ | tn/`er` 自洽 | 拼接串的 md5 必须与 `er` 一致 | 严格复刻 JS 语义(§15.3) |
842
+ | 编码链自洽 | gzip→base64→AES 必须能被服务端解开 | 严格按 §15.4 流水线 |
843
+ | `ep` 可解密 | RSA 必须用数美公钥、输出 base64 | RSA PKCS#1 v1.5 + base64 |
844
+ | 字段格式合理 | UA/分辨率/时区等需为合理值 | 常量取自真实 Windows 环境 |
845
+ | device_id 来源可信 | 必须是数美签发过、未过期的 SMID | 每次签发都真上报换取 |
846
+ | 设备未被标记 | 账号/设备维度的风险名单 | 失败时重签一次 |
847
+
848
+ 也就是说:**格式自洽 + 真实签发**才是关键,无需(也不可能)逐位复刻真实浏览器。
849
+
850
+ ### 17.6 稳定性评估与退化预案
851
+
852
+ | 风险 | 触发条件 | 表现 | 预案 |
853
+ |------|----------|------|------|
854
+ | 协议再升级 | 数美改字段/密钥/算法 | `1902` 持续出现 | 按 §16.2 七步复现流程重跑一遍 |
855
+ | 新增字段 | 服务端要求新采集项 | `1902` | `capture_payload.js` 输出会直接显示新字段 |
856
+ | 密钥轮换 | 字段密钥变了 | `1902` | `crack_des_keys.py` 重跑回填 |
857
+ | 设备被标记 | 该 SMID 进风险名单 | `RISK_DEVICE_DETECTED` | 已内置自动重签一次 |
858
+ | 环境一致性加强 | 服务端开始比对指纹真实性 | 不确定(暂无先例) | 需按 §17.4 补齐真实采集值 |
859
+ | 部署不可持久化 | config 丢失 | device_id 重签(不失败) | 尽量挂持久化目录 |
860
+
861
+ 判断"是协议变了还是设备被标记"的快速方法:
862
+ - 用 `payload.json` 直接 POST 数美接口 → 返回 `1902` ⇒ **协议变了**(走 §16.2)
863
+ - 返回 `1100` 但登录仍被拒 ⇒ **设备/账号被标记**(换账号或等待)
docs/web-endpoint-upgrade.md CHANGED
@@ -17,8 +17,9 @@
17
  | 浏览器特有头 | 无 | sec-ch-ua, origin, referer等 |
18
 
19
  > 2026-09 更新:数美风控升级后,两端登录都改为向数美申请真实签发的 device_id
20
- > (本地伪造值会被判定为 `RISK_DEVICE_DETECTED`)。协议细节见
21
- > `docs/shumei-device-id-research.md` 第十五节。
 
22
 
23
  ### 架构设计
24
 
 
17
  | 浏览器特有头 | 无 | sec-ch-ua, origin, referer等 |
18
 
19
  > 2026-09 更新:数美风控升级后,两端登录都改为向数美申请真实签发的 device_id
20
+ > (本地伪造值会被判定为 `RISK_DEVICE_DETECTED`)。
21
+ > 协议细节见 `docs/shumei-device-id-research.md` 第十五节,
22
+ > 风控整体结构与失败分类见同文档第十七节。
23
 
24
  ### 架构设计
25