Get Hardware IDs like Microsoft does
Asked Answered
N

4

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
Nightstick answered 7/8, 2014 at 19:44 Comment(2)
I might have a better answer, but where the heck is this CLI tool? I cannot find it to test some algorithms.Millenarian
@Plutonix the tool is inside the Windows SDK, at least in the SDK for Win8/8.1: msdn.microsoft.com/en-us/windows/desktop/bg162891.aspx and here is an url that contains only this tool: mediafire.com/download/g1gd1sphdwlo9jb/computerhardwareids.rar I've put both x64/x86 executables of the SDK for Win 8.1, thanks for commentNightstick
B
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.

Bannerman answered 25/4, 2017 at 19:58 Comment(4)
A link to a solution is welcome, but please ensure your answer is useful without it: add context around the link 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.Immaculate
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!.Nightstick
@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...Nightstick
I think the 3 vfuncs here will help you a lot: github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-hwids.c#L325Bannerman
S
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 #
Scherle answered 7/8, 2014 at 20:9 Comment(1)
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 answerNightstick
F
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;
}
Faustofaustus answered 12/11, 2014 at 16:44 Comment(1)
Code dumps without explanation are rarely useful. Please edit your question to add some context.Firework
B
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.

Bannerman answered 25/4, 2017 at 19:58 Comment(4)
A link to a solution is welcome, but please ensure your answer is useful without it: add context around the link 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.Immaculate
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!.Nightstick
@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...Nightstick
I think the 3 vfuncs here will help you a lot: github.com/fwupd/fwupd/blob/main/libfwupdplugin/fu-hwids.c#L325Bannerman
N
0

I just would like to share the full solution I did time ago, under VB.NET.

This code replicates all the exact same fourteen HWIDs as the computerhardwareids program. See the code example in the XML documentation block to get a identical output.

Two things to consider: I didn't tested this code outside of my computer, so I can't say if it will still generate the same HWIDs as the computerhardwareids program under unexpected circumstances.

And the part where I build the enclosureType string it is totally theoretical in the meaning that I'm not sure if the HardwareId-12 it will work as expected when the chassisTypes array has various values, since I didn't tested it outside of my computer as I said.

ComputerHardwareIdMicrosoftType.vb

