1

In the Windows SDK there is a CLI tool named computerhardwareids

The tool returns various GUIDs to select the proper HardwareId for the specific case.

This is the output that returns this tool in my PC:

Using the BIOS to gather information

Computer Information
--------------------

BIOS Vendor: American Megatrends Inc.
BIOS Version string: 1201
System BIOS Major Release: 4
System BIOS Minor Release: 6

System Manufacturer: To be filled by O.E.M.
System Family: To be filled by O.E.M.
System ProductName: To be filled by O.E.M.
SKU Number: SKU

Enclosure Type: 03 "Desktop"


Hardware IDs
------------
{a8670b03-1d98-5e95-ad4e-c64211eac9df}    <- Manufacturer + Family + ProductName + SKUNumber + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release
{01c6b2a2-a2b2-58e4-906d-4677639f1a42}    <- Manufacturer + Family + ProductName + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release
{dc5af3fe-c2de-539d-aafd-5061a1634723}    <- Manufacturer + ProductName + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release
{d78b474d-dee0-5412-bc9d-e9f7d7783df2}    <- Manufacturer + Family + ProductName + SKUNumber
{7ccbb6f1-9641-5f84-b00d-51ff218a4066}    <- Manufacturer + Family + ProductName
{5a127cba-be28-5d3b-84f0-0e450d266d97}    <- Manufacturer + SKUNumber
{6525c6e5-28e9-5f9c-abe4-20fd82504002}    <- Manufacturer + ProductName
{6525c6e5-28e9-5f9c-abe4-20fd82504002}    <- Manufacturer + Family
{482f3f58-6045-593a-9be4-611717ce4770}    <- Manufacturer + Enclosure Type
{11b4a036-3b64-5421-a372-22c07df10a4d}    <- Manufacturer

I would like to develop a generic use function that should mimic the functionality of that Microsoft tool, returning exactly the same HardwareIds (exactly the same).

I've found info on MSDN, all the output seems very documented and it contains info about the values that returns this tool, but it does not specify exactly what properties of the WMI classes are, it just says "Bios" and "System":

· ComputerHardwareIds Overview

· Specifying Hardware IDs for a Computer

I'm very lost, I can't find any values such as "Family", "BIOS Vendor", "Bios Major Release", "Bios Minor Release" and I'm not sure where the "SKU Number" refers to.

I Think these are the WMI Classes where the tool obtains part of all the data to make the guid:

· Win32_BIOS class

· Win32_BaseBoard class

· Win32_ComputerSystem class

· Win32_ComputerSystemProduct class

Note that the documentation also says this:

Each string is then converted into a GUID by using the SHA-1 hashing algorithm.


This is what I've tried to do, but I'm not sure whether i'm wrong with some concept or some values, it's incomplete and I also have problems with the Guis (explained with commentlines):

Private Function GetHardwareId() As Guid

    Dim HardwareId As String = String.Empty

    Dim BIOSVersion, BIOSVendor, BIOSMajorRelease, BIOSMinorRelease,
        SystemManufacturer, SystemFamily, SystemProductName, SKUNumber As String

    ' Get System Info.
    Using wmi As New Management.ManagementObjectSearcher("select * from Win32_ComputerSystem")

        Using SystemInfo As Management.ManagementObject = wmi.Get(0)

            SystemManufacturer = Convert.ToString(SystemInfo.Properties("Manufacturer").Value)
            SystemProductName = Convert.ToString(SystemInfo.Properties("Model").Value)
            SystemFamily = I don't know how to get it.
            SKUNumber = I don't know how to get it.

        End Using

    End Using

    ' Get BIOS Info.
    Using wmi As New Management.ManagementObjectSearcher("select * from Win32_BIOS")

        Using BIOSInfo As Management.ManagementObject = wmi.Get(0)

            BIOSVersion = Convert.ToString(BIOSInfo.Properties("SMBIOSBIOSVersion").Value) 
            BIOSVendor = I don't know how to get it.
            BIOSMajorRelease = I don't know how to get it.
            BIOSMinorRelease = I don't know how to get it.

        End Using

    End Using ' wmi

    HardwareId = BIOSVersion & BIOSVendor & BIOSMajorRelease & BIOSMinorRelease &
                 SystemManufacturer & SystemFamily & SystemProductName & SKUNumber

    ' Here I call other method to encode the resulting string to SHA1 Hash
    HardwareId = ConvertToSHA1(HardwareId)
    ' and then continue below...

    ' But this will not work, 
    ' it throws an exception about missing "-" chars in the SHA1 string.
    ' So Microsoft formats "manualy" the SHA1 string to add some "-"?
    Return Guid.Parse(HardwareId)

