基于截断哈希的安装 ID






4.93/5 (29投票s)
使用截断哈希和 Crypto++ 创建半匿名安装指纹
引言
本文是对 基于 RSA 签名的产品激活 的补充。它将向读者提供一个框架,用于在保持一定程度用户匿名性的同时唯一标识软件安装。如果使用 产品激活系统,人们会希望有一个唯一的安装标识,因为该系统有助于阻止盗版、中和密钥生成器并开发最终用户的人口统计数据。
为了表示一次安装,将使用 WMI 来确定已安装的硬件。为了实现匿名性,示例将采用 Crypto++ 的截断哈希。
本文在 Windows Vista、Windows Server 2003、Windows XP SP2 和 Windows 2000 SP4 上进行了测试。标准用户帐户和管理员帐户均按预期运行。
本文将介绍以下主题
- 知识产权
- 背景信息
- 设置 API
- Windows 注册表
- 主机的 RSA 公钥
- WMI
- 截断哈希
- 机器信息
- 编译和集成Crypto++
- SHA-512
- 故障排除 WMI
- 操作系统依赖行为
- Windows 2000
- Windows XP
- Windows Server 2003
- Windows Vista
- 示例代码
- 截断位数
- 碰撞
- WMI 中的设备位置
- 安装指纹
- 操作系统升级效果
- 摘要
知识产权
微软拥有一项与本文密切相关的专利。虽然安装指纹似乎没有被专利化,但容错指纹却被专利化了。在美国,专利号为 6,243,468,“软件防盗版系统,可适应硬件升级”。在欧洲,专利号为 EP1452940。应通过以下方式联系微软以获取许可:
微软公司
法律部
One Microsoft Way
Redmond, WA 98052
微软还提供电子邮件地址 http://www.microsoft.com/legal/。然而,作者未能从列出的电子邮件中收到回复。
背景
在研究用于盘点计算机系统硬件的方法时,脑海中立刻浮现出两个想法:Windows 注册表和设置 API。每种方法都有其自身的不足之处,如下所述。
在主机指纹(不进行硬件识别)的上下文中,还有一个额外的选择——主机的 RSA 公钥。然而,这也存在一个缺点,如下所述。
设置 API
有关使用设置 API 的示例,请参阅 A. Raiza 的 使用设置 API 枚举已安装设备。A. Raiza 提供的方法存在一个问题,即关键信息不易获得。例如,处理器详细信息无法直接获得。
此外,诸如特定主板插槽安装的内存大小之类的信息也无法获得。
Windows 注册表
Windows 注册表存在与设置 API 相同的限制——易于获得的信息有限。
主机的 RSA 公钥
计算机的 RSA 密钥用于,例如,在计算机与域控制器之间建立安全通道。然而,尚不清楚机器的 RSA 密钥对何时(如果)刷新,以及标准用户是否因安全限制而能够访问密钥材料。作者知道 S-Channel 自动重置,但对具体如何实现并不熟悉。
推测的 RSA 缺点令人失望——可以轻松创建 RSA 公钥的哈希(无需 ASN.1 DER 解码)。RSA 密钥满足唯一性和匿名性的要求。
WMI
WMI 不仅允许程序员从操作系统中提取基本的“命名”信息,还可以轻松确定额外的对象属性,如序列号(当由操作系统提供时)。此外,如果软件用户在格式化硬盘后安装新的操作系统,然后重新安装以前激活的产品,软件将不需要额外的重新激活。
有关 WMI 的更多信息,请参阅 Aamir Butt 的 在 Visual C++ 中获取 WMI 信息 和 Martin Friedrich 的 在 C++ 中执行 WMI 查询。最后,您可以在 WMI C++ 应用程序示例 中找到微软的 WMI 示例。
截断哈希
简单来说,截断哈希会丢弃哈希函数输出的某些位,从而得到一个更短的摘要。理论上,如果需要一个 128 位摘要,可以选择
- SHA,保留所有 128 位
- SHA-256,丢弃 128 位
- SHA-512,丢弃 384 位
这假定一个理想化的哈希——或者一个哈希中的每个位都有 50% 的几率取值为 0 或 1。这意味着截断的 256 位哈希与 128 位哈希具有相同的加密安全性。实际上,没有哈希是理想化的。
本文的匿名性要求允许使用缩短的摘要。与 确定文档修改状态的确定性方法 不同,该方法使用 CRC 上的哈希来避免冲突,此处期望一定程度的冲突以提供匿名性。期望两个不同、独立的设备产生相同的截断哈希,只是不希望它过于频繁地发生。换句话说,给定一个截断哈希,不能确定地说,“这是一个 Abit 主板”,或者“这是一张 GeForce 显卡”。
有关使用截断哈希的 IETF 提议实现,请参阅 主机标识协议 (HIP) 域名系统 (DNS) 扩展。
机器信息
用于创建安装指纹的机器信息包括 BIOS、处理器、内存、硬盘和网卡信息属性。
读者应探索扩展指标以开发指纹。例如,应使用 CD-ROM、DVD 和声卡信息来扩展熵。各种 WMI 类文档可在 Win32 类 中找到。
下载次数
本文共有四个下载文件。它们在文章末尾提供。下载主题是
- 示例 1 - 基本 WMI 程序
- 示例 2 - 检索 BIOS、处理器、内存、硬盘和网卡信息
- 示例 3 - 截断哈希
- 示例 3 发布版 - 为方便起见提供的发布版
编译和集成Crypto++
Crypto++ 可从 Wei Dai 的 Crypto++ 页面下载。有关编译和集成问题,请参阅 在 Microsoft Visual C++ 环境中编译和集成 Crypto++。本文基于先前文章中提出的基本假设。
对于对其他 C++ 加密库感兴趣的人,请参阅 Peter Gutmann 的 Cryptlib 或 Victor Shoup 的 NTL。
SHA-512 哈希函数
本文将使用 SHA-512 作为哈希函数。它是 SHA-2 系列哈希的成员。SHA-2 哈希由 FIPS 强制用于联邦使用;并被 ISO 认可为国际标准。
对于那些希望使用平面 C 文件和非 NIST 建议的人,《ISO》认可 RIPEMD 和 WHIRLPOOL(除了 SHA)。RIPEMD 和 WHIRLPOOL 都在 Crypto++ 中实现。
- RIPEMD: http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
- WHIRLPOOL: http://paginas.terra.com.br/informatica/paulobarreto/
故障排除 WMI
微软的 TechNet 网站有一篇很好的文章,题为“WMI 无法正常工作”。如果读者遇到脚本故障或 WMI 服务问题,应参考本文。TechNet 文章包含一个 WMI 诊断工具。
操作系统依赖行为
在哪里介绍本文的特殊情况带来了一个小问题。尽管差异很少,但支持的屏幕截图很快就使文章部分内容过长。因此,现在将详细介绍勘误。总的来说,随着家庭的成熟,每个操作系统使用 WMI 显示的信息都更多。
总的来说,没有操作系统从 WMI 的 Win32_MotherBoards 类返回任何有价值的信息。通常以 Caption、Description 和 Name 返回的有用信息被表示为“Motherboard”。
Windows 2000
Windows 2000 存在不少缺点。当标准用户尝试实例化 IWebAdminstratorLocator
类时,会发出 COM 错误 0x80041014。当尝试使用 IWebLocator
连接到 WMI 服务时,标准用户会收到 COM 错误 0x80041008 (E_INVALID_PARAMETER)。
管理员在使用 IWebLocator
接口连接到 WMI 服务时也收到了 0x80041008 (未实现)。这让作者认为 IWebLocator
已损坏或使用不当,并且 WMI 服务需要调整权限,以允许标准用户使用 IWebAdminstratorLocator
连接。
最后,Windows 2000 在网络适配器方面非常详细,总共返回了七个适配器——包括 RAS Async 和多个 WAN Miniport 适配器。在示例 3 中,将通过优化 WQL 语句(添加 WHERE 子句)来纠正这种冗余。
Windows XP
Windows XP 缺少硬盘序列号,并且在网络适配器方面略显冗余。
Windows Server 2003
Server 2003 正确返回了网络适配器信息,但没有返回硬盘序列号等项目。
Windows Vista
Vista 正确返回了所有信息,但网络适配器信息略有偏差——添加了 Teredo Tunneling Pseudo-Adapter。
操作系统比较
为了进行比较,下面根据操作系统复制了示例 2 的输出。
Windows 2000 |
Windows XP |
Windows Server 2003 |
Windows Vista |
示例 1
第一个示例演示了连接到 WMI 服务。它包括对 CoInitializeEx()
和 CoInitializeSecurity()
的调用;以及实例化 IWbemLocator
和 IWbemServices
。MSDN 和之前提到的 Code Project 文章都对此有详细介绍,因此此处不再提供详细信息。
示例 2
第二个示例是一个重复的练习。对于要查询的每个类,都会调用 IEnumWbemClassObject
的 Next()
方法,直到耗尽。每次调用时,都会检索相关数据(例如,名称或 MAC 地址)并将其打印到控制台。简化的代码如下所示。
CComPtr< IEnumWbemClassObject > pEnumerator = NULL; pService->ExecQuery( CComBSTR( L"WQL" ), CComBSTR( L"Select * from Win32_<Class Of Interest>" ), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator ); for( UINT i = 0; NULL != pEnumerator; i++ ) { CComPtr< IWbemClassObject> pObject = NULL; ULONG uReturn = 0; VARIANT vtProperty; cout << "Device " << i << endl; hr = pEnumerator->Next( WBEM_INFINITE, 1, &pObject, &uReturn ); if( FAILED( hr ) || 0 == uReturn ) { break; } hr = pObject->Get( L"Name", 0, &vtProperty, 0, 0); if( SUCCEEDED( hr ) ) { cout << _T(" Name: "); if( VT_EMPTY != vtProperty.vt && VT_NULL != vtProperty.vt ) { cout << vtProperty.bstrVal << endl; } else { cout << "Not Available" << endl; } } }
下面是示例 2 的典型输出(取自 Windows Server 2003)。检索用户名时出错是由于 Windows Server 2003 主机存在问题。
请注意,查询的属性因设备而异。这是因为操作系统在显示对象数据方面并不一致。例如,对于 BIOS,“Name”很重要,但对于内存则不重要。要确定什么是有意义的,读者必须编写演示代码。似乎没有这个信息的来源。
示例 3
第三个示例将 WMI 对象引入截断哈希。示例 2 和示例 3 之间的变化如下:
- 添加 cbHash[ SHA512::DIGEST_SIZE ]
- 添加哈希逻辑
- 移除了不相关的字段
- 扩展了 WMI 查询以包含基本的 WHERE 子句
此示例生成了一个截断哈希,其中包括:
- BIOS 名称
- 处理器速度
- 内存条大小
- 硬盘型号
- 网卡 MAC 地址
下面是示例三在 Windows Vista 上运行的结果。请注意,内存现在以字节为单位显示为字符串(示例二使用 _wtoi()
进行了转换)。
截断哈希的创建方式如下。请注意,即使创建了完整的 512 位 SHA 哈希,也只保留了一小部分,使用了位掩码 0x3F (0011 1111),它丢弃了除第 0 字节低序 6 位之外的所有位。
szValue = vtProperty.bstrVal; std::wcout << (wchar_t*)szValue << std::endl; hash.Update( (PBYTE)(BSTR) szValue, szValue.Length() * sizeof( WCHAR ) ); hash.Final( cbHash ); cout << _T(" Truncated Hash: 0x"); cout << std::hex << std::setw(2); cout << setfill( '0' ) << std::uppercase; cout << ( cbHash[ 0 ] & 0x3F ); cout << std::endl;
用于过滤适配器的 WQL 如下。请注意,AdapterType
和“Ethernet 802.3”在 WMI 的 Win32_NetworkAdapter 类文档中都有完整记载。
hr = pService->ExecQuery( CComBSTR( L"WQL" ), CComBSTR( L"Select Name, MACAddress from Win32_NetworkAdapter " \ L"WHERE AdapterType=\"Ethernet 802.3\"" ), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumNetworkAdapter );
下面是使用更窄的 WQL 查询在 Windows 2000 上运行示例程序的結果。
最后,为完整起见,下面显示了 Windows Server 2003。
截断位选择
作者根据对本文的背景研究选择了 6 位。六位可以产生 2^6 或 64 个不同的值。鼓励读者尝试不同的尺寸。由于作者没有字符串集合来测试和开发指标,因此他无法提供关于 6 位相对于其他可能选择的有效性的实证数据。
如果读者选择创建处理器制造商字符串的截断哈希,6 位可能不是一个合适的选择。WMI 似乎返回 CPUID 指令的结果。对于 Intel,该字符串是“GenuineIntel”。作者知道七个 x86 兼容制造商:AMD、Cyrix、IBM、IDT、Intel、NEC 和 Transmeta。在这种情况下,由于匿名性限制,两位可能是一个更好的选择。
使用哈希的位数更少的最终示例是使用操作系统作为指标。本文调查了四个版本:Windows 2000、Windows XP、Windows Server 2003 和 Windows Vista。在这种情况下,1 位对于匿名性来说将是一个合适选择,因为 2^1 = 2。
碰撞
在 Windows Vista 的处理器(799 MHz)和 Windows XP(231 MHz)的截断哈希中观察到了一个冲突。覆盖图如下所示。读者还应从上方注意到,Windows 2000(731 MHz)产生了值 0x1F。作者可以向读者保证,完整的 512 位哈希是不同的——在不观察任何一个的完整哈希的情况下。
在 Windows Server 2003 和 Windows Vista 关于硬盘方面似乎存在另一个冲突。然而,两者都安装了 IBM Deskstar(同一型号)。因此,这个冲突本身并不是真正的冲突。
设备位置
有时,设备的位置并不明显。例如,当查询网络适配器时会返回一个 USB 无线网卡,而 SCSI Orb 磁带驱动器则会被归类到硬盘驱动器中。
安装指纹
在上面提供的示例中,简单的字符串连接足以创建标识指纹。在下面的示例(取自 Windows 2000)中,字符串将是 27:1F:11:11:3E:19。
现在,假设用户将两个 256 MB 的内存条更换为 512 MB 的内存条。签名将从 27:1F:11:11:3E:19 变为 27:1F:22:22:3E:19。请注意 11 的加倍——这是巧合。它是算法的输出,而不是字面上的标量加倍。以 Windows Server 2003 为例,128 MB 截断为 0x30。
鼓励读者开发一个加权系统,以便小的硬件更改不需要用户重新激活。有关加权讨论的更多信息,请参阅 基于 RSA 签名的产品激活。
操作系统升级
下面是已升级到 Windows XP 的 Windows 2000 安装。请注意,作者执行的唯一更改是操作系统和硬盘升级。尽管物理设备与前一个操作系统安装相同,但 BIOS 名称和网卡信息已发生变化。
摘要
本文向读者介绍了唯一标识安装的基础。在实施其系统时,除了简洁性和适度使用技术(由于涉及个人用户信息交换)之外,读者应牢记的关键点是:
- 可以基于已安装的硬件生成指纹
- 并非 WMI 查询中返回的所有字段都有用
- 不同版本的 Windows 填充 WMI 查询的方式不同
- 硬件不总是在一个明显的 WMI 设备类别中
- 截断哈希通过创建冲突来增加匿名性
- 由于截断,几乎任何哈希选择都是合适的
下载次数
- 下载 Sample1.zip - 3.8 KB
- 下载 Sample2.zip - 5.3 KB
- 下载 Sample3.zip - 5.3 KB
- 下载 Sample3.zip - 发布版 - 2,732.1 KB
致谢
- Wei Dai for Crypto++ 及其在 Crypto++ 邮件列表上的宝贵帮助
- A. Brooke Stephens 博士,他为我打下了密码学基础
- James Snyder(RFID Solutions 所有者)感谢他分享了自己的想法
- Dr. Vasiliy Smirnov(www.DiscoveryBiz.net)感谢他分享了自己的想法
修订
- 2007.05.12 文章重新分类
- 2007.05.12 扩展了 Windows Vista 信息
- 2007.12.09 扩展了“截断位选择”部分
- 2007.12.06 添加了操作系统升级效果
- 2007.12.06 添加了示例 3 发布版
- 2007.11.06 从 Windows 2000 升级后添加了 Windows XP 指纹
- 2007.10.06 扩展了有关截断位的信息
- 2007.09.06 初始发布
校验和
- Sample1.zip
MD5: 9BD62E560E9027224A6EE66C5E149B45
SHA-1: 17E3ED48125B3DF311BA9719FCEF360276761F0B - Sample2.zip
MD5: 82AD788AC3F413461CB2D8F9218EA05D
SHA-1: A69C2312FB67BE53F07465C6A6AB7721A04FC7FE - Sample3.zip
MD5: 87FB2486B979DFC58C7B13C229D8ABF3
SHA-1: 47038167029AD4DABBB3D13C33886052C7BCE811 - Sample3RelExe.zip
MD5: 9F7172F914692A6E96206AA701E8E1C2
SHA-1: 4C7A186959189BBE227159B35FA112FDB40F945E