''' <summary>
''' Specifies a Computer Hardware Id (CHID) for <see cref="UtilDevices.ComputerHardwareIdFromMicrosoftHwIdType"/> property.
''' </summary>
'''
''' <remarks>
''' <see href="https://docs.microsoft.com/en-us/windows-hardware/drivers/install/specifying-hardware-ids-for-a-computer"/>
''' <para></para>
''' Use <see cref="HardwareID_3"/> or <see cref="HardwareID_4"/> as the first choice 
''' if the software matches a computer that has a specific make, family, and model. 
''' This allows a software to match the specified computer, which provides the most precise metadata for the computer.
''' <para></para>
''' Use <see cref="HardwareID_5"/> as the second choice if the software covers the entire family of computers. 
''' In this case, the computer family is unique and is not branded with more than one product line.
''' <para></para>
''' Use <see cref="HardwareID_6"/> or <see cref="HardwareID_7"/> as the third choice 
''' if the software covers all of your computers or those computers with a specific enclosure type.
''' </remarks>
Public Enum ComputerHardwareIdMicrosoftType

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{035a20a6-fccf-5040-bc3e-b8b794c57f52} &lt;- Manufacturer</item>
    ''' </list>
    ''' </summary>
    HardwareID_14 = 14

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Baseboard Manufacturer</item>
    '''   <item>Baseboard Product</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{53b44fbf-2036-56ac-bc21-722e871e765a} &lt;- Manufacturer + Baseboard Manufacturer + Baseboard Product</item>
    ''' </list>
    ''' </summary>
    HardwareID_13 = 13

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Enclosure Type</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{a7aaa273-f280-513a-b907-919359a44175} &lt;- Manufacturer + Enclosure Type</item>
    ''' </list>
    ''' </summary>
    HardwareID_12 = 12

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Family</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{7315e85a-f6ec-5bf0-b11d-411fb886c323} &lt;- Manufacturer + Family</item>
    ''' </list>
    ''' </summary>
    HardwareID_11 = 11

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Family</item>
    '''   <item>Baseboard Manufacturer</item>
    '''   <item>Baseboard Product</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{a92d6619-1da8-577e-97af-b17ddbd71f75} &lt;- Manufacturer + Family + Baseboard Manufacturer + Baseboard Product</item>
    ''' </list>
    ''' </summary>
    HardwareID_10 = 10

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Product Name</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{0dcce594-e561-59aa-9e30-21077acb429c} &lt;- Manufacturer + ProductName</item>
    ''' </list>
    ''' </summary>
    HardwareID_9 = 9

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Product Name</item>
    '''   <item>Baseboard Manufacturer</item>
    '''   <item>Baseboard Product</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{8de62569-29ce-5767-84de-655c56d8ab67} &lt;- Manufacturer + ProductName + Baseboard Manufacturer + Baseboard Product</item>
    ''' </list>
    ''' </summary>
    HardwareID_8 = 8

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>SKU Number</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{d2957e9e-702e-591d-a829-36238d29d986} &lt;- Manufacturer + SKUNumber</item>
    ''' </list>
    ''' </summary>
    HardwareID_7 = 7

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>SKU Number</item>
    '''   <item>Baseboard Manufacturer</item>
    '''   <item>Baseboard Product</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{3b4375dd-3630-5839-9362-85b2bdb9defb} &lt;- Manufacturer + SKUNumber + Baseboard Manufacturer + Baseboard Product</item>
    ''' </list>
    ''' </summary>
    HardwareID_6 = 6

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Family</item>
    '''   <item>Product Name</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{ceb119e2-5411-5694-a8f1-57a5767fe85d} &lt;- Manufacturer + Family + ProductName</item>
    ''' </list>
    ''' </summary>
    HardwareID_5 = 5

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Family</item>
    '''   <item>Product Name</item>
    '''   <item>SKU Number</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{9b3edbb4-9a86-5379-ad5e-4c6df298d9f8} &lt;- Manufacturer + Family + ProductName + SKUNumber</item>
    ''' </list>
    ''' </summary>
    HardwareID_4 = 4

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Family</item>
    '''   <item>Product Name</item>
    '''   <item>SKU Number</item>
    '''   <item>Baseboard Manufacturer</item>
    '''   <item>Baseboard Product</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{618e64e8-ca25-5b1a-b1ee-b66a7385f1ed} &lt;- Manufacturer + Family + ProductName + SKUNumber + Baseboard Manufacturer + Baseboard Product</item>
    ''' </list>
    ''' </summary>
    HardwareID_3 = 3

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Product Name</item>
    '''   <item>BIOS Vendor</item>
    '''   <item>BIOS Version</item>
    '''   <item>BIOS Major Release</item>
    '''   <item>BIOS Minor Release</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{dfed620c-3091-5ce9-bf23-2d47ba155d65} &lt;- Manufacturer + ProductName + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release</item>
    ''' </list>
    ''' </summary>
    HardwareID_2 = 2

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Family</item>
    '''   <item>Product Name</item>
    '''   <item>BIOS Vendor</item>
    '''   <item>BIOS Version</item>
    '''   <item>BIOS Major Release</item>
    '''   <item>BIOS Minor Release</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{9b8fcb39-4611-5c0c-a947-4c269fb3b9e9} &lt;- Manufacturer + Family + ProductName + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release</item>
    ''' </list>
    ''' </summary>
    HardwareID_1 = 1

    ''' <summary>
    ''' A hardwae Id. based on the following components: 
    ''' <list type="bullet">
    '''   <item>Manufacturer</item>
    '''   <item>Family</item>
    '''   <item>Product Name</item>
    '''   <item>SKU Number</item>
    '''   <item>BIOS Vendor</item>
    '''   <item>BIOS Version</item>
    '''   <item>BIOS Major Release</item>
    '''   <item>BIOS Minor Release</item>
    ''' </list>
    ''' 
    ''' <para></para>
    ''' Example output from <c>computerhardwareids</c> program:
    ''' <list type="bullet">
    '''   <item>{d669d1f2-9fcc-580d-bee1-a7ec7078d827} &lt;- Manufacturer + Family + ProductName + SKUNumber + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release</item>
    ''' </list>
    ''' </summary>
    HardwareID_0 = 0

End Enum

GetComputerHardwareIdFromMicrosoftHwIdType method

''' <summary>
''' Gets a unique computer hardware identifier (CHID) that matches the current computer.
''' <para></para>
''' This identifier is generated following Microsoft's PC Device Metadata packages schemas:
''' <para></para>
''' https://docs.microsoft.com/en-us/windows-hardware/drivers/install/specifying-hardware-ids-for-a-computer
''' <para></para>
''' In fact, this function generates the exact same identifiers as the 
''' 'computerhardwareids.exe' program included in the Windows SDK:
''' https://docs.microsoft.com/es-es/windows-hardware/drivers/devtest/computerhardwareids
''' <para></para>
''' https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/
''' <para></para>
''' <para></para>
''' Use HardwareID-3 or HardwareID-4 as the first choice if the software matches a computer 
''' that has a specific make, family, and model. This allows a software to match the specified computer, 
''' which provides the most precise metadata for the computer.
''' <para></para>
''' Use HardwareID-5, as the second choice if the software covers the entire family of computers. 
''' In this case, the computer family is unique and is not branded with more than one product line.
''' <para></para>
''' Use HardwareID-6 or HardwareID-7 as the third choice if the software covers all of your computers 
''' or those computers with a specific enclosure type.
''' </summary>
'''
''' <example> This is a code example.
''' <code language="VB.NET">
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_0)}}}    &lt;- Manufacturer + Family + ProductName + SKUNumber + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_1)}}}    &lt;- Manufacturer + Family + ProductName + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_2)}}}    &lt;- Manufacturer + ProductName + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_3)}}}    &lt;- Manufacturer + Family + ProductName + SKUNumber + Baseboard Manufacturer + Baseboard Product")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_4)}}}    &lt;- Manufacturer + Family + ProductName + SKUNumber")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_5)}}}    &lt;- Manufacturer + Family + ProductName")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_6)}}}    &lt;- Manufacturer + SKUNumber + Baseboard Manufacturer + Baseboard Product")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_7)}}}    &lt;- Manufacturer + SKUNumber")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_8)}}}    &lt;- Manufacturer + ProductName + Baseboard Manufacturer + Baseboard Product")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_9)}}}    &lt;- Manufacturer + ProductName")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_10)}}}    &lt;- Manufacturer + Family + Baseboard Manufacturer + Baseboard Product")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_11)}}}    &lt;- Manufacturer + Family")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_12)}}}    &lt;- Manufacturer + Enclosure Type")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_13)}}}    &lt;- Manufacturer + Baseboard Manufacturer + Baseboard Product")
''' Console.WriteLine($"{{{GetComputerHardwareIdFromMicrosoftHwIdType(ComputerHardwareIdMicrosoftType.HardwareID_14)}}}    &lt;- Manufacturer")
''' </code>
''' </example>
'''
''' <returns>
''' The resulting unique computer hardware identifier (CHID) that matches the current computer.
''' </returns>
<DebuggerStepThrough>
Private Shared Function GetComputerHardwareIdFromMicrosoftHwIdType(hwidType As ComputerHardwareIdMicrosoftType) As Guid

    Select Case CInt(hwidType)
        Case Is < ComputerHardwareIdMicrosoftType.HardwareID_0, Is > ComputerHardwareIdMicrosoftType.HardwareID_14
            Throw New InvalidEnumArgumentException(argumentName:=NameOf(hwidType), invalidValue:=hwidType, enumClass:=GetType(ComputerHardwareIdMicrosoftType))
    End Select

    ' Namespace used by 'computerhardwareids.exe' program included in the Windows SDK.
    ' https://mcmap.net/q/1919229/-get-hardware-ids-like-microsoft-does/43619888#43619888
    ' https://blogs.gnome.org/hughsie/2017/04/25/reverse-engineering-computerhardwareids-exe-with-winedbg/
    Dim namespaceUUID As Guid = Guid.Parse("70ffd812-4c7f-4c7d-0000-000000000000")

    ' Win32_ComputerSystem fields
    Dim manufacturer, family, productName, skuNumber As String

    ' Win32_BaseBoard fields
    Dim baseBoardManufacturer, baseBoardProduct As String

    ' Win32_BIOS fields
    Dim biosVendor, biosVersion, biosMajorRelease, biosMinorRelease As String

    ' Win32_SystemEnclosure fields
    Dim enclosureType As String = Nothing

    ' Retrieve computer info.
    Using mos As New ManagementObjectSearcher()

        ' Win32_ComputerSystem
        mos.Query.QueryString = "SELECT Manufacturer,Model,SystemFamily,SystemSKUNumber FROM Win32_ComputerSystem"
        Using systemInfo As ManagementObject = DirectCast(mos.Get(0), ManagementObject)
            manufacturer = CStr(systemInfo.Properties("Manufacturer")?.Value)?.Trim()
            productName = CStr(systemInfo.Properties("Model")?.Value)?.Trim()
            family = CStr(systemInfo.Properties("SystemFamily")?.Value)?.Trim()
            skuNumber = CStr(systemInfo.Properties("SystemSKUNumber")?.Value)?.Trim()
        End Using

        ' Win32_BaseBoard
        mos.Query.QueryString = "SELECT Manufacturer,Product FROM Win32_BaseBoard"
        Using baseBoardInfo As ManagementObject = DirectCast(mos.Get(0), ManagementObject)
            baseBoardManufacturer = CStr(baseBoardInfo.Properties("Manufacturer")?.Value)?.Trim()
            baseBoardProduct = CStr(baseBoardInfo.Properties("Product")?.Value)?.Trim()
        End Using

        ' Win32_BIOS
        mos.Query.QueryString = "SELECT Manufacturer,SMBIOSBIOSVersion,SystemBiosMajorVersion,SystemBiosMinorVersion FROM Win32_BIOS"
        Using biosInfo As ManagementObject = DirectCast(mos.Get(0), ManagementObject)
            biosVendor = CStr(biosInfo.Properties("Manufacturer")?.Value)?.Trim()
            biosVersion = CStr(biosInfo.Properties("SMBIOSBIOSVersion")?.Value)?.Trim()
            biosMajorRelease = CByte(biosInfo.Properties("SystemBiosMajorVersion")?.Value).ToString("X2").ToLower()
            biosMinorRelease = CByte(biosInfo.Properties("SystemBiosMinorVersion")?.Value).ToString("X2").ToLower()
        End Using

        ' Win32_SystemEnclosure
        mos.Query.QueryString = "SELECT ChassisTypes FROM Win32_SystemEnclosure"
        Using enclosureInfo As ManagementObject = DirectCast(mos.Get(0), ManagementObject)
            Dim chassisTypes As UShort() = DirectCast(enclosureInfo.Properties("ChassisTypes").Value, UShort())
            For Each chassisType As UShort In chassisTypes
                enclosureType &= CStr(chassisType)
            Next chassisType
            If String.IsNullOrEmpty(enclosureType) Then
                enclosureType = "2" ' Unknown (2)
            End If
        End Using

    End Using

#If DEBUG Then
    ' Console.WriteLine($"{NameOf(manufacturer)}          = {manufacturer}")
    ' Console.WriteLine($"{NameOf(enclosureType)}         = {enclosureType}")
    ' Console.WriteLine($"{NameOf(productName)}           = {productName}")
    ' Console.WriteLine($"{NameOf(family)}                = {family}")
    ' Console.WriteLine($"{NameOf(skuNumber)}             = {skuNumber}")
    ' Console.WriteLine($"{NameOf(baseBoardManufacturer)} = {baseBoardManufacturer}")
    ' Console.WriteLine($"{NameOf(baseBoardProduct)}      = {baseBoardProduct}")
    ' Console.WriteLine($"{NameOf(biosVendor)}            = {biosVendor}")
    ' Console.WriteLine($"{NameOf(biosVersion)}           = {biosVersion}")
    ' Console.WriteLine($"{NameOf(biosMajorRelease)}      = {biosMajorRelease}")
    ' Console.WriteLine($"{NameOf(biosMinorRelease)}      = {biosMinorRelease}")
#End If

    Dim stringToHash As String = String.Empty

    Select Case hwidType

        Case ComputerHardwareIdMicrosoftType.HardwareID_0
            stringToHash = $"{manufacturer}&{family}&{productName}&{skuNumber}&{biosVendor}&{biosVersion}&{biosMajorRelease}&{biosMinorRelease}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_1
            stringToHash = $"{manufacturer}&{family}&{productName}&{biosVendor}&{biosVersion}&{biosMajorRelease}&{biosMinorRelease}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_2
            stringToHash = $"{manufacturer}&{productName}&{biosVendor}&{biosVersion}&{biosMajorRelease}&{biosMinorRelease}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_3
            stringToHash = $"{manufacturer}&{family}&{productName}&{skuNumber}&{baseBoardManufacturer}&{baseBoardProduct}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_4
            stringToHash = $"{manufacturer}&{family}&{productName}&{skuNumber}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_5
            stringToHash = $"{manufacturer}&{family}&{productName}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_6
            stringToHash = $"{manufacturer}&{skuNumber}&{baseBoardManufacturer}&{baseBoardProduct}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_7
            stringToHash = $"{manufacturer}&{skuNumber}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_8
            stringToHash = $"{manufacturer}&{productName}&{baseBoardManufacturer}&{baseBoardProduct}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_9
            stringToHash = $"{manufacturer}&{productName}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_10
            stringToHash = $"{manufacturer}&{family}&{baseBoardManufacturer}&{baseBoardProduct}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_11
            stringToHash = $"{manufacturer}&{family}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_12
            stringToHash = $"{manufacturer}&{enclosureType}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_13
            stringToHash = $"{manufacturer}&{baseBoardManufacturer}&{baseBoardProduct}"

        Case ComputerHardwareIdMicrosoftType.HardwareID_14
            stringToHash = manufacturer

    End Select

    ' Creates a name-based UUID using the algorithm from RFC 4122 §4.3.
    ' https://mcmap.net/q/1839771/-generating-uuid-based-on-strings
    Dim generateGuidDelegate As Func(Of Guid, String, Guid) =
        Function(namespaceId As Guid, name As String) As Guid

            ' Converts a GUID (expressed as a byte array) to/from network order (MSB-first).
            Dim swapByteOrderDelegate As New Action(Of Byte())(
                Sub(guid() As Byte)
                    Dim temp As Byte = guid(0)
                    guid(0) = guid(3)
                    guid(3) = temp

                    temp = guid(1)
                    guid(1) = guid(2)
                    guid(2) = temp

                    temp = guid(4)
                    guid(4) = guid(5)
                    guid(5) = temp

                    temp = guid(6)
                    guid(6) = guid(7)
                    guid(7) = temp
                End Sub)

            ' Convert the name to a sequence of octets (as defined by the standard or conventions of its namespace) (step 3).
            Dim nameBytes() As Byte = Encoding.Unicode.GetBytes(name)

            ' Convert the namespace UUID to network order (step 3).
            Dim namespaceBytes() As Byte = namespaceId.ToByteArray()
            swapByteOrderDelegate.Invoke(namespaceBytes)

            ' Compute the hash of the name space ID concatenated with the name (step 4).
            Dim hash As Byte()
            Using algorithm As HashAlgorithm = SHA1.Create()
                algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, Nothing, 0)
                algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length)
                hash = algorithm.Hash
            End Using

            ' Most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12).
            Dim newGuid(15) As Byte
            Array.Copy(hash, 0, newGuid, 0, 16)

            ' Set the four most significant bits (bits 12 through 15) of the time_hi_and_version field
            ' to the appropriate 4-bit version number from Section 4.1.3 (step 8).
            newGuid(6) = CByte((newGuid(6) And &HF) Or (5 << 4))

            ' Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved
            ' to zero and one, respectively (step 10).
            newGuid(8) = CByte((newGuid(8) And &H3F) Or &H80)

            ' Convert the resulting UUID to local byte order (step 13).
            swapByteOrderDelegate.Invoke(newGuid)
            Return New Guid(newGuid)
        End Function

    Return generateGuidDelegate.Invoke(namespaceUUID, stringToHash)

End Function
Nightstick answered 27/5 at 0:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.