微软的“全局设备标识符”——2026年7月Scattered Spider起诉书中提到的持久性Windows指纹——实际是如何生成、存储和传输的。
[!NOTE] 以下内容属实,但缺少部分信息。无论是否使用MSA登录,你都会拥有一个GDID。我发帖时并未意识到这一点,但后来查证了。CDP有一个匿名设备路径,如果未连接MSA则会使用该路径。底层系统在事实上仍然是正确的,只是缺少了一些内容。
Global Device Identifier g:6755467234350028。g:<十进制数>。wlidsvc(Microsoft账户服务)向login.live.com注册设备并获取设备PUID -> 存储在注册表中 -> Connected Devices Platform(cdp.dll / CDPSvc)读取并将其注册到设备目录服务(DDS) 图中 -> Delivery Optimization将其报告为文档化的UCDOStatus.GlobalDeviceId。[!NOTE] 置信度标记。 每个声明都带有标签,供您自行判断:
[COURT]主要来源事实,[OBSERVED]在我的测试机上实机复现,[STATIC]从二进制文件和公开Windows PDB中证明,[ASSESSED]基于证据的强推理。
wlidsvc)2026年7月1日,美国司法部解封了一份针对Peter Stokes的刑事起诉书,他涉嫌是Scattered Spider(又名Octo Tempest / UNC3944 / 0ktapus)的成员。宣誓书描述了微软如何帮助FBI将活动归因到一台设备。
[!IMPORTANT]
[COURT]来自补充起诉书(¶25,第34页),原文如下:“ngrok账户是通过全局设备标识符 g:6755467234350028(‘GDID’)设置的。根据微软代表的说法,Windows生态系统中的全局设备标识符是一个持久的、设备级别的标识符,旨在唯一标识设备上某个Windows操作系统安装……GDID是一个全局唯一标识符,与设备上Windows的安装绑定。GDID在设备上的Windows操作系统更新期间保持不变,但重新安装Windows……将绑定一个新的唯一GDID。”
脚注补充说,一个Microsoft用户可以有多个GDID。宣誓书接着将GDID的IP历史记录和浏览记录(例如empirehotelnyc.com,一个Growtopia/Ubisoft登录URL)与嫌疑人登录的账户关联起来。
这里有两件事支撑了整份报告:
g:加上一个十进制整数**(g:6755467234350028)。十六进制为0x0018000FC8CB93CC,所以是一个64位数字。社交媒体上的总结声称GDID是*“一个由安装时的序列号生成的128位标识符”*。两个部分都是错误的:
| 说法(社交媒体) | 现实(主要来源) |
|---|---|
| “128位” | 起诉书中的值是g:6755467234350028,一个适合64位的十进制数(0x0018000FC8CB93CC)。 |
| “由安装时的序列号生成” | 起诉书称重新安装会产生新的GDID。由固定序列派生出的值在重新安装后会恢复原样,不会改变。 |
[!NOTE] 在对CDP进行更多逆向分析后,我提供了一些错误信息。使用本地账户并不能阻止GDID的产生。CDP有一个匿名设备路径,如果未连接Microsoft账户则会采用。阅读时请记住这一点。
[STATIC] 微软公开的Azure Monitor文档在**UCDOStatus**(更新合规 / Delivery Optimization)表中定义了一个GlobalDeviceId列:
GlobalDeviceId(字符串):“Microsoft全局设备标识符。这是Microsoft内部使用的标识符。”
它与LastCensusSeenTime、ISP、City、Country相邻,因此一个设备ID与地理位置和IP相关联。这是Microsoft在公开文档中命名该值的唯一地方。但Delivery Optimization仅仅报告它。重要的是,它并不拥有这个标识符。向上游追踪,你会落脚于Connected Devices Platform。
[STATIC] C:\Windows\System32\cdp.dll(Connected Devices Platform,服务为CDPSvc + CDPUserSvc)包含GlobalDeviceId符号和一个完整的设备目录服务注册子系统:
ddsregistrationclient.cpp ddsregistrationmanager.cpp ddsregistrationinfo.cpp
DdsRegistrationClient RegisterUserDevicesObserver DdsRegistrationInfoProviderForCDP
endpoints: dds.microsoft.com fd.dds.microsoft.com aad.cs.dds.microsoft.com cdpcs.access.microsoft.com
device-id format string: "g:%s"
DDS = 设备目录服务,微软的跨设备身份图(Phone Link、云剪贴板、“在电脑上继续”、Nearby Share的后端)。CDP是Windows客户端,负责将安装注册到该图中,并以g:<十进制数>的形式作为键。
[OBSERVED] 强制进行全新注册(清除CDP本地状态后重启CDPSvc),并捕获CDP自己的ETW提供程序,产生了整个握手过程:
DdsClient::RegisterUserDeviceAsync() RegistrationReason: Startup Account Type: MSA
DDSClient: Registration response received. HTTP status code: 200
OnRegisterUserDeviceComplete
GetDeviceIdAndTicketActivity -> deviceid: 0018XXXXXXXXXXXX
那个deviceid以g:<十进制数>形式写入,与起诉书中的值在结构上匹配:
| 值 | 十六进制(64位) | 类别前缀 | |
|---|---|---|---|
| 我的机器(已打码) | g:XXXXXXXXXXXXXXXX | 0x0018XXXXXXXXXXXX | 0018 |
| 法院证据 | g:6755467234350028 | 0x0018000FC8CB93CC | 0018 |
两者都是位于相同0x0018高字类别(设备PUID命名空间,参见§6)中的64位值。g:前缀只是该整数的十进制形式。
[STATIC] 借助公共PDB(cdp.pdb),cdp.dll中的设备ID路径仅仅是对身份栈的请求和等待。CDP自身从不计算该标识符:
flowchart TD
A["GetStableDeviceIdFromProvider<br/>0x0A3140"] --> B["provider.GetStableDeviceIdAsync<br/>(vtable +0x48)"]
B --> C["OneCoreAccountProvider::<br/>GetStableDeviceIdAsync 0x0C8370"]
C --> D["IWebAccountBackedAccountProvider<br/>(MSA / AAD identity COM)"]
D --> E["OnGetStableDeviceIdCompleted<br/>(const char* deviceId) 0x06CEA0"]
E -->|"assign() string, signal flag"| A
接收它的回调函数显而易见。该ID以字符串形式出现,并且只是被存储:
; OnGetStableDeviceIdCompleted
mov rbp, r9 ; r9 = device-id STRING handed in by the identity provider
lea rcx, [rsi+0xD8] ; CDP member field
mov rdx, rbp
call assign@basic_string ; store it, no computation, no serials
call Set@CdpWaitableFlag ; unblock the waiter
结论: GDID在CDP之下铸造,位于Windows身份栈底层,作为不透明字符串传递给CDP。这指向了Microsoft账户服务。
wlidsvc)[STATIC] C:\Windows\System32\wlidsvc.dll,Microsoft账户/Passport(Windows Live ID)服务,是唯一包含字面值GlobalDeviceId的身份二进制文件,并且拥有完整的设备注册机制:
CDeviceIdentityBase::CreateNewDeviceIdentity / Provision / BindDeviceToHardware / GetDeviceCert
DeviceAssociateRequest (Passport PPCRL SOAP -> login.live.com)
<ps:DevicePUID> ... </ps:DevicePUID>
DeviceIdStore::LogToRegistry
BCryptGenRandom / CCryptRandom::GenRandom (device KEY, not the id)
该标识符是一个设备PUID(Passport唯一标识符),一个64位的MSA标识符。其中的BCryptGenRandom生成的是设备身份验证密钥,由BindDeviceToHardware固定到机器上,而不是 PUID。
[STATIC] 客户端从服务器的SOAP响应中提取PUID,使用XPath定位响应体中的元素:
/S:Envelope/S:Body/ps:DeviceUpdatePropertiesResponse/HWPUIDFlipped
CAssociateDeviceRequest::ParseResponseBody及相关的ParseResponse方法将响应XML节点读入BSTR。因此流程是:客户端注册设备 -> login.live.com分配并返回设备PUID -> 客户端存储。 这正是为什么重新安装会产生新的GDID(新注册,新服务器分配的PUID),以及为什么它不是硬件哈希。
[OBSERVED] Microsoft账户身份存储将该值直接保存在注册表中,位于你自己的用户配置单元中:
HKCU\SOFTWARE\Microsoft\IdentityCRL\ExtendedProperties
LID = 0018XXXXXXXXXXXX
HKCU\SOFTWARE\Microsoft\IdentityCRL\Immersive\production\Token\{...}
DeviceId = 0018XXXXXXXXXXXX
与CDP注册进DDS时的值逐字节相同。你的用户账户PUID是一个不同的数字,存储在其他地方,格式为puid = 0003...(例如00034002XXXXXXXX)。还有一个以PUID命名的HKLM缓存键(HKLM\SOFTWARE\Microsoft\IdentityCRL\NegativeCache\<PUID>_<userSID>),但那个只能由SYSTEM访问,因此你从HKCU读取该值。
[!NOTE] 前缀告诉你它是什么。 用户PUID为
0003类别,设备PUID为0018类别。法院的GDID(0018000FC8CB93CC)位于0018设备PUID空间中,与我的相同。
[OBSERVED] MSA令牌缓存(HKLM\SOFTWARE\Microsoft\IdentityCRL\NegativeCache\...)包含范围限定为确切CDP所使用的端点的设备令牌:
scope=service::dds.microsoft.com::MBI_SSL_TOKEN_BROKER
scope=service::activity.windows.com::MBI_SSL_SA_TOKEN_BROKER
因此,Microsoft账户服务发放的设备凭据可以验证DDS注册以及承载GDID的活动上传。