针对社区中关于“多账号连续封禁是否源于客户端底层硬件锁(如主板序列号、MAC 地址或 TPM 芯片指纹)”的讨论,本报告对 Windows 平台下的 Claude Code CLI(2.1.306) 与 Claude Desktop GUI(3.1623.0) 进行了静态代码提取、底层原生模块扫描及本地落盘数据核对。
分析范围涵盖 CLI 端的 281.9 MB Bun 单文件可执行程序(提取出 3983 个明文 JS 模块,共 25.9 MB),以及 Desktop 端的 app.asar 归档(解包 509 个文件,含 477 个 JS 脚本,共 40.3 MB)与 3.8 MB 的 Rust 原生扩展模块 claude-native-binding.node。本报告仅陈述客户端侧可观测的代码逻辑与本地持久化证据,不对服务端风控算法与封号阈值做主观推断。
一、 核心结论摘要
在讨论“硬件识别”时,需先区分两个概念:唯一硬件标识(物理序列号/机器码)与设备环境指纹(软硬件配置组合)。前者的特征是出厂固定或跨软件共享,可直接作为数据库主键;后者则是多个型号级与配置级字段的组合,用于辅助提高风控聚类的置信度。
基于四重静态与落盘证据(本机落盘文件、原生二进制双编码扫描、跨模块调用链追踪、注册表字面量全枚举),核心结论如下:
-
未采集任何物理硬件序列号或系统机器码:两个客户端均未采集网卡 MAC 地址、磁盘物理序列号、主板/BIOS/SMBIOS 序列号或 CPU 序列号。对于操作系统级机器码(如 Windows
MachineGuid、macOSIOPlatformUUID、Linux/etc/machine-id),Desktop 客户端已将其完全移除;CLI 客户端虽包含读取代码,但因唯一调用方hostDetector未在任何遥测路径中注册,实际属于从不执行的死代码。 -
跨账号关联的首要通道是本地随机安装 ID:CLI 端通过
~/.claude.json持久化 64 位十六进制随机数userID与machineID;Desktop 端通过%USERDATA%\ant-did持久化随机 UUIDv4。这些标识纯属随机生成,与物理硬件无关,但其生命周期与账号登录态解耦——切换账号、退出重登均不会重置这些 ID。若未手动清理配置文件而在同一客户端连续切换多个账号,服务端在所有请求与遥测中看到的都是同一个设备 ID。 -
Desktop 端通过 Sentry 默认收集完整的设备环境指纹:虽然没有物理序列号,但 Desktop 端的 Sentry 错误上报上下文(持久化于本地
scope_v3.json)默认组装了 CPU 型号、逻辑核数、精确物理内存、最多 3 块 GPU 的型号/PCI ID/驱动版本、屏幕分辨率与缩放比例、精确到毫秒的开机时间(boot_time)以及时区语言,并将user.id绑定为ant-did。 -
TPM 2.0 仅用于凭据防盗,而非硬件封禁:Desktop 端调用 Windows
PlatformCryptoProvider在 TPM 芯片内生成密钥对,但并未读取出厂背书密钥(EK)或执行硬件平台证明(TPM2_Quote)。其生成的椭圆曲线密钥对按accountUuid独立隔离,不同账号拥有完全独立的随机公钥,服务端无法借此判定两个公钥来自同一颗物理 TPM 芯片。 -
CLI 端存在灰度中的 Git 仓库地址上报逻辑:CLI 2.1.306 内置了名为
x-claude-code-wiggly-dove的自定义请求头逻辑,受 GrowthBook 特性开关tengu_wiggly_dove控制(当前默认false)。一旦远程开启,客户端会解析当前工作区.git/config中的remote originURL 并随每次 API 请求发送。
二、 Claude Code (CLI 2.1.306) 逆向分析
Claude Code 2.x 采用 Bun --compile 打包为单文件原生程序,其业务 JS 源码以明文形式嵌入在二进制中。通过按连续可打印字节切分并结合模块的 export{... as ...} 导出表进行跨 chunk 符号校验,可完整还原其内部调用链。
1. 操作系统机器码读取:存在实现但无调用出口
在二进制偏移 246,053,789 处存在平台分派函数 getMachineId(),可根据 process.platform 动态加载各操作系统的机器码读取实现:
-
Windows(偏移
266,138,923):调用%windir%\System32\REG.exe QUERY HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography /v MachineGuid提取注册表值。 -
macOS(偏移
266,135,306):执行ioreg -rd1 -c "IOPlatformExpertDevice"提取IOPlatformUUID。 -
Linux 与 FreeBSD:Linux 依次尝试读取
/etc/machine-id与/var/lib/dbus/machine-id;FreeBSD 读取/etc/hostid或回退至kenv -q smbios.system.uuid。
然而,对全部 3983 个模块进行符号引用统计后发现,这组代码在当前版本中并未生效:
// 偏移约 246,054,895:OpenTelemetry SDK 内部的 hostDetectorclass _A { detect(e) { return { attributes: { [dt.ATTR_HOST_NAME]: nn.hostname(), [dt.ATTR_HOST_ARCH]: mA.normalizeArch(nn.arch()), [dt.ATTR_HOST_ID]: pA.getMachineId() // 全量代码中唯一的 getMachineId 调用点 }}; }}全量扫描结果显示,getMachineId 出现的 8 处全部局限在 OpenTelemetry(OTel)resource 模块内部,hostDetector.detect(...) 的实际调用次数为 0。进一步排查客户端内全部三条 OTel Resource 构造路径,均可确认 hostDetector 被排除在外:
-
路径 A(1P 官方遥测
/api/event_logging/v2/batch):在HGe()(偏移247,628,495)中直接通过resourceFromAttributes显式构造,仅包含service.name、service.version以及 WSL 环境下的wsl.version,不经过任何 detector。 -
路径 B(3P 企业 OTLP 导出):在构造器
it()(偏移280,337,857)中,代码显式调用了osDetector.detect()(获取系统类型与版本)、host.arch(仅 CPU 架构)以及envDetector.detect()(读取OTEL_RESOURCE_ATTRIBUTES),唯独跳过了hostDetector,因此不会产生host.id与MachineGuid。 -
路径 C(
defaultResource()兜底):仅返回service.name与遥测 SDK 语言/版本元数据,不执行任何 detector。
此外,即便未来版本启用了 MachineGuid 读取,从技术属性上看,MachineGuid 也只是 Windows 系统安装实例的编号(重装系统即变,克隆磁盘则会重复,换主板/CPU 不变),并非物理硬件序列号。
2. 常见可疑字符串的误判排除
在静态字符串检索中,二进制内会出现若干与硬件或网络相关的系统命令与 API,经上下文核对均与硬件指纹采集无关:
-
systeminfo、getmac、wmic:仅出现在 Bash 工具的命令安全分类表中(例如getmac: { safeFlags: ["/FO", "/NH", "/V"] }),用于判断 AI 尝试执行的终端命令是否属于只读安全命令、是否需要向用户弹出权限确认提示。 -
networkInterfaces()(共 9 处):分别用于 Google Compute Engine(GCE)云环境探测(比对 Google MAC 地址段)、本地沙箱与代理网络配置,以及 Node.js 内部实现,无 MAC 地址外发逻辑。 -
serialNumber(共 27 处):全部属于 PKI/ASN.1 证书解析字段或@parcel/watcher文件系统监控中的卷序列号(volumeSerialNumber,用于检测本地文件变更)。
3. CLI 持久化标识与遥测载荷结构
CLI 客户端真正用于标识设备的字段存储在用户目录的 ~/.claude.json 中,由函数 Obt(getOrCreateMachineID)和 p0(getOrCreateUserID)在偏移 246,991,000 附近生成:
function Obt(e) { // getOrCreateMachineID let n = ce(), r = ue(); if (typeof r.machineID === "string" && Ds.test(r.machineID)) return r.machineID; if (n.generatedMachineID) return n.generatedMachineID; let s = Ju(32).toString("hex"); // 32 字节纯随机数(64 位 hex) return n.setGeneratedMachineID(s), Re((h) => ({ ...h, machineID: s }), e), s;}这两个 ID 均为 randomBytes(32).toString("hex") 生成的纯随机字符串,另有随机 UUID 形态的 remoteControlMachineId 专用于远程控制/云会话。它们在网络请求中的映射关系如下:
-
常规 API 请求头:仅携带
User-Agent: claude-cli/2.1.306 (external, cli)与每次会话随机生成的X-Claude-Code-Session-Id,不携带机器标识或用户名。 -
1P 遥测事件(
/api/event_logging/v2/batch):将本地userID映射为外发 JSON 中的device_id(以及user_metadata.deviceId)。其env块仅包含平台、架构、Node/Bun 版本、终端与 Shell 类型、CI/容器环境标记及 Git 工作树状态统计(如head_sha、变更文件数),不含主机名、用户名或机器码。 -
错误上报(Sentry / Datadog 格式):将
hostname字段硬编码为"claude-code",同时截取本地machineID的前 12 位填入host_name_redacted字段。 -
真实主机名(
os.hostname())的外发边界:全量 19 处引用中,仅在用户主动发起 Remote Control 云会话注册(作为machineName)或同步设备 MCP 授权(device_mcp_consent中的machine_name)时才会上传真实主机名。
由于 userID 和 machineID 存储在全局配置层级,不随 oauthAccount 的登出或切换而重置,同一配置文件下的多个账号在遥测流中会共享完全相同的 device_id。
4. 灰度中的敏感数据采集:x-claude-code-wiggly-dove
在模块 run-1625-248304930.js(偏移 250054 起)中,内置了一套按 RFC 8941 字典规范组装动态客户端元数据的自定义请求头逻辑,头部名称为 x-claude-code-wiggly-dove:
var qc = "x-claude-code-wiggly-dove";var Wc = "983fad3bbb6cff8714b9f0c8bce0d76386cabd5e65e046625c159df3b953f99d"; // qc 的 SHA-256
function ci(e) { let r = e.toLowerCase(); return r === qc || kb("sha256").update(r).digest("hex") === Wc; // 防用户自定义变量覆盖校验}var xb = "tengu_wiggly_dove"; // GrowthBook 远程灰度开关该头部使用双字母混淆短码记录环境特征(如 ob 为版本号、sk 为客户端入口、tt 为权限模式、bf 为沙箱分类、vd 为主机环境),其中键名 jy 对应采集函数 Db()(偏移约 254000):
-
Db()内部调用svt()(位于run-1506-245151486.js偏移76504),直接读取当前工作目录.git/config文件中[remote "origin"]段落下的pushurl或url。 -
剥离查询参数并截断至 126 字符后,拼入每次 API 请求的默认头部:
x-claude-code-wiggly-dove: v=1, yw=:jm:, ob="2.1.306", sk="cli", jy="github.com/org/repo"。
该逻辑目前受 E("tengu_wiggly_dove", false) 门控,默认未开启;但若服务端通过 GrowthBook 下发策略开启该开关,同一本地代码仓库下的不同账号将通过 jy 字段暴露相同的 Git 远程仓库地址。
三、 Claude Desktop (GUI 3.1623.0) 逆向分析
Windows 版 Claude Desktop 是基于 MSIX 托管的 Electron 应用(Electron 44.9.0 / Chromium 152 / Node 24.22.0),包含主进程/渲染进程 JS 包、Rust 编写的 claude-native-binding.node 以及 Go 编写的后台服务 cowork-svc.exe。
1. 物理序列号与系统机器码的“负命题证明”
为确认 Desktop 客户端是否采集底层硬件标识,分析不仅对 477 个 JS 文件及原生二进制(.node 与 cowork-svc.exe)执行了 ASCII 与 UTF-16LE 双编码扫描,还对主进程代码中出现的所有 Windows 注册表路径字面量进行了全量枚举。
扫描确认:MachineGuid、IOPlatformUUID、IOPlatformSerialNumber、/etc/machine-id、Win32_ComputerSystemProduct、Win32_BaseBoard、Win32_DiskDrive、Win32_BIOS、GetVolumeInformation、GetAdaptersInfo 等字符串在全量 JS 与原生层中的命中数均为 0。
同时,正则枚举主进程中的注册表路径字面量,共提取出全部 15 条访问路径:
SOFTWARE\Microsoft\Windows NT\CurrentVersion (读取 BuildLabEx / UBR)SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList (枚举本地用户目录,用于日志脱敏)SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUISOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManagerSOFTWARE\Policies\ClaudeCodeSOFTWARE\Google\Chrome\NativeMessagingHostsSOFTWARE\Google\Chrome\ExtensionsSOFTWARE\Microsoft\Edge\NativeMessagingHosts...(其余 7 条均为 Brave / Vivaldi / Opera / Arc / Island / Prisma 等浏览器的 NativeMessagingHosts)枚举结果提供了坚实的负命题支撑:
-
MachineGuid不在访问面内:存放MachineGuid的SOFTWARE\Microsoft\Cryptography根本不在主进程的注册表读取列表中。 -
ProfileList与LogonUI的真实用途:代码(偏移286100–288200)虽然枚举了本机所有安全标识符(SID)的ProfileImagePath与登录用户名,但其唯一用途是在生成本地诊断日志时执行路径脱敏(将C:\Users\Alice替换为<user>),并未作为遥测字段上传。 -
网卡 MAC 地址仅驻留内存:
index.chunk-DWDY4R3F.js(偏移257345)中每 3 秒执行一次networkInterfaces()并构造${e.family}|${e.address}|${e.mac}签名,但该签名仅用于在内存中比对网络切换事件,以便在 Wi-Fi 与有线网络切换时打印日志并重探 SSH 控制器(probing SSH controllers),不写入磁盘亦不进入网络载荷。
2. 核心安装标识:ant-did 的生成与跨端注入
Desktop 端的核心持久化标识存储在 %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\ant-did 文件中。其生成逻辑位于 index.chunk-7dsQFims.js(偏移 2045997):直接调用 crypto.randomUUID() 生成纯随机的 UUIDv4,经 Base64 编码后以 0o600 权限落盘;若文件未删除,该值在应用生命周期内保持只读,不随账号切换或重登而改变。
ant-did 在 Desktop 客户端中的覆盖面极广,贯穿以下 8 个场景:
-
每次 Anthropic API 请求:通过请求头
anthropic-device-id发送; -
GrowthBook 特性开关拉取:通过请求头
anthropic-device-id发送(@2053543); -
GrowthBook 实验曝光事件:作为
device_id字段发送(@2059897); -
1P 业务遥测(
event_logging):作为metadata.install_id发送(@3061696); -
自动更新检查:作为 URL 查询参数
device_id=<ant-did>发送(@2400654); -
Sentry 错误上报:直接写入
initialScope.user.id与session.did; -
设备信任注册:作为
getLocalDeviceInfo中的deviceId; -
客户端 UI 菜单:对应界面上的“Copy Installation ID”选项。
此外,当 Desktop GUI 调起内置的 CLI 会话时,存在一处受 GrowthBook 开关 "2220415149" 控制的跨端身份注入逻辑(位于 index.chunk--MCmm2mz.js 偏移 475226):
async seedStableDeviceIdIntoSession(e) { if (!t.h1("2220415149")) return; // GrowthBook 特性开关门控 let n = je(); // 计算 sha256(ant-did) if (n) try { await na(this.getClaudeConfigDir(e), n); // 将哈希值覆写进目标 .claude.json 的 userID 字段 } catch (n) { ... }}一旦该特性开关开启,GUI 会将 sha256(ant-did) 直接写入子 CLI 会话配置文件 .claude.json 的 userID 字段中(不修改 machineID),从而将桌面端与 CLI 端的安装标识关联起来。
3. 三条设备环境数据外发链路与 Sentry 配置指纹
Desktop 客户端向外发送设备环境信息的通道共有三条,其特征粒度呈递进关系:
-
链路 C(每次 API 请求头,
@2045614):携带anthropic-client-device-class(按内存分为le4/8/16/gt16四档)、anthropic-client-total-memory-gb(总内存 GB 数)、anthropic-client-os-platform、anthropic-client-os-version以及anthropic-device-id。 -
链路 B(1P 遥测
/api/event_logging/v2/batch,@3061696):在metadata中额外增加cpu_model(os.cpus()[0].model)、精确内存字节数,以及从注册表读取的 Windows 完整构建标签BuildLabEx(os_build)与累积更新补丁号UBR(os_build_revision)。 -
链路 A(Sentry 错误监控,
index.pre.js @963674):维度最全的设备环境指纹链路。
在主进程初始化 Sentry SDK 时,代码将 initialScope.user.id 设为 w5()(即 ant-did),且 GPU 上下文收集开关 dropGpuContext 仅在命令行显式传入 --disable-gpu 参数时才为真(正常启动一律收集)。直接读取测试机本地落盘的 %LOCALAPPDATA%\...\Claude\sentry\scope_v3.json 快照,可观测到已组装就绪的完整设备上下文:
| 上下文分类 | 字段名 | 实机落盘示例值 | 跨账号稳定性与区分度说明 |
|---|---|---|---|
| 用户标识 | user.id / session.did | 7fcd29ad-3cd3-4eed-96ac-d4da39e250f1 | 等于 ant-did,不删文件则跨账号恒定 |
| 处理器 | device.cpu_description | Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz | 型号级特征,随硬件更换改变 |
| 核心与频率 | processor_count / frequency | 36 / 2295 | 逻辑核数与主频 |
| 物理内存 | device.memory_size | 68563431424 (约 64 GB) | 精确到字节的物理内存容量 |
| 显卡列表 | gpu_1 / gpu_2 / gpu_3 | 双卡 RTX 2080 Ti (0x10de:0x1e07) + Basic Render | 含型号、PCI 厂商与设备 ID、驱动版本 32.0.16.1692 |
| 显示屏幕 | screen_resolution / density | 2880x1800 / 1.5 | 分辨率与系统 DPI 缩放比例 |
| 开机时间 | device.boot_time | 2026-09-17T09:01:15.964Z | 重启即变,但同一次开机内精确到毫秒恒定 |
| 系统与区域 | os.version / timezone / locale | 10.0.26200 / Asia/Shanghai / zh-CN | 操作系统版本与区域设置 |
对上述字段的评估表明:虽然其中没有任何单项属于全球唯一的硬件序列号(例如 PCI ID 0x1e07 仅代表 RTX 2080 Ti 这一显卡型号),但当 “特定 CPU 型号 + 逻辑核数 + 精确内存字节 + 多显卡组合与特定驱动版本 + 屏幕分辨率与缩放比例” 组合在一起时,已在很大程度上构成了高区分度的配置指纹。特别是在单次开机周期内,多个账号若交替登录并触发上报,毫秒级一致的 boot_time 配合相同的硬件配置组合,足以形成强关联信号。
4. 出网注册表(Egress Registry)与遥测开关归属
在 index.chunk-7dsQFims.js(偏移 833800–840500)中,客户端通过声明式出网注册表定义了 11 项外部网络服务及其对应的控制开关:
| 控制开关 | 管辖的服务项 | 对应端点与行为说明 |
|---|---|---|
| 无开关(常驻) | vm-bundle、model-catalog、dictation | 连接 claude.ai(拉取工作区包、5–15 分钟轮询模型目录)及听写服务主机 |
disableAutoUpdates | updater | /api/desktop/.../update 与 /releases/ 自动更新检查 |
disableNonessentialServices | connector-favicons、artifact-sandbox、mcp-app-sandbox、spellcheck-dictionary | Google 图标服务、claudeusercontent.com、claudemcpcontent.com、Linux 拼写词典 gvt1.com |
disableNonessentialTelemetry | anthropic-telemetry | 仅阻断 /api/event_logging/ 使用分析(含渲染层与 CLI 子进程遥测) |
disableEssentialTelemetry | sentry、datadog-rum | 阻断 *.sentry.io 错误上报与 /api/v2/rum 及 7 个 Datadog 域名 |
此处有三个关键事实需要注意:
-
Sentry 归属于“必要遥测(Essential Telemetry)”:仅开启
disableNonessentialTelemetry只能关闭event_logging,无法关闭携带完整设备配置指纹的 Sentry 上报;必须启用disableEssentialTelemetry才会触发y5a("disableEssentialTelemetry")将 Sentry 禁用(且这两个开关在配置定义中仅对3p部署暴露)。 -
Datadog RUM 仅为白名单声明:虽然注册表列出了 Datadog RUM 端点,但全量代码扫描
datadogRum、DD_RUM、@datadog/均为 0 命中;桌面端 Shell 并未打包 RUM SDK,该项仅为远端 Web 渲染层(claude.ai)预留的 CSP 与防火墙出网白名单。 -
崩溃内存转储(Minidump)经由 Sentry 发送:虽然 Electron 原生的
crashReporter.start被设为uploadToServer: false(提交地址为占位符https://f.a.k/e),但 Sentry 集成会主动读取%USERDATA%\Crashpad目录下的.dmp进程内存快照作为附件上传,限制为每会话最多 10 个(maxMinidumpsPerSession)。
四、 TPM 2.0 与设备信任机制(Trusted Devices)分析
CLI 与 Desktop 客户端中均包含一套“设备信任(Trusted Devices)”机制,涉及请求头 X-Trusted-Device-Token、403 untrusted_device 自动注册重试,以及 Desktop 原生模块 claude-native-binding.node 中的 TPM 2.0 硬件调用。
1. 原生层调用链路
通过对 3.8 MB 的 claude-native-binding.node(由 Rust windows-0.58.0 编译)进行字节扫描与符号追踪,还原出其底层加密调用栈如下:
claude.exe → require("@ant/claude-native") (源码路径:src\hardware_key\win32.rs) → NCryptOpenStorageProvider("PlatformCryptoProvider") → NCryptCreatePersistedKey(ECC P-256) → NCryptExportKey(ECCPUBLICBLOB) → NCryptSignHash(...) → 导出公钥并注册至服务端 DeviceRegistry当本机不支持 TPM 或 PlatformCryptoProvider 不可用时,原生模块会降级调用 Windows DPAPI(CryptProtectData / CryptUnprotectData / userDpapi)使用当前系统用户凭据加密保存软件密钥,并在遥测事件 cowork_remote_attestation 中将证明类型标记为 td_v2_software_shim(TPM 可用时标记为 td_v2_hardware)。
2. 为何 TPM 调用不构成“跨账号硬件锁”?
在安全机制上,TPM 的应用分为密钥存储模式(KSP)与硬件平台证明模式(Platform Attestation)两类。前者仅把芯片当作不可导出私钥的密码学协处理器;后者则会读取芯片出厂烧录的唯一背书密钥(Endorsement Key, EK)以证明物理硬件身份。
三项代码证据表明,Claude 客户端仅使用了前一种模式:
-
无背书密钥与硬件证明调用:在原生模块二进制中,针对
NCRYPT_PCP_EKPUB_PROPERTY、EKPUB、Tbsi_Context_Create、Endorsement、Attestation、TPM2_、Quote等平台证明接口的扫描结果全部为 0。 -
密钥容器按账号独立隔离:在 JS 调用层(
@5321772)中,密钥容器的命名函数GZi明确以accountUuid作为隔离维度:
function GZi(accountUuid, generation) { return generation > 0 ? `device-registry-${accountUuid}:g${generation}` : `device-registry-${accountUuid}`;}这意味着账号 A 与账号 B 会分别在 TPM 中生成两套完全独立、随机的 ECC P-256 密钥对。由于椭圆曲线公钥的随机性,服务端无法通过比对两个账号导出的公钥(ECCPUBLICBLOB)来推断它们是否生成自同一颗物理 TPM 芯片。
- 支持密钥代数轮转与重置:客户端通过
ant-device-key-generation.json管理密钥代数(generation + 1即可轮转生成新密钥),且执行系统级Clear-Tpm或重装系统后旧密钥即告失效。
因此,该机制的设计目标是持有性证明(Proof-of-Possession)——防止木马窃取本地 Session Token 后在攻击者机器上异地重放请求,而非用于跨账号硬件锁定。
五、 CLI 与 Desktop 机制横向对比与关联面评估
1. 双端采集与检测特征对照表
| 维度 | Claude Code (CLI 2.1.306) | Claude Desktop (GUI 3.1623.0) |
|---|---|---|
| 打包形态 | Bun 单文件 EXE(内嵌 25.9 MB 明文 JS) | Electron MSIX(40.3 MB JS + 3.8 MB Rust .node) |
系统机器码 (MachineGuid 等) | 含跨平台读取代码,但 hostDetector 零调用(死代码) | 完全移除(15 条注册表访问路径中无 Cryptography) |
| 物理序列号 / MAC 地址 | 未采集 | 未采集(MAC 仅在内存每隔 3 秒比对以重连 SSH) |
| CPU 与内存信息 | 仅上报架构(如 x64) | 完整 CPU 型号、核数、精确内存字节数及内存分档 |
| GPU 与屏幕信息 | 未采集 | 最多 3 块 GPU 的型号/PCI ID/驱动版本 + 分辨率与缩放 |
开机时间 (boot_time) | 未采集 | 采集(精确至毫秒,位于 Sentry 上下文) |
| OS 版本精度 | 基础版本号 | 注册表 BuildLabEx 完整构建标签 + UBR 补丁号 |
| 核心持久化标识 | ~/.claude.json 中的 userID / machineID(64 hex) | %USERDATA%\ant-did(随机 UUIDv4) |
| 硬件密钥机制 | 软件密钥签名(系统密钥链存储) | TPM 2.0 KSP(按账号隔离)或 DPAPI 降级 |
真实主机名 (hostname) | 仅在主动开启云会话或设备 MCP 授权时外发 | 作为设备注册的 displayName(截断 255 字符) |
| Git 远程仓库地址 | 存在灰度采集逻辑(x-claude-code-wiggly-dove) | 未采集(.git/config 仅出现在清理清单中) |
| 双端身份打通 | 独立运行时无关联 | GUI 调起 CLI 时按开关注入 sha256(ant-did) 至 userID |
2. 多账号关联面强度排序
综合客户端所有外发数据通道,服务端可观测到的跨账号关联信号按强度由高到低排序如下:
-
强关联(确定性主键级):持久化安装标识
ant-did(GUI)与userID/machineID(CLI)。只要切换账号时未清理本地配置或缓存文件,所有账号在 API 请求头、遥测及 Sentry 事件中均携带完全一致的 UUID / Hex 标识。若开启了tengu_wiggly_dove灰度,CLI 下同一项目的 Git Origin URL(jy)同样构成强关联。 -
中度关联(网络与会话级):客户端代码不可控但服务端必然可见的源 IP 地址、ASN、代理出口特征、TLS 指纹(JA3/JA4)以及同一次开机周期内毫秒级一致的
device.boot_time。 -
弱至中度关联(环境聚类特征):Desktop 端 Sentry 上报的硬件配置组合(CPU 型号 + 多 GPU 型号/驱动版本 + 内存容量 + 屏幕分辨率与缩放)以及
event_logging中的BuildLabEx + UBR系统补丁版本。此类特征无法单独作为封禁主键,但足以在清理了ant-did后作为风控模型的辅助聚类因子。 -
无关联(客户端不存在该通道):物理硬件序列号、网卡 MAC 地址、Windows
MachineGuid、TPM 背书密钥(EK)。
六、 本地自查与遥测控制参考
若需验证本机客户端生成的持久化标识或控制非必要的数据外发,可通过以下方式进行核查与配置:
1. 查看本地持久化标识与 Sentry 快照
在 PowerShell 中查看 CLI 端的安装级随机 ID:
$j = Get-Content "$env:USERPROFILE\.claude.json" -Raw | ConvertFrom-Json$j.machineID$j.userID查看 Desktop 端的 ant-did 与已组装的 Sentry 设备上下文快照:
# 读取并解码 ant-did$didBase64 = Get-Content "$env:LOCALAPPDATA\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\ant-did" -Raw[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($didBase64))
# 查看 Sentry 本地落盘的设备指纹上下文Get-Content "$env:LOCALAPPDATA\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\sentry\scope_v3.json"2. CLI 端流量与灰度采集阻断环境变量
在 CLI(2.1.306)中,可通过环境变量控制遥测与错误上报行为(对应代码中的 WGt() 判定函数):
# 禁用全部遥测(同时会阻断 GrowthBook 远程拉取,使 x-claude-code-wiggly-dove 灰度开关无法激活)$env:DISABLE_TELEMETRY = "1"$env:DO_NOT_TRACK = "1"
# 禁用 Sentry / Datadog 错误上报$env:DISABLE_ERROR_REPORTING = "1"
# 仅保留必要流量(停止 1P event_logging 遥测)$env:CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1"(注:启用 DISABLE_TELEMETRY 或 DO_NOT_TRACK 后,由于 GrowthBook 远程特性求值一并被阻断,依赖远程版本协商的附加功能如 Remote Control 云会话可能会受到限制。)