End Function
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • I might have a better answer, but where the heck is this CLI tool? I cannot find it to test some algorithms. – Ňɏssa Pøngjǣrdenlarp Aug 08 '14 at 15:00
  • @Plutonix the tool is inside the Windows SDK, at least in the SDK for Win8/8.1: http://msdn.microsoft.com/en-us/windows/desktop/bg162891.aspx and here is an url that contains only this tool: http://www.mediafire.com/download/g1gd1sphdwlo9jb/computerhardwareids.rar I've put both x64/x86 executables of the SDK for Win 8.1, thanks for comment – ElektroStudios Aug 08 '14 at 15:11

3 Answers3

2

I dont think your problem can be solved the way you want. But there is also no reason to, as yet.

MS is creating deterministic GUIDs from a SHA hash of the various data provided. If created according to the rfc422 standards which includes one of the 4 defined GUID namespaces, we should be able to recreate the GUID from the same data using one of those 4 namespaces.

However, a) I can't and b) MSDN's 'Specifying Hardware IDs for a Computer' states: the hardware ID for the computer must be produced by the ComputerHardwareIds tool (ComputerHardwareIDs.exe).... This leads me to believe they use a proprietary method (Salt, private Key etc) or defined their own namespace to generate these.

Some answers to your secondary/sub questions:

  • BIOS Version/Release - According to the DTMF.org spec, Table 5 "Release" are at offset &H14 and &H15 and are different than "Version". However, they may also be embedded in the Name, Caption, Description and SoftwareElementID properties of Win32_BIOS (see tool below). It also seems to be tucked away in Win32_BIOS.BiosVersion(1) but it is the same as Name/Caption etc.

I find it a bit curious that our systems are several years apart but have the same Release values, it may refer to the SMBios release/spec.

  • SKU: According to MSDN, this is AKA IdentificationCode

  • Family: Apparently part of the BIOS encoding, but WMI does not expose or return it (yet?).

  • The same is true for Product Name, the Model you fetch from elsewhere may have the same value only by coincidence.

So, it does not appear the values used in the hash are all exposed. On my older system, Family and SKU are null. As a result, it seems that the first and second ID should be the same, but they are not.

I am not sure what these are for or how they are of much value to the average app if the GUID/ID is only available from that tool. You might poke around other parts of the SDK to see if there are assemblies or such to provide the info at run time.

If you simply want to recognize a system or device the next time you see it, you could simply write your own method based on rfc422 to assure the very same 'very high probability' of a unique value within a namespace you define. The only reason to do it just like MS is if you were going to see that value coming from elsewhere, which is not the case.

Finally, I did not bother posting the GUID maker since it won't do what you want anyway.


WMI helpers to fetch property values:

Public Sub GetWMIInfo(wmiclass As String)

    Using searcher As New Management.ManagementObjectSearcher("select * from " & wmiclass)

        For Each item As System.Management.ManagementObject In searcher.Get
            DebugProperties(item)
        Next

    End Using
End Sub

' this sub is copied from the watcher answer I gave:
Private Sub DebugProperties(mo As Management.ManagementObject)

    For Each pd As PropertyData In mo.Properties
        If pd.Value IsNot Nothing Then
            ' some props are string arrays, so you can iterate them if you want

            Console.WriteLine("{0} {1}", pd.Name,
                              If(pd.Value IsNot Nothing,
                                 pd.Value.ToString,
                                 "Nothing"))
        End If

    Next
End Sub

Output is like this:

Caption BIOS Date: XXXXXXXXXXXX Ver: 04.06.04
Description BIOS Date: ##/##/## 11:18:49 Ver: 04.06.04
Manufacturer Dell Inc.
Name BIOS Date: ##/##/## 11:18:49 Ver: 04.06.04
PrimaryBIOS True
ReleaseDate ########000000.000000+000
SerialNumber ######
SMBIOSBIOSVersion A##
SMBIOSMajorVersion #
SMBIOSMinorVersion #
Community
  • 1
  • 1
Ňɏssa Pøngjǣrdenlarp
  • 38,411
  • 12
  • 59
  • 178
  • Doues not solve the problem, any of those values are the proper values, note the Microsoft Tool retrieves the "BIOS Major/Minor Release" not "Bios Major/Minor Version", I don't know the difference but I've tried the same of your code before and the 'SMBIOSMajorVersion' returns a different value than the Microsoft Tool. also still unknown how to locate the system Family and the other values that I explained, and how to convert the SHA1 string to GUID. thanks for your answer – ElektroStudios Aug 07 '14 at 20:14
