乐于分享
好东西不私藏

Shopee什么条件可以免登录,谈谈Shopee DFP / SFU 遥测了什么数据

Shopee什么条件可以免登录,谈谈Shopee DFP / SFU 遥测了什么数据

作者介绍:http://gitee.com/haidragon
 
这也是一个朋友推荐的一个app,要我分析下他的遥测数据,然后前面一搞各种复现不了免登录,然后找了一圈找到一个学员有环境他可以不需要登陆访问,结果只好远程现现场在他电脑上干了。

不是一个 sz-token,而是 DFP 指纹报告 + 服务端 riskToken + SFU 头注入 + SAP 旁路签名

快速总结

  • 这份 Shopee DFP 的主体是一份浏览器安全遥测报告,不是单个 sz-token
  • 当前样本实际上报 103 个 TLV 遥测字段:环境/指纹/行为 88 个,API / 代码完整性探针 15 个。
  • 它上报的内容包括:UA/UA-CH、platform/vendor、screen/viewport、CSS media query、media codec、network、time/perf、Canvas/Picasso、pixel sample、WebGPU/GPU、storage/memory、cookie 能力、设备/会话 ID、环境状态位、行为 packed report。
  • API / 代码完整性遥测包括:Permissions API 状态、Storage Access 错误、structured clone 异常、webdriver/global 变量、fetch/XHR/cookie hook 痕迹、函数源码快照。
  • 它的遥测方式是:浏览器 API 读值 + canvas/GPU 渲染采样 + API 行为/异常触发 + 函数源码/原型 hook 快照 + 行为队列 packed;这些值再进入 VMP 的 TLV/LV 字段协议。
  • 它的上报方式是: 103 个遥测字段 -> VMP TLV/LV -> gzip -> AES-CBC/PKCS7 -> RSA key envelope -> POST/v2/shpsec/web/report
  • 登录/校验页不是 DFP report 上报和 riskToken 消费的必选前置条件;它更像本地环境、账号态、cookie、地域或风控状态不满足时出现的分支。
  • 用到的编码/混淆/加密层包括:字段级字符替换、hash-like 摘要、packed numeric、TLV tag/length/value XOR、LV/RC4-like 字段扰动、gzip/deflate、base64、AES-CBC/PKCS7、RSA-1024/PKCS#1 v1.5 key envelope。
  • /report 返回的 data.riskToken 会写入 shopee_webUnique_ccd,随后进入业务请求头 sz-token 和 af-ac-enc-sz-token
  • VMP 协议目录里的 BODY 213 个字段、PAYLOAD 3 个字段只是字段目录,不等于当前 report 实际上报了 216 个遥测值。

本轮重新扫了整个 /Volumes/haidragon-D/shopee,不是只看 token_analysis_20260703。高信号证据源按用途分成这几类:

用途
证据文件
本文使用方式
TLV 解密后的遥测字段表
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.decoded.json
作为主遥测字段来源,读取 decodedFields 的 103 个字段。
TLV 原始入参
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.originals.json
对照 decoded JSON,确认 tlv.fields[].value 是进入 TLV 创建函数的值。
TLV 动态 hook
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.jsonl
对照 sfu.tlv.call、 sfu.rc4.call、 sfu.rc4.ret,确认字段进入 VMP TLV/LV 路径。
第二份 report TLV 解码
token_analysis_20260703/vmp_static/program_analysis/report_tlv_decoded_pc16.json
用 pc16 crypto trace 交叉确认当前解码结构仍是 103 个 TLV 字段。
字段协议表
token_analysis_20260703/vmp_static/runtime_field_tables.json
读取 BODY 213 个 slot、report 字段、async/sync 分组和 PAYLOAD 3 个字段。
report 加密信封
token_analysis_20260703/vmp_static/program_analysis/report_crypto_dynamic_pc16.json

