65.9K
CodeProject 正在变化。 阅读更多。
Home

基于截断哈希的安装 ID

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (29投票s)

2007 年 6 月 9 日

CPOL

13分钟阅读

viewsIcon

71557

downloadIcon

2316

使用截断哈希和 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》认可 RIPEMDWHIRLPOOL(除了 SHA)。RIPEMD 和 WHIRLPOOL 都在 Crypto++ 中实现。

故障排除 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() 的调用;以及实例化 IWbemLocatorIWbemServices。MSDN 和之前提到的 Code Project 文章都对此有详细介绍,因此此处不再提供详细信息。

示例 2

第二个示例是一个重复的练习。对于要查询的每个类,都会调用 IEnumWbemClassObjectNext() 方法,直到耗尽。每次调用时,都会检索相关数据(例如,名称或 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 设备类别中
  • 截断哈希通过创建冲突来增加匿名性
  • 由于截断,几乎任何哈希选择都是合适的

下载次数

致谢

  • 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
© . All rights reserved.