Microsoft Windows 中的泄露防护
winver,显示 EULA 哈希和值得保密的警告自 1999 年底以来,Microsoft 采取了多种安全措施,防止尚未发布的 Windows 版本在 Internet 中公开泄露。几乎所有防泄露措施都集中在对可能或可能不打算泄露内部 Windows 版本内容的实体或个人进行指纹识别。然后,可以使用这些指纹追溯到泄漏的源头,并在可能的情况下采取法律行动。
Windows 2000 至 Longhorn 重置后早期版本
指纹识别的最早实现是通过包含 UUIDREF 来观察到的,这些光盘镜像可见于 Microsoft 编写的 Windows 2000、[a]Windows XP、Windows Server 2003、重置前的 Longhorn 以及重置后的早期 Windows Vista。公司内部的光盘镜像预母带实用程序(CDIMAGE)包含一个未公开的 -xx 参数,允许创建带有 FFFFFFFF CRC32 头部的光盘镜像;这些镜像可以轻松修改,将标识符包含在未使用的头部扇区中,同时保持头部和整体 CRC 不变(只需修改 1 千字节连续数据以包含不同标识符)。UUIDREF 值只是一个对终端用户和测试者没有实际意义的 ASCII 字符串,但 Microsoft 可以解码以追踪光盘映像的来源,例如:是哪台机器下载了此版本,或者是谁制作了光盘。
下面是一个示例鉴定器:
;UUIDREF=562D5041534341575C4555524F50452057494E58502D504332
;
标识符是 ASCII 字符串的十六进制表示,随后可以解码为 V-PASCAW\EUROPE WINXP-PC2。这会显示发起员工或自动构建账户(V-PASCAW)的用户名、源域名(EUROPE)以及违规机器的名称或 IP 地址(此处为 WINXP-PC2)。这些标识符在下载过程中应用于光盘映像。
外部测试者的下载后端或系统其他部分也修改了 UUIDREF,其格式为 winbeta_361937[b],其中大概包含测试器 ID 和下载准备时的 IP 地址。
因此,可以追踪泄密到最后一名记录的 Microsoft 员工或外部测试人员,尽管实际这样做的频率尚不清楚。
在 Windows 内部,UUIDREF 戳是在 SMB 服务器(SMB.SYS)实现的,并通过注册表配置(HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters 中的 REG_MULTI_SZ 值:CheckFile 用于测试,此文件在 SMB 读取时内容会被 UUIDREF 值替换(仅使用第一个 REG_MULTI_SZ 条目);CheckExtension 用于文件扩展名,在通过 SMB 读取时将 UUIDREF 值添加到内存中(仅使用第一个 REG_MULTI_SZ 条目);CheckShares,列出在读取文件时启用此功能的共享列表。
如果 ISO 镜像中已有 UUIDREF 值,若从被监控的共享复制到另一个受监控的共享,再从第二个被监控的共享复制,则此值可能会被(部分)覆盖:这可以在 Windows Vista Build 5219.050830-2010 中观察到。
相关代码在 Windows 10 2022 更新中仍保留在 SMB.SYS 中;尽管从某个时间点开始,用于从注册表读取配置的代码已被移除,从那时起此功能变得无法访问。
有效性
这种弱指纹识别方法并不能防篡改,因此该机制只有在泄露的镜像是官方安装媒介的完全未修改副本时才有效。由于许多公开可用的版本出于各种原因被重新打包或以其他方式修改,因此很少有版本在泄露时拥有完整的标识符。
Windows Vista/7
Windows 8 至 Windows 10 11 月更新
在 Windows 8 和 Windows 10 11 月更新开发期间,Windows 指纹识别服务的更新版本以类似最初作为 Windows XP 一部分包含的唯一标识符机制的方式实现,此设计使得无论版本被重新打包多少次,指纹都会出现在每个泄露的 Windows 版本中。
早期的泄露预防实现最初以简单的壁纸替换和 Build 7762 中 winver 的警告形式出现,后来通过在 Build 7785 和 Build 7875 之间引入指纹块和专门的保密警告进行了迭代,这些内容显示在桌面水印上,并通过 shell32.dll 动态链接库显示。指纹直接附加在 BuildLab 字符串末尾(查询注册表键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion),以版本哈希的形式出现(官方称为“EULA 哈希”),通过将 WFS blob 拆分为 8 个 16 字节的数组,并将每个 16 字节的 XOR 连接计算。随后可以推导出 EULA 哈希,以便向现有的 Microsoft 关联公司获取版本泄露;EULA 哈希值的可见性和机密警告取决于某些编译器标志的存在。
注意 EULA 哈希是不同版本(或同一版本的变体,如不同CPU体系结构或合作伙伴发布/下载源[c])独有的,且机密性警告和 EULA 哈希的可见性依赖于特定编译器标志的存在。
指纹识别数据现在既存储在注册表中,也以文件的形式存储,分别在注册表代码 HKLM\SYSTEM\WPA\478C035F-04BC-48C7-B324-2462D786DAD7-5P-9 和 Windows\System32\config 目录中以文件 FP 形式存储。注册表键和文件必须同时存在于 Windows 安装中,否则操作系统要么拒绝启动,要么在启动时用代码 KERNEL_SECURITY_CHECK_FAILURE,运行时在运行时的周期性 WFS 检查失败时,代码为 CRITICAL_STRUCTURE_CORRUPTION 进行非确定性错误检查。此外,大部分指纹数据现在都被加密了,因此需要逆向工程来解码和提取相关信息。由于 Windows 预览体验计划(一个公开倡议)的相关性提升,这些防泄露措施在 Windows 10 周年更新开发周期(Build 11073 之后)非常早期就被移除,此项目是一项公开倡议,允许志愿的测试者(称为 Insider,预览体验成员)访问主线分支中新编译的 Windows 开发版本。
实现细节
指纹数据
指纹数据本质上是一个名为 FP 的 164 字节文件,存储在 Windows\System32\config 目录中。它使用以下结构:
typedef struct _FPData
{
WFSBlob WPACertificateHash; // 加密的 WFS blob
BYTE Time[32]; // nt5.cat 的 SHA-256 哈希
DWORD Type; // 未知;可能是时间戳
} FPData;
WPACertificateHash 是包含实际指纹数据的加密 WFS blob。与普遍看法相反,它与 Windows 产品激活功能无关,只是名称上这么叫作为掩饰。Time 与 nt5.cat 安全目录的 SHA-256 哈希有关,存储在 Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}(同样命名以迷惑逆向工程人员)。Type 值似乎是一种时间戳,可能用于前述安全目录。
WFS blob
Windows 指纹服务 blob(也称为 WFS blob)采用以下结构:
typedef struct _WFSBlob
{
BYTE ValidationSequence[36]; // Magic 'number' (string)
BYTE UserIdHash[32]; // (SHA-256?) hash of user ID
BYTE Nt5Hash[32]; // SHA-256 hash of nt5.cat
BYTE Nt5TimeStamp[12]; // Timestamp of nt5.cat (string)
BYTE BlobCrc[16]; // Truncated SHA-256 hash of the blob
} WFSBlob;
首先必须使用以下公钥 RSA 密钥解密 WFS 数据块:
0000 52 53 41 31 00 04 00 00 03 00 00 00 80 00 00 00 RSA1........€...
0010 00 00 00 00 00 00 00 00 01 00 01 A9 E6 BB AA 49 ...........©æ»ªI
0020 D4 A9 25 6B 22 48 69 85 8C C8 09 D8 DC 68 87 CB Ô©%k"Hi…ŒÈ.ØÜh‡Ë
0030 ED 1E E0 35 31 0B 27 F6 E0 1B 1D F6 54 D5 62 B2 í.à51.'öà..öTÕb²
0040 84 F9 2C 95 2B CD 5B 8C 8C EC B2 05 6E C4 66 5B „ù,•+Í[ŒŒì².nÄf[
0050 9B ED 56 14 07 03 91 39 57 F1 FD 64 DB EB 7E 92 ›íV...‘9WñýdÛë~’
0060 2D 0C AB AD CD FF D4 4B EC B9 84 6C B2 73 75 3D -.«.ÍÿÔK칄l²su=
0070 D3 47 7B F0 0C 24 D4 90 BB C2 71 AF A1 27 65 82 ÓG{ð.$Ô.»Âq¯¡'e‚
0080 D0 51 20 4A 70 0B A4 84 BA 6E 77 97 6E 2B 57 77 ÐQ Jp.¤„ºnw—n+Ww
0090 70 E6 06 11 F7 A1 BD E2 D6 DB 7B pæ..÷¡½âÖÛ{
ValidationSequence 必须等价于字符串 Windows Fingerprinting Service 2.0,用于验证解密后的 blob 是否有效。只有 UserIdHash 在同一版本的不同指纹块中是唯一的,用于追踪泄露源头。Nt5Hash 是 nt5.cat 代码签名目录的 SHA-256 哈希值,其对应的时间戳以字符串形式存储为 Nt5TimeStamp,格式为 YYYYMMDDhhmm(遵循 ISO 8601 国际日期/时间标准)。BlobCrc 是用于完整性验证的部分 SHA-256 哈希。
Nt5Hash 和 Nt5TimeStamp 的值用于确保 blob 仅适用于特定版本,从而防止其他人在新泄露版本上使用之前泄露版本的 blob。时间戳不包含秒字段,以完全忽略因文件系统切换引起的四舍五入。
每次启动时,Windows 内核都会检查磁盘上是否存在指纹数据块,如果不存在则会使用代码 KERNEL_SECURITY_CHECK_FAILURE 进行错误检查,尽管它会加载包含在 Windows 注册表中的指纹数据块。内核会在随机时间(在 300 到 310 秒之间的一次)对注册表中的指纹数据块进行后续完整性检查,如果 Windows 注册表中的数据无效,则会使用代码 CRITICAL_STRUCTURE_CORRUPTION 进行错误检查。
指纹识别方法
当版本被编译时,会生成一个包含适当指纹数据的通用 WFS 块,并使用值 A3CC5B12B2DABE8E47FF9EBA8DD7522269781D4A5F3C7F47B7D0096FDE6B9978 作为其用户 ID 哈希。随后会生成一个包含上述数据的通用 Windows 映像文件;每当安装介质分发时,通用指纹数据会被替换为针对特定个人或组织的变体。
Windows 映像格式
在这些开发期间创建的 Windows 映像文件是非标准的;它们通常在每个元数据资源之后包含四千字节的缓冲区,并且包含 Windows\System32\config\FP 条目的每个元数据资源块保持未压缩状态;这样做是为了便于在尝试下载现有安装媒体时轻松高效地替换指纹数据。由于包含 Windows\System32\config\FP 元数据的块是未压缩的,因此读取或修改其中包含的 SHA-1 哈希值无需进行解压/重新压缩。包含的四千字节缓冲区是为了确保在相关块以某种方式被压缩且在修改后必须重新压缩的情况下,重新压缩的元数据资源能够正确适配。
有效性
这种形式的指纹识别非常有效,因为从理论上讲,在保持操作系统功能的情况下,不可能将其移除。指纹数据可以被完全移除,但这样做会导致操作系统镜像无法启动,或因数据不一致而导致周期性错误检查。指纹数据也无法被修改,因为它已通过私有 RSA 密钥加密。
由于 SYSTEM 注册表配置单元和/或 Windows 恢复环境 WIM 映像中存在通用指纹数据,实际上有一些版本可以无指纹化。某些版本,特别是来源于公开发布分支的版本,例如 th2_release 或 winblue_rtm,也通过编译器标志禁用了 Windows 指纹服务,如果指纹数据无效或缺失,将不会触发错误检查。
Windows 11 开发版本
显示
桌面和 Windows 预安装环境的壁纸主机应用上的水印包含一条通知,提醒 Microsoft 员工不要截取此版本的屏幕截图。从 Build 21376.fs_dev6_flt 开始,屏幕左下角还会额外显示一个十六进制的版本哈希值。
水印中的每个字符串以及文件资源管理器外壳中的字符串都使用了隐写术,空格、冒号和破折号字符会根据 WNF_SHEL_INTERNAL_EXPERIMENT 的值和一个旋转左移指令中使用的固定比特数可预测地替换为它们的 Unicode 全角对应字符,这个旋转值每行总是固定的;使用的旋转值为 0-4,并对每个字符串递增。相同的效果也应用于任务栏时钟以及 winver 中的版权/商标保护字符串(这里使用旋转值 0),并且还尝试用于关于框中的操作系统版本字符串(包含版本号)。由于人为错误,在 Build 21380(以及可能更早的内部版本)中,第一个字符串的指针会再次传入(这可能为 NULL,导致空指针取消引用崩溃,或者是已释放的缓冲区)。当前日历日期的可见性和“显示桌面”按钮的大小会根据上述变量的值进行调整。
Windows 操作系统中的特定外壳界面明确被标记为 DRM 敏感材料,在截图中会显示为纯黑色。
LogonController.dll 动态链接库最初会从位于 HKEY_LOCAL_MACHINE\Software\Classes\CLSID\{2C57C51B-FD43-4E74-B077-551AE6228AD6} 的 DWORD 值 Attributes 中设置变量 WNF_SHEL_INTERNAL_EXPERIMENT,然后再次以二进制值 0D83063EA3B87C75 保存到注册表 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Notifications\Data(最后 4 个字节为 DWORD 值);其中一些比特也负责控制其他功能。
启用和实施细节
现有的大部分功能由 Velocity 暂存功能 ID 31496852 控制,此 ID 在 Firesteel Build 21370 及以后默认启用,内部称为 XPTest。泄漏警告字符串被硬编码在动态链路库 shell32.dll;这些都未被翻译成其他所有语言。
文本被连接到一个缓冲区,测试代码签名水印写入此缓冲区;如果当前启动条目启用测试代码签名,缓冲区会先连接两个额外的句点和空白字符,使水印显示 Test Mode. Do not take screen shots of this build.(测试模式。请勿截图拍摄此版本。);这一点也在公开的 Build 21990 视频中有所体现。
注意水印、哈希值(及其位置)、显示桌面按钮大小、日期/时间格式以及
winver 法律通知在注册表编辑器修改 0D83063EA3B87C75 时,系统重启后动态变化。哈希 ID 的实现方式与 Windows 的旧版主版本不同:
- 获取当前用户的 SID,并将其子权限进行异或(XOR)。
- 获取 WNF 状态值
WNF_SHEL_INTERNAL_EXPERIMENT的 DWORD,并与常数值0x5475A568异或; - 设前两个值为 x 和 y;哈希值初始化为零,然后通过 32 轮计算获得:
hash += hash + _addcarry_u32(0, x, x, &x); hash += hash + _addcarry_u32(0, y, y, &y)
哈希的高度也取决于 WNF_SHEL_INTERNAL_EXPERIMENT 变量的当前值。当它显示在桌面上时,Y 位置是由 WNF_SHEL_INTERNAL_EXPERIMENT 的第 11-12 位得出的:{{{1}}}。
由于上述额外代码,带有哈希 ID 的截图会泄露 WNF_SHEL_INTERNAL_EXPERIMENT 值,这可以用来从哈希中推导出当前用户的 SID(从而揭示 Microsoft 企业网络内的 Active Directory 域名用户)。
Microsoft 会收集唯一的设备 ID,并通过认证 Microsoft 拥有的特定域名来生成指纹。Windows 操作系统中的特定壳面被明确标记为 DRM 敏感材料,截图中会显示为纯黑,且需要从内部 Firesteel 开发分支获取的专门累积更新才能启用其余 Windows 11 壳层功能(如公开版本 Build 22000.51 所示)。安装特定的 MSIX/AppX 包以更新 Windows Shell Experience Pack,依赖于每个设备签署的 FireSteel Token,系统中存在虚假的 Velocity 暂存密钥开关以欺骗数据挖掘者。
此功能在 Build 21996 编译前曾暂时从主线版本中移除,后来在开发 Windows 11 2022 和 2024 更新后功能时重新引入;这些版本中的实现似乎与 Cobalt 开发版本完全相同。
有效性
已经观察到,指纹识别方法会导致现有系统功能的意外中断,因为已知 Build 21380 的原始安装介质是使用错误的元数据制作的;主要安装映像是在执行服务操作时捕获的,此操作会重新生成 WNF_SHEL_INTERNAL_EXPERIMENT 种子,导致无效的访问控制列表和错误的重新解析数据在映像中的数千个文件中持续存在,并且还会导致底层系统服务和内置应用(并间接影响 Windows 外壳的部分功能)出现异常行为。
另行参阅
注
- ↑ 在 Windows 2000 Build 2020.1 和 Windows 2000 Build 2091.1 安装介质上发现
- ↑ 示例来自 Windows XP Build 2525 和 Windows XP Build 2532
- ↑ 如在 Windows 8 Build 8520 中观察到的;尽管其 ARMv7 编译的大多数语言版本包括相同的指纹,但作为简体中文语言版本的一部分包含的指纹完全不同;这很可能是由于另一次 Connect 下载或另一合作伙伴发布的版本交给了另一个个人或组织导致的。