、 report_frame_layout_pc14.json
确认 gzip、AES-CBC/PKCS7、RSA-1024 key envelope 和最终 frame。
report response / token 缓存
token_analysis_20260703/trace/dfp_trace_20260704_report_response_pc5/*.jsonl

、 pc6/*.jsonl
确认 /report 返回 token 后写缓存,并进入后续 header。
Ce1 / Picasso 链路
dfp_trace_20260703/dfp_trace_chrome_headless_deep_ce1_cem.jsonl

、 token_analysis_20260703/trace/dfp_trace_20260704_picasso_pc18..pc22/*.jsonl、 ce1_writer_pc25..ce1_h_raw_pc29/*.jsonl
用于解释 Ce1 不是 JSON/AES 密文,而是 Picasso/canvas payload。
VMP 静态语义
token_analysis_20260703/vmp_static/program_analysis/*.md

、 decompiled_entries/*.md
用于确认 vmp_resolveAllFields、 vmp_createTLV、 vmp_createLV、crypto/helper 模块角色。
Markdown 摘要
dfp_trace_chrome_headless_tlv1.decoded.md

、 dfp_trace_chrome_headless_tlv1.originals.md、 shopee_dfp_flow_analysis_20260703.md、 shopee_dfp_ce1_cem_deep_analysis_20260703.md、 token_analysis_20260703/analysis_report.md
作为人读摘要和交叉检查,不直接照抄真实运行值。

token_analysis_20260703/trace/codex_tmp_migrated_20260704_0758 下面还有同名迁移副本,用来保留当时的临时工作区产物;本文按 canonical 源文件去重,不把迁移副本重复计入字段数。

这条链路的重点不在于“某个 header 怎么拼”,而在于四件事:

  • 这 103 个 DFP TLV 字段分别上报了什么浏览器遥测
  • API / 代码完整性探针怎样作为遥测证据上传给服务端
  • 遥测报告怎样经过 TLV / LV、gzip、AES、RSA 和二进制 frame 变成 /report body
  • /report 返回的 riskToken 怎样进入缓存,并由 SFU wrapper 汇合到后续业务请求

按当前本地样本,这条 Shopee 请求链路更像下面这个结构:

  1. 浏览器指纹/行为/ API /代码完整性遥测
  2. ->当前样本103 TLV 上报字段
  3. -> VMP TLV / LV field frame
  4. -> gzip
  5. -> AES-CBC/PKCS7 + RSA-1024 key envelope
  6. -> POST /v2/shpsec/web/report
  7. ->服务端返回 data.riskToken
  8. -> cookie/localStorage: shopee_webUnique_ccd
  9. ->后续/api/v4/search/search_items 请求头

本文里的所有运行值都是脱敏演示值。真实 token、真实 hash、真实 AES key、真实请求 body、真实 cookie 值都没有写入。


一、先分清三条链路

Shopee 这份样本里容易混在一起的是 DFP、SFU 和 SAP。它们会在同一个业务请求上汇合,但角色不同。

链路
作用
关键位置
脱敏演示值
当前状态
DFP report
上传浏览器指纹/行为/完整性报告
/v2/shpsec/web/report REPORT_FRAME_B64_FAKE_PREFIX...
已确认 body 不是明文 JSON。
DFP risk token
/report

 返回并缓存的风险 token
shopee_webUnique_ccd
`RISKFAKEA==\
RISKFAKEB==\
SFU wrapper
fetch/XHR 包装和头注入
module 57846
af-ac-enc-dat:DAT_FAKE_16HEX
已确认注入规则和 header 名。
SAP secure request
业务请求旁路安全签名
x-sap-ri

 / x-sap-sec
x-sap-sec:BASE64_FAKE_2164_CHARS...
已确认共现,生成链路另属 SAP。

因此, sg.xiapibuy.com、 /verify/traffic 这类现象要按环境分支看,不能和 /report、 /api/v4/search/search_items 混成同一个“商品数据接口”。本地环境、账号态、cookie、地域状态或风控状态不对时,页面可能被强制带到登录/校验分支;如果本地环境过了,这条登录分支不是必经链路。

  • 商品搜索接口是 /api/v4/search/search_items
  • DFP 上报接口是 /v2/shpsec/web/report
  • sg.xiapibuy.com 在当前语境里应理解为本地环境不达标时出现的登录/校验分支,不是商品搜索真实数据接口,也不是环境正常后的必经数据链路

二、SFU wrapper 到底注入了什么

SFU 外层模块 57846 负责把 DFP SDK 的结果合并到业务请求。这里的字段名是明文常量,不是猜出来的。

字段别名
分组
脱敏演示值
说明
sfu.header.deviceszfingerprint
DFP body/header 切换
DFP_SHORT_FAKE_64BIT_STATE useHeader=false

 时, getClientCheckData() 的结果进入请求数据字段。
sfu.header.sz_token
DFP 短 token
`RISKFAKEA==\
RISKFAKEB==\
sfu.header.afacencsztoken
DFP degraded token
`RISKFAKEA==\
RISKFAKEB==\
sfu.header.afacenc_dat
每请求随机数
DAT_FAKE_16HEX
8 个随机字节转 16 位 hex。不是主 token,也不是指纹 JSON。
sfu.header.xszsdk_version
SDK 版本
1.12.40
SFU 直接返回的 SDK 版本常量。
sfu.header.dnonptchasync
行为同步头
`AAAGFAKESYNC\
6\

这里最关键的修正是: getDegradedData() 在当前链路里不是“重新加密生成 af-ac-enc-sz-token”。后续静态和 pc6 runtime 交叉确认,它走的是:

  1. getDegradedData()
  2. -> getShortToken()
  3. ->当前内存 token slot
  4. -> getCacheDfp()
  5. -> cookie/session/storage 中的 shopee_webUnique_ccd

也就是说, af-ac-enc-sz-token 在这次样本里就是 /report 返回 token 的缓存读出结果。


三、主 DFP report 上报了什么遥测

Shopee 这套 DFP report 的主题不是“生成一个 sz-token”,而是先把浏览器侧环境、能力、指纹、行为和完整性探针采集成一份遥测报告,再由 /v2/shpsec/web/report 送到服务端。服务端返回的 riskToken 只是这份遥测报告被消费后的结果之一。

当前样本里,真正落进 TLV 的上报遥测字段是 103 个。字段名保留 VMP 原始别名,值全部改成脱敏演示值;这里不把 SFU header、SAP header、BODY/PAYLOAD 协议目录混进遥测字段表。

这份 103 个字段可以按遥测主题理解为:

  • Navigator / UA / UA-CH / platform / vendor
  • 屏幕、窗口、viewport、CSS media query
  • 媒体 codec、网络状态、时间戳、计数/性能指标
  • Canvas / Picasso / pixel sample / 图形摘要
  • WebGPU / GPU feature / adapter 信息
  • storage quota、内存/存储额度、cookie 能力
  • 会话/设备 ID、环境状态位、hash-like 子指纹摘要
  • 行为/report packed payload
  • API 行为、API 异常、权限状态、hook 检测、函数源码完整性

字段完整性按本地文件做过交叉核对:

来源文件
抽取口径
字段数
结论
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.decoded.json decodedFields[].key 103
主字段来源。
dfp_trace_20260703/dfp_trace_chrome_headless_tlv1.originals.json firstSearchPhase.tlv.fields[].keyCandidates[0] 103
和 decoded JSON key 集合一致。
token_analysis_20260703/vmp_static/program_analysis/report_tlv_decoded_pc16.json decodedFields[].key 103
pc16 交叉样本,key 集合一致。
token_analysis_20260703/vmp_static/program_analysis/report_tlv_originals_pc16.json firstSearchPhase.tlv.fields[].keyCandidates[0] 103
pc16 originals 交叉验证,key 集合一致。

runtime_field_tables.json 里的 BODY 213 个字段和 PAYLOAD 3 个字段是 VMP 协议目录:它说明“哪些字段可能被 VMP 管”,但不等于当前这次 report 实际上报了 216 个遥测值。所以正文遥测表只按 decoded/originals 里实际落进 TLV 的 103 个字段写。

1. 环境 / 指纹 / 行为遥测字段全表

这一表是当前 report 里已经确认会上报的常规遥测字段,共 88 个。它们不是业务商品数据,而是服务端用于交叉判断浏览器环境是否一致、是否像真实环境、行为队列是否自然的一组证据。

字段别名
分组
脱敏演示值
说明
CEw 媒体能力 ogg:probably;wav:probably;mp4a:probably;xMpegUrl:maybe
音视频 codec 支持矩阵,来自浏览器 canPlayType 一类能力探针。
CEb 屏幕/窗口 1440;900
屏幕或 viewport 尺寸摘要,用来判断窗口几何是否像真实桌面环境。
CEX 图形指纹摘要 FAKE40HEX_CANVAS_AUDIO_DIGEST
图形/媒体子探针折叠出的 40 位摘要值,演示只保留形态。
CEs 预留/空值 ""
当前样本为空字符串,说明该 TLV 槽位被保留但本轮未填具体遥测值。
CED 环境状态位 37
数值化环境状态/能力位,VMP 在写 TLV 前已折叠成短整数。
CER 预留/空值 ""
当前样本为空字符串,属于保留槽位或条件未触发字段。
CEt 图形指纹摘要 FAKE40HEX_RENDER_DIGEST
另一组图形/媒体/环境摘要值,演示只保留 40 位摘要形态。
CEr 网络状态 4g;10;50
Network Information API 摘要:effectiveType、downlink、rtt 一类网络质量值。
Fis 环境状态位 51
布尔/状态位被 VMP 数值化后的结果,不直接写 true/false。
CEl 预留/空值 ""
当前样本为空字符串,说明这个原始字段本轮未采到有效值。
CEx 环境状态位 71
客户端能力/风险状态的短整数编码。
CEK 环境状态位 119
客户端环境状态数值,属于已折叠的能力/风险位。
CEW 预留/空值 ""
当前样本为空字符串,保留字段名和协议位置。
CEY 语言/地区 en-SG
locale 或语言地区短码,用于和 UA、时区、页面站点做一致性校验。
CEm 环境状态位 97
客户端能力/风险状态的短整数编码。
CEn 环境状态位 120
客户端能力/风险状态的短整数编码。
CG1 环境状态位 65
客户端能力/风险状态的短整数编码。
CG4 环境状态位 1
客户端能力/风险状态的短整数编码。
CG9 Navigator/环境 undefined
某个 navigator/window 属性的未定义状态,原始样本经字符替换后还原成 undefined 形态。
CGI 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
CGd 插件列表 ChromePDFViewer;PDFViewer;WebKitbuilt-inPDF
navigator.plugins/PDF viewer 插件列表摘要。
FNW 屏幕/窗口 1.00
浮点型比例值,当前形态更像 devicePixelRatio/scale 一类窗口比例探针。
CGo 环境状态位 31
客户端能力/风险状态的短整数编码。
CGF 屏幕/窗口 1440;900
另一处屏幕/viewport 尺寸摘要,用于和 CEb、visualViewport 互相校验。
CGE 会话/设备ID 00000000-1111-4222-8333-abcdef123456
浏览器侧生成或缓存的会话/设备 UUID 形态标识,演示值已脱敏。
FiD 会话/设备ID 00000000-1111-4222-8333-abcdef123456
与 CGE 同形态的 UUID 类标识,常用于同轮采集或缓存态绑定。
CGf 系统平台 IntelMacOS X14_4_1
操作系统版本/平台字符串。
CGw 系统平台 Macintosh
UA platform family 或系统大类字符串。
CGj Navigator/UA Mozilla/5.0(Macintosh;IntelMacOS X14_4_1)AppleWebKit/537.36(KHTML,likeGecko)Chrome/149.0.0.0Safari/537.36
完整 User-Agent 字符串;decoded JSON 中已做字符替换层还原。
Fiy 语言/地区 ll_ss
本地化/locale 辅助标记,保留短码形态。
CGk Navigator/环境 GoogleInc.(Apple)
navigator.vendor 或 vendor 派生值。
CGh 预留/空值 ""
当前样本为空字符串,保留字段名和协议位置。
CGK 预留/空值 ""
当前样本为空字符串,保留字段名和协议位置。
CGY 布局/字体测量 [9508,9508,9228,9509,9521,...]
一组重复数值测量结果,更像字体/DOM 布局/渲染差异数组。
CGz 环境摘要 FAKE_ENV_DIGEST_56CHARS
环境摘要或子指纹串,演示只保留长摘要形态。
CGm Canvas/Picasso [[255,20,147,255],[123,104,238,255],[32,178,170,255],...]
canvas/Picasso RGBA 颜色采样结果。
CGL 指纹摘要数组 ["FAKE40HEX_A","FAKE40HEX_B","FAKE40HEX_C"]
多个 40 位子指纹摘要组成的数组,用于交叉校验图形/媒体探针。
Ce1 Canvas/Picasso CE1_FAKE_PICASSO_DATA_URL_PAYLOAD
Picasso/canvas PNG data-url 相关 payload,经过 base64/XOR/字符扰动后再入 TLV。
Ce2 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
Ce3 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
CEp 预留/空值 ""
当前样本为空字符串,保留字段名和协议位置。
CEd 预留/空值 ""
当前样本为空字符串,保留字段名和协议位置。
Ce5 预留/空值 ""
当前样本为空字符串,保留字段名和协议位置。
Ce6 WebGPU/GPU packed_4x8_integer_dot_product,subgroup_uniformity,shader-f16,...
WebGPU feature/limit 名称串,检测 GPU/浏览器图形能力。
Ce7 WebGPU/GPU bgra8unorm
WebGPU texture format 或 canvas format 支持探针。
Cea WebGPU/GPU 1,1,1,1,1
GPU 能力位向量,多个支持项被压成逗号分隔的 0/1 串。
FQv WebGPU/GPU 0
GPU/adapter 请求或能力探针的状态值。
FQp 随机/浮点探针 0.6446436629
浮点样本值,可用于判断 Math.random/数值序列形态是否异常。
FQ8 Canvas/Pixel 255255255255128128128255191191191255646464255
RGBA/pixel sample 的十进制拼接串,不是加密 hex。
CeO 环境状态位 94
客户端能力/风险状态的短整数编码。
Cej 计数/性能 27542
数值型计数或耗时指标,进入 TLV 前已经折叠成整数。
Ceq 计数/性能 19398
数值型计数或耗时指标,进入 TLV 前已经折叠成整数。
Cey 环境状态位 126
客户端能力/风险状态的短整数编码。
CeX 计数/性能 3437
数值型计数或耗时指标,进入 TLV 前已经折叠成整数。
CeZ 环境状态位 29
布尔/状态位被 VMP 数值化后的结果。
CeT 环境状态位 72
布尔/状态位被 VMP 数值化后的结果。
CG2 环境状态位 66
布尔/状态位被 VMP 数值化后的结果。
Cet 环境状态位 46
布尔/状态位被 VMP 数值化后的结果。
Fhs 时间戳 1783120000000
毫秒时间戳形态,记录某个采集阶段时间。
FhR 时间戳 1783120000000
毫秒时间戳形态,和 Fhs 同轮时间点相互校验。
Cec 环境状态位 63
布尔/状态位被 VMP 数值化后的结果。
CeU 页面状态 visible
document.visibilityState 页面可见性。
CeW 窗口/布局桶 74-75
窗口/布局派生桶值,常用于和 viewport、screen 探针组合判断。
bfso_screenOrientation 屏幕/窗口 landscape-primary;0
screen.orientation 的 type 和 angle。
bfso_visualViewport 屏幕/窗口 w:1440;h:900;s:1.00;ol:0;ot:0;pl:0;pt:0
visualViewport 宽高、scale 和 offset。
bfso_mediaFeaturePrefs CSS/媒体查询 dark:0;rm:0;fc:0;ic:0;pc:0;pf:1;hc:1;srgb:1;p3:0
暗色模式、reduced-motion、forced-colors、pointer、色域等 media query 结果。
CGT UA-CH {"mobile":false,"model":"","platform":"macOS","platformVersion":"14.4.1","uaFullVersion":"149.0.0.0"}
UA Client Hints high entropy values 展开对象。
CGZ 环境状态位 123
客户端能力/风险状态的短整数编码。
Cew 环境状态位 109
客户端能力/风险状态的短整数编码。
Fi3 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
FiE WebGPU/GPU ["depth32float-stencil8","texture-compression-bc","shader-f16",...]
WebGPU feature/format 支持列表。
Fie WebGPU/GPU [objectGPUAdapterInfo]
GPUAdapterInfo 对象字符串化结果,用于判断 WebGPU adapter 信息暴露情况。
FiO WebGPU/GPU getAd:35.7,adFt:0
requestAdapter/getAdapter 相关耗时或状态摘要。
FiG 环境状态位 7
客户端能力/风险状态的短整数编码。
FiU 内存/存储 10737418240;4395630592
内存/存储额度类双数值摘要。
bfso_storageEstimate 存储额度 q:33;u:0;p:0
navigator.storage.estimate/persist 相关 quota、usage、persisted 摘要。
Cev 系统平台 MacIntel
navigator.platform。
Ceo 浏览器能力 true
cookieEnabled 或同类浏览器能力布尔结果。
CeF Navigator/环境 GoogleInc.(Apple)
vendor/vendorSub 一类 navigator 环境值。
CG0 环境状态位 59
客户端能力/风险状态的短整数编码。
Fhe 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
FhO 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
Fhy 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
FhX 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
CGQ 预留/空值 ""
当前样本为空字符串,属于条件未触发或保留字段。
Ceg 行为/性能计数 {"2":35,"5":25,"7":329,"8":32,"52":255,...}
数值键到计数/耗时的 map,像行为、性能或采集步骤统计。
Cez 报告对象 {}
report 子对象,本样本为空对象,保留对象形态。
Cem 行为/报告packed [1783120000000,8261,1293,8342,1299,...]
packed numeric/report/behavior payload,第一项是毫秒时间戳形态。

2. API / 代码完整性遥测探针

这一表单独列 API 行为、异常文本、权限状态、hook 痕迹、全局变量摘要和函数源码形态,共 15 个。它们同样是遥测数据:客户端上传的不是“要执行的代码”,而是关键函数/API 在本地环境里的表现证据。服务端可以用这些证据判断 fetch、 XMLHttpRequest、 document.cookie、structured clone、Permissions API、Storage Access API、webdriver/global 变量等有没有被改、被 hook、被自动化框架替换。

字段别名
分组
脱敏演示值
说明
CES 自动化/Hook webdrivernativehook;
webdriver/native hook 检测结果,命中时会留下对应标记。
CGl 函数完整性 function(){returnnativeCheck(326)}
函数源码字符串/代理函数快照,用于判断原生函数是否被替换。
CGB 函数完整性 FUNCTION_SOURCE_SNAPSHOT_FAKE_BASE64
fetch/XHR 等函数源码片段的 anti-tamper 快照;这是被上报的证据,不是在字段里执行代码。
FQP API异常探针 Failedto execute postMessage:MimeTypeArrayobjectcouldnotbe cloned.
postMessage structured clone 对 MimeTypeArray 的异常结果。
FQF API异常探针 Failedto execute postMessage:MimeTypeobjectcouldnotbe cloned.
postMessage structured clone 对 MimeType 的异常结果。
Cee API异常探针 Cyclic__proto__ value
原型链/对象结构异常探针,用于观察 JS 引擎报错文本。
Ceb API探针对象 {}
JSON-like 子对象,本样本为空对象,保留对象形态。
CEo API探针列表 []
JSON-like 数组字段,本样本为空数组,通常承载探针结果列表。
CeD 编码/API行为 {"1":{"cT":"ArrayBuffer","uT":"ArrayBuffer","e":null},"2":{"e":"EncodingError"}}
TextEncoder/ArrayBuffer/structured clone 一类 API 行为差异探针。
FhZ 自动化/全局变量 0,__playwright__binding__,BROWSER_YEAR,__APP_ID__,__LOCALE__,...
window/globalThis 上的全局变量名摘要,可暴露 Playwright/注入脚本/站点运行态变量。
CeR 权限/StorageAPI {"st":"rejected","e":"fake storage access error"}
Storage Access / communication medium 权限请求的错误结果。
bfso_permissionStates 权限状态 geo:p;cam:p;mic:p;noti:p;clipR:p;clipW:g;midi:pdS
Permissions API 状态摘要:地理位置、摄像头、麦克风、通知、剪贴板、MIDI 等。
Ced 自动化检测 false
webdriver/自动化相关布尔结果。
Fhw Hook/异常列表 []
数组型检测结果,本样本为空数组,表示该类异常列表未填。
FhM Hook检测 ["XMLHttpRequest.prototype.send:68","window.fetch:68","document.cookie:hookGetter,hookSetter"]
fetch/XHR/addEventListener/cookie hook 检测数组。

CGB、 CGl 这类字段尤其要按“代码完整性遥测”理解:字段里出现函数源码片段,不代表 report body 携带一段要执行的业务代码;它表示客户端把函数源码字符串、VMP stub 形态或原型函数快照作为 anti-tamper 证据上传。服务端消费的是“本地关键函数有没有被改过”的证据,而不是执行这段字段文本。

这类字段的“原始值”也不总是浏览器 API 原文。更准确地说, tlv.fields[].value 是进入 TLV 创建函数的值;它可能已经过字段级字符替换、hash、packed numeric 或 VMP 内部编码。


四、 Ce1 不是 JSON,也不是 AES 密文

Ce1 是这份样本里最容易被误读的字段。当前最稳的结论是:

  • Ce1 是 BODY slot 129
  • TLV tag 是 1000
  • 类型是 FIELD_TYPE_STR
  • 长度类型是 Uint16
  • 非空样本长度约 916 字符
  • 上游是 module 8321 的 Picasso/canvas 链路

它的当前样本闭环可以概括成:

  1. Picasso R() canvas draw/state
  2. -> raw compact data URL
  3. ->每字符 XOR 0x5a
  4. -> base64
  5. -> X/字符交换
  6. ->Ce1/tag1000
  7. -> TLV
  8. ->/report body

脱敏演示:

阶段
脱敏演示值
说明
raw compact data URL
data:image/png;base64,iVBOR_FAKE_10x10...
10×10 PNG data-url 形态。
VM raw state
RkFLRV9iYXNlNjRfYWZ0ZXJfeG9y...
对 data-url 处理后的 base64-like 字符串。
final Ce1
RkFLRV9iYXNlNjRfYWZ0ZXJfWHNfc3dhcA...
进入 TLV 的最终字符串。
decoded side view
dota:imoge/png;bose64,...demo
旧视角下看到的伪 data-url,不是独立加密层。

所以, Ce1 不能按“加密 JSON”去找明文。它更像图形指纹里的 Picasso/canvas payload,当前已闭合的是 data-url 到 Ce1 的可逆扰动层;还没有完全复现的是 raw compact data URL 之前的浏览器绘制状态。


五、 Cem 是 packed numeric/report,不是压缩包

Cem 是另一个容易误判的字段。当前确认:

  • Cem 是 BODY slot 161
  • TLV tag 是 5658
  • 类型是 FIELD_TYPE_NUM_ARR
  • 它不是 gzip
  • 它不是 deflate
  • 它不是 brotli
  • 它不是 JSON
  • 它也不是普通可打印文本

脱敏演示:

  1. [1783120000000,8261,1293,8342,1299,8338,1299,...FAKE_NUMERIC_PAYLOAD...]

第一项按 varint 读出来是毫秒时间戳形态,后面跟着成组的行为/报告数字。它和 SFU 里的键盘、鼠标、触摸、civ/event 序列化结构方向一致,但 pB[161] 对应的 exact VMP encoder schema 还没有完全 lift 成可读 JS。

所以对 Cem 的更准确说法是:它是 packed numeric/report/behavior payload。现在缺的是 schema 命名,不是“还差一个解密密码”。


六、VMP 里的 TLV / LV 和 /report 加密流程

这里的 TLV/LV 不是泛泛的“打包格式”四个字。它是 DFP VMP 里把字段值变成 report body 的中间协议层,夹在“浏览器字段采集”和“最终 AES/RSA report body”之间。

按当前样本,相关模块可以这样看:

模块/函数
角色
当前理解
module 1711 / vmp_resolveAllFields
字段解析
按 BODY/PAYLOAD 字段表解析同步、异步、report 字段。
module 1711 / vmp_getDfpData
report 入口
把解析出的字段送进 TLV/LV 和后续 report body 管线。
module 1388 / vmp_createTLV
TLV 字段编码
按 tag、lengthType、type/bsi、mask 把单字段编码成二进制片段。
module 6578 / vmp_createLV
LV/report 拼装
组织部分字段的 length-value 片段,并调用 RC4-like 处理。
module 7511
RC4-like 字段处理
对进入 LV 路径的部分字段做字段级流加密/扰动。
module 1200
gzip/deflate
压缩拼好的 report 二进制 payload。
module 3405
bytes/base64
把 gzip bytes 转 base64,后续也参与最终 frame 的 base64。
module 4786
AES
生成随机 AES key,并用 AES-CBC/PKCS7 加密 gzip base64。
module 5661
RSA
用 RSA-1024/PKCS#1 v1.5 加密 AES key。

当前已确认的总路径如下:

  1. 浏览器字段采集
  2. ->module1711: vmp_resolveAllFields / vmp_getDfpData
  3. ->module1388: vmp_createTLV(tag, lengthType, value, mask, bsi)
  4. ->module6578: vmp_createLV(...)
  5. ->module7511: selected fields RC4-like
  6. -> report binary payload
  7. ->module1200: gzip
  8. ->module3405: gzip bytes -> base64
  9. ->module4786: AES-CBC/PKCS7 encrypt(gzipBase64, randomAesKey)
  10. ->module5661: RSA encrypt(randomAesKey)
  11. -> prefix23 || rsaKeyCipher128 || separator14 || aesCipher
  12. ->final base64
  13. -> POST /v2/shpsec/web/report

1. TLV 是字段级协议,不是最终 body 加密

TLV 这一层处理的是“一个字段怎样落成二进制片段”。例如 Ce1、 Cem、 CGj、 CGB 这些字段,都会根据字段表带上 tag、lengthType、type/bsi。

参数
脱敏演示值
说明
tag
1000
协议字段号,例如 Ce1
lengthType
2
长度字段宽度,常见 1/2/4。
value
Ce1_FAKE_PAYLOAD
进入 TLV 的字段值。
mask
0xFAKE_RUN_MASK
每次 trace 里捕获的 mask 可能不同,不应写成全局固定值。
bsi
BSI2_ID
字段类型/整数宽度标记。

TLV 的要点是:tag、length、value 都不是裸写。当前逆出来的规则可以概括成:

  1. value 规范化
  2. ->undefined/null变空 bytes
  3. -> bigint string
  4. ->object/array  JSON.stringify
  5. ->string UTF-8 bytes
  6. tag
  7. -> uint16be(tag)
  8. ->每字节 XOR mask
  9. length
  10. -> lengthType  uint8/uint16be/uint32be
  11. ->每字节 XOR mask
  12. value
  13. -> valueXorByte = mask[0]^ tagLow ^ tagHigh
  14. ->每个 value byte XOR valueXorByte
  15. TLV output
  16. -> tagBytes || lengthBytes || encodedValue

所以 TLV 不是最终的 AES/RSA 加密层,它更像 VMP 字段协议里的第一层“字段编号 + 长度 + 值 + 字段级 XOR 混淆”。要解 TLV,必须知道字段表、tag、lengthType、mask 和字段类型。

2. LV 是部分字段的 length-value / report 拼装层

LV 这一层不是所有字段都走。它更像 report payload 里的另一种 length-value 拼装方式,负责把部分字段组织成 report 二进制 payload,并在部分路径上套 RC4-like 处理。

脱敏演示值
说明
LV 入口
vmp_createLV(...)
module 6578 暴露的 report/LV 组包函数。
RC4-like 调用
sfu.rc4.call->sfu.rc4.ret
运行期能看到字段进入和返回。
RC4-like key
RC4_KEY_FAKE_PER_RUN
样本运行期捕获到过短 key,但文章不放真实值,也不写成全局固定。
作用范围
selected LV/report fields
只覆盖部分字段/片段,不是整个 /report body 的唯一加密层。
输出位置
report binary payload
和 TLV 片段一起进入 gzip 前二进制 buffer。

这里容易误判:看到 RC4-like 不等于“整个 report body 是 RC4”。更准确的说法是:VMP 内部对部分字段做 LV 组包和字段级流加密/扰动,随后这些片段才会和其他 TLV 片段一起进入 gzip。

3. gzip 是 report payload 压缩层

TLV/LV 之后得到的是 gzip 前二进制 payload,不是最终请求 body。当前样本里能看到:

阶段
当前样本长度关系
脱敏演示值
说明
gzip input
10736

 bytes 级别
TLV_LV_BINARY_PAYLOAD_FAKE
TLV/LV 拼好的二进制 payload。
gzip output
8450

 bytes 级别
GZIP_BYTES_FAKE
可 gunzip 回 gzip input。
gzip base64
11268

 chars 级别
GZIP_BASE64_FAKE...
gzip bytes 被转成 base64 字符串。

也就是说,gzip 压缩的是已经被 VMP 字段协议处理过的 report payload,不是原始浏览器 JSON。

4. AES / RSA 是外层 report body 加密信封

阶段
当前样本长度关系
脱敏演示值
说明
AES key
16

 chars
k9FAKEbase36z1q
随机 base36 字符串;key 和 IV 同源。
AES plaintext
11268

 chars 级别
GZIP_BASE64_FAKE...
gzip bytes 的 base64 字符串。
AES cipher
11280

 bytes 级别
AES_CIPHER_FAKE_BYTES
AES-CBC/PKCS7 后的密文字节,长度按 16 字节块补齐。
RSA key cipher
128

 bytes
RSA_FAKE_128_BYTES
RSA-1024 / PKCS#1 v1.5 加密 AES key。
final frame
prefix23+128+separator14+aesCipher REPORT_FRAME_B64_FAKE_PREFIX...
最后再 base64 成 /report body。

这层才是外层 report body 的主加密信封:

  1. gzipBase64
  2. -> AES-CBC/PKCS7(key=random16, iv=random16)
  3. -> aesCipher
  4. random16 AES key
  5. -> RSA-1024/PKCS#1 v1.5 public encrypt
  6. -> rsaKeyCipher
  7. prefix23 || rsaKeyCipher128 || separator14 || aesCipher
  8. -> base64
  9. ->/report body

所以 Shopee DFP report 不是一层算法,而是至少有这些层:

层级
作用
是否全局加密
TLV
字段编号、长度和值的协议编码,并带字段级 XOR 混淆
否,字段级。
LV
部分字段的 length-value/report 拼装
否,部分字段/片段。
RC4-like
LV 路径上部分字段的流式扰动/加密
否,部分字段/片段。
gzip
压缩 TLV/LV 后的 report payload
否,是压缩层。
AES-CBC/PKCS7
加密 gzip base64
是,覆盖主要 payload 密文。
RSA-1024
加密 AES key
是,覆盖 key envelope。
final base64 frame
把二进制 frame 变成 ASCII body
不是加密,是传输编码。

因此, /report body 呈现为一段较长的 base64-like ASCII 字符串时,它不是“被截断的 JSON”,而是外层 base64 over packed binary frame。


七、 /report 返回 token 后怎样汇合到搜索请求

当前 pc6 链路修正了一个重要点:后续搜索请求上的两个 token 不是浏览器本地刚重新算出来的两个不同值,而是 /report 返回 token 的复用。

  1. POST /v2/shpsec/web/report
  2. -> response JSON:{ code:0, data:{ riskToken:"..."}}
  3. -> setResponseText(riskToken)
  4. ->写入in-memory token slot
  5. ->持久化 shopee_webUnique_ccd
  6. -> getShortToken()/getCacheDfp()
  7. -> getClientCheckData()/getDegradedData()
  8. ->搜索请求头 sz-token / af-ac-enc-sz-token

1. 缓存与 header 映射表

字段别名
存放位置
脱敏演示值
说明
report.response.riskToken
/report

 响应 JSON
`RISKFAKEA==\
RISKFAKEB==\
storage.shopeewebUniqueccd
cookie/localStorage/session
`”RISKFAKEA==\
RISKFAKEB==\
request.sz-token
搜索请求 header
`RISKFAKEA==\
RISKFAKEB==\
request.af-ac-enc-sz-token
搜索请求 header
`RISKFAKEA==\
RISKFAKEB==\
request.af-ac-enc-dat
搜索请求 header
DAT_FAKE_16HEX
每请求 16 hex 随机值,和 token 主体不是一回事。
request.x-sz-sdk-version
搜索请求 header
1.12.40
SDK 版本。
request.d-nonptcha-sync
搜索请求 header
`AAAGFAKESYNC\
6\

2. pipe token 形态

当前样本里的 token 形态可以抽象成:

  1. <base64_16_bytes>|<base64_64_bytes>|<base64_12_bytes>|08|3

脱敏演示:

  1. PART1_FAKE_16B64==|SECOND_FAKE_64B64_SEGMENT_PLUS_PADDING==|THIRD_FAKE_12B64|08|3

这个 pipe token 是服务端消费 DFP 遥测报告后返回的风险结果,不是原始指纹 JSON。当前样本里它从 /report 响应进入本地缓存,再被 SFU wrapper 读出并放进后续业务请求头。


八、SAP 字段要单独看

Shopee 请求上还会出现 SAP 侧字段,它们和 DFP/SFU 会共现在业务请求里,但不是同一条 VMP 链路。

字段别名
分组
脱敏演示值
说明
SAP._sapid
持久 ID
SAPID_FAKE_56HEX
56 hex 字符形态,存 localStorage/cookie。
SAP.xsapri
请求 ID/完整性
SAP_RI_FAKE_HEX
请求变化的 hex-like header。
SAP.xsapsec
请求安全 blob
BASE64_FAKE_SEC_BLOB_LEN_2164...
大 base64-like header,不是 DFP report body。

当前文章只把 SAP 放在“业务请求汇合面”里说明,不把它和 DFP 的 /report body、 Ce1、 Cem 混成同一件事。


九、遥测数据怎么上报和被消费

从当前样本看,Shopee DFP report 的服务端消费对象不是一个明文 JSON,而是一份已经封装好的遥测剖面。它把“环境像不像真实浏览器”“API 行为是否正常”“函数有没有被改”“行为队列是否自然”这些信号打包到同一份 report 里。

阶段
遥测动作
上报内容
浏览器环境采集
读取 Navigator、UA-CH、platform、vendor、screen、viewport、CSS media query、network、storage、cookie 能力
CGj

、 CGT、 Cev、 CGf、 CGw、 CEb、 CGF、 bfso_visualViewport、 bfso_mediaFeaturePrefs、 CEr、 bfso_storageEstimate 等。
图形和媒体采样
调用 media codec、canvas/Picasso、pixel sample、WebGPU/GPU adapter/feature 探针
CEw

、 CGm、 Ce1、 FQ8、 CEX、 CEt、 CGL、 Ce6、 Ce7、 FiE、 Fie、 FiO 等。
API 行为探针
主动触发 Permissions API、Storage Access、structured clone、TextEncoder/ArrayBuffer 等 API 行为或异常
bfso_permissionStates

、 CeR、 FQP、 FQF、 Cee、 CeD、 Ceb、 CEo 等。
代码完整性探针
采样函数源码字符串、原型函数长度、fetch/XHR/cookie hook 痕迹、window/globalThis 异常变量
CGB

、 CGl、 FhM、 FhZ、 CES、 Ced、 Fhw 等。
行为和计数汇总
把时间戳、性能计数、行为/report 队列压成数值或 packed numeric payload
Fhs

、 FhR、 Cej、 Ceq、 CeX、 Ceg、 Cem 等。
VMP 字段封装
把上述字段归一化、字符替换、hash、packed,再进入 TLV/LV
tlv.fields[].value

 是进入 TLV 创建函数的值,不一定是浏览器 API 原文。
report body 加密
TLV/LV payload 经过 gzip、AES-CBC/PKCS7、RSA key envelope 和 final base64 frame
最终请求体发到 /v2/shpsec/web/report
服务端回 token
服务端消费这份遥测剖面后返回 data.riskToken
token 被写入 shopee_webUnique_ccd,后续进入 sz-token / af-ac-enc-sz-token

所以这套东西的核心是“遥测剖面”而不是“token 字符串”。 riskToken 是服务端对这份 DFP 遥测报告的响应结果;业务请求上看到的 sz-token、 af-ac-enc-sz-token 只是这个结果在本地缓存和请求头上的汇合形态。

十、最后怎么理解这条链路

如果只看最终搜索请求,很容易把它简化成:

  1. 带上 sz-token 就能请求商品接口

但当前样本说明真实结构更接近:

  1. 浏览器侧先构造 DFP 报告
  2. ->报告经过多层二进制封装上传
  3. ->服务端根据报告返回 riskToken
  4. -> SDK 缓存这个 token
  5. -> SFU 在业务请求上注入 tokendatversionsync
  6. -> SAP 另行注入 x-sap-ri / x-sap-sec
  7. ->最终才到/api/v4/search/search_items

所以,这里谈的不是“单个 token 算法”,而是一套浏览器遥测、报告封装、服务端回 token、本地缓存、业务请求汇合的完整状态机。主线是:Shopee 先消费浏览器侧 DFP 遥测报告,再把服务端返回的 riskToken 缓存并汇合进后续业务请求;SAP 的 x-sap-ri / x-sap-sec 是并行的业务请求安全层。

    • 公众号:安全狗的自我修养

    • vx:2207344074

    • http://gitee.com/haidragon

    • http://github.com/haidragon

    • bilibili:haidragonx