Introduction #
In an article on CodeProject, Michael Haephrati shows how to retrieve the hard drive serial number to be used as an unique hardware ID.
It can be up for debate how you define the “same hardware”. Most people will agree that if you just change the case it is still the same computer, while people who had to replace a hard drive because of a crash might be arguing that they are still using the same computer.
The BIOS UUID is a unique number tied to the motherboard and I feel that if you changed your motherboard you can safely say you changed your computer. From my point of view, it is a better method of identifying your hardware and, as I’ll show you, it is very easy to implement. Background
What most of us call “the BIOS” it is technically called SMBIOS (System Management BIOS) and its specification is managed by an entity called DMTF. If you ask, those initials don’t mean anything; they used to stand for Distributed Management Task Force but not any more: they are just four random letters.
The latest version of the standard can be found at https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.3.0.pdf and it makes for a very long and tedious reading.
The TLDR; is that the SMBIOS has a loooong collection of structures and the UUID is in the System Information table.
Getting access to the SMBIOS tables is just a call to Windows API GetSystemFirmwareTable
function.
That’s all there is: call GetSystemFirmwareTable
function to get the start of SMBIOS tables, iterate through those tables until you find the system information table, and read the 16 bytes of UUID. The only remaining quirk is that BIOS UUID has a strange byte ordering and some bytes have to be swapped.
The code #
Everything is one single function bool biosuuid (unsigned char *uuid)
. If successful, it returns a 16 byte array with the BIOS UUID.
First it calls the GetSystemFirmwareTable to retrieve the raw SMBIOS data:
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);
Each BIOS block has two parts, a formatted (known length) part and an un-formatted part. The formatted part starts with a type and a length. The function goes through successive blocks until it finds the System Information block with type 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
A valid UUID should not consist of only zeroes or only ones:
// 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;
}
Now we just have to copy the UUID taking care of the byte ordering issue:
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;
}
If we haven’t located the block, we have to advance to the next one skipping over the un-formatted (variable length) part of the block. The end of the un-formatted part is marked by two 0x00 bytes:
//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;
That’s all! In 100 lines of code you’ve got a unique identifier for the motherboard.
Show me teh codez #
The code is on GitHub as part of mlib library
History #
27-Mar-2020 Initial version 01-Jan-2023 Added link to code