Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

How to Get the BIOS UUID

4.99/5 (18 votes)
29 Mar 2020MIT2 min read 64.7K   1.8K  
Get a unique identifier for a computer
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:

C++
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:

C++
//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:

C++
// 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:

C++
  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:

C++
//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.

History

  • 27th March, 2020 Initial version

License

This article, along with any associated source code and files, is licensed under The MIT License