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

如何获取BIOS UUID

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.99/5 (18投票s)

2020年3月27日

MIT

2分钟阅读

viewsIcon

66421

downloadIcon

1802

获取计算机的唯一标识符

引言

在本文网站的另一篇文章中,Michael Haephrati展示了如何检索硬盘序列号,用作唯一的硬件ID。

如何定义“相同的硬件”可能存在争议。大多数人会同意,如果只是更换机箱,它仍然是同一台计算机,而那些因崩溃而不得不更换硬盘的人可能会争辩说他们仍然在使用同一台计算机。

BIOS UUID是一个与主板绑定的唯一数字,我认为如果你更换了主板,就可以放心地说你更换了计算机。从我的角度来看,它是一种更好的硬件识别方法,而且正如我将向你展示的,它非常容易实现。

背景

我们大多数人所说的“BIOS”在技术上称为SMBIOS(系统管理BIOS),其规范由一个名为DMTF的实体管理。如果你问,这些首字母缩写对你来说可能没有任何意义;它们曾经代表分布式管理任务组,但现在只是四个随机字母。

该标准的最新版本可以在这里找到,它是一篇冗长而乏味的文章。

TLDR; 是SMBIOS拥有一个漫长的结构集合,而UUID位于系统信息表中。

访问SMBIOS表只需调用Windows API GetSystemFirmwareTable函数即可。

就是这样:调用GetSystemFirmwareTable函数获取SMBIOS表的起始位置,遍历这些表直到找到系统信息表,然后读取16字节的UUID。剩下的唯一问题是BIOS UUID具有奇怪的字节顺序,并且某些字节需要交换。

代码

所有内容都包含在一个名为bool biosuuid (unsigned char *uuid)的单个函数中。如果成功,它将返回一个包含BIOS UUID的16字节数组。

首先,它调用GetSystemFirmwareTable来检索原始SMBIOS数据

  DWORD size = 0;

  // Get size of BIOS table
  size = GetSystemFirmwareTable ('RSMB', 0, smb, size);
  smb = (RawSMBIOSData*)malloc (size);

  // Get BIOS table
  GetSystemFirmwareTable ('RSMB', 0, smb, size);

每个BIOS块有两个部分,一个格式化的(已知长度)部分和一个未格式化的部分。格式化的部分以类型和长度开头。该函数遍历连续的块,直到找到类型为0x01的系统信息块

  //Go through BIOS structures
  data = smb->SMBIOSTableData;
  while (data < smb->SMBIOSTableData + smb->Length)
  {
    BYTE *next;
    dmi_header *h = (dmi_header*)data;

    if (h->length < 4)
      break;

    //Search for System Information structure with type 0x01 (see para 7.2)
    if (h->type == 0x01 && h->length >= 0x19)
    {
      data += 0x08; //UUID is at offset 0x08

有效的UUID不应该只包含零或只包含一

      // check if there is a valid UUID (not all 0x00 or all 0xff)
      bool all_zero = true, all_one = true;
      for (int i = 0; i < 16 && (all_zero || all_one); i++)
      {
        if (data[i] != 0x00) all_zero = false;
        if (data[i] != 0xFF) all_one = false;
      }

现在我们只需要复制UUID,注意字节顺序问题

      if (!all_zero && !all_one)
      {
        /* As off version 2.6 of the SMBIOS specification, the first 3 fields
        of the UUID are supposed to be encoded on little-endian. (para 7.2.1) */
        *uuid++ = data[3];
        *uuid++ = data[2];
        *uuid++ = data[1];
        *uuid++ = data[0];
        *uuid++ = data[5];
        *uuid++ = data[4];
        *uuid++ = data[7];
        *uuid++ = data[6];
        for (int i = 8; i < 16; i++)
          *uuid++ = data[i];

        result = true;
      }
      break;
    }

如果我们没有找到该块,我们必须跳过块的未格式化(可变长度)部分,前进到下一个块。未格式化部分的结尾由两个0x00字节标记

    //skip over formatted area
    next = data + h->length;

    //skip over unformatted area of the structure (marker is 0000h)
    while (next < smb->SMBIOSTableData + smb->Length && (next[0] != 0 || next[1] != 0))
      next++;
    next += 2;

    data = next;

就这样!在100行代码中,你获得了主板的唯一标识符。

历史

  • 2020年3月27日 初始版本
© . All rights reserved.