1
public ArrayList<String> getWinVendor()
        throws SecurityException, IOException,
        NullPointerException, IndexOutOfBoundsException,
        UnsupportedEncodingException {
    try {
        Process processProduct = Runtime.getRuntime().exec(new String[]{"wmic", "csproduct", "get", "vendor"});
        processProduct.getOutputStream().close();
        BufferedReader output = getOutput(processProduct);
        BufferedReader error = getError(processProduct);
        StringProductList = new ArrayList<String>();
        String line = "", result = "";
        while ((line = output.readLine()) != null) {
            if (!line.toLowerCase().startsWith("vendor") && line.length() > 0) {
                result = getSubStringSubstractEmptyAndTabSpace(line);
                if (result.length() > 0) {
                    StringProductList.add(result);
                } else {
                    StringProductList.add(UNKNOWN);
                }
            }
        }
        if (!StringProductList.isEmpty()) {
            return StringProductList;
        }
    } catch (Exception e) {
        if (e instanceof SecurityException
                || e instanceof IOException
                || e instanceof NullPointerException
                || e instanceof IndexOutOfBoundsException
                || e instanceof UnsupportedEncodingException) {
            e.printStackTrace();
        }
    }
    return null;
}

public BufferedReader getError(Process process) throws SecurityException, IOException,
        NullPointerException, IndexOutOfBoundsException, UnsupportedEncodingException {
    try {
        if (getCmdEncoding() != null) {
            return new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
        }
    } catch (Exception e) {
        if (e instanceof SecurityException
                || e instanceof IOException
                || e instanceof NullPointerException
                || e instanceof IndexOutOfBoundsException) {
            e.printStackTrace();
        }
    }
    return new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
}

public BufferedReader getOutput(Process process) throws SecurityException, IOException,
        NullPointerException, IndexOutOfBoundsException, UnsupportedEncodingException {
    try {
        if (getCmdEncoding() != null) {
            return new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
        }
    } catch (Exception e) {
        if (e instanceof SecurityException
                || e instanceof IOException
                || e instanceof NullPointerException
                || e instanceof IndexOutOfBoundsException) {
            e.printStackTrace();
        }
    }
    return new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
}

public static String getSubStringSubstractEmptyAndTabSpace(String word)
        throws NullPointerException, IndexOutOfBoundsException {
    if (word.length() > 0) {
        try {
            int length = word.length();
            int start = 0, end = length;
            for (int stringCharacter = 0; stringCharacter < length; stringCharacter++) {
                char c = word.charAt(stringCharacter);
                if (c == ' ' || c == '\t') {
                    start++;
                } else {
                    stringCharacter = length;
                }
            }
            for (int stringCharacter = length - 1; stringCharacter >= 0; stringCharacter--) {
                char c = word.charAt(stringCharacter);
                if (c == ' ' || c == '\t') {
                    end--;
                } else {
                    stringCharacter = -1;
                }
            }
            if (start == length) {
                return "";
            }
            if (end == 0) {
                return "";
            }
            if (start <= length - 1 && end >= 0) {
                return word.substring(start, end);
            }
        } catch (Exception e) {
            if (e instanceof NullPointerException
                    || e instanceof IndexOutOfBoundsException) {
                e.printStackTrace();
            }
        }
    }
    return word;
}
1

To generate the same GUIDs you need to get the values from SMBIOS (typically using GetSystemFirmwareTable), and then join them using the '&' char. Make sure the strings are encoded with UTF-16. You then need to use a type 5 (SHA-1) UUID generation scheme with 70ffd812-4c7f-4c7d-0000-000000000000 as the namespace.

  • 2
    A link to a solution is welcome, but please ensure your answer is useful without it: [add context around the link](//meta.stackexchange.com/a/8259) so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. [Answers that are little more than a link may be deleted.](//stackoverflow.com/help/deleted-answers) – Baum mit Augen Apr 25 '17 at 22:02
  • Thankyou so much for the answer. I read the article and it is an excellent reverse engineering work, but without a demostrative example code (written in whatever language) I still very lost, because doesn't seem to exist examples of the GetSystemFirmwareTable api usage neither I don't know what you are doing to retrieve the Computer Information components like in the output of your article (you just mention the function names required and technicist reversed values, but not how to use those functions). Anyways thanks for solving this mistery!. – ElektroStudios Jun 01 '17 at 01:23
  • @Richard Hughes Hi, I hope you can read this. I need to ask you: how are treated and joined the numeric values in the string to hash it?. It also uses the "&" char to append numeric values?. I ask this because I finally managed to replicate all hardware id types of this program except few which gets uint8 values from Win32_BaseBoard WMI class (CLI program hardware id types: 0, 1 and 2) those are the bios major and minor release version, so I suspect the numeric values are modified in some way when appending it to the string... – ElektroStudios Mar 17 '22 at 10:26
  • I think the 3 vfuncs here will help you a lot: https://github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-hwids.c#L325 – Richard Hughes Mar 18 '22 at 11:02