The BIOS UUID is a unique number tied to the motherboard and 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 in this tip, it is very easy to implement.
Introduction
In another article on this site, Michael Haephrati shows how to retrieve the hard drive serial number to be used as a 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" 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 here 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;
size = GetSystemFirmwareTable ('RSMB', 0, smb, size);
smb = (RawSMBIOSData*)malloc (size);
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
:
data = smb->SMBIOSTableData;
while (data < smb->SMBIOSTableData + smb->Length)
{
BYTE *next;
dmi_header *h = (dmi_header*)data;
if (h->length < 4)
break;
if (h->type == 0x01 && h->length >= 0x19)
{
data += 0x08;
A valid UUID should not consist of only zeroes or only ones:
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)
{
*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:
next = data + h->length;
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.
History
- 27th March, 2020 Initial version