Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WMI HDD Serial Number Transposed

I have some code to fetch a hard drive serial number from the WMI.

SelectQuery selectQuery = new SelectQuery("Win32_PhysicalMedia");
ManagementObjectSearcher searcher =
             new ManagementObjectSearcher(selectQuery);
foreach (ManagementObject wmi_PM in searcher.Get())
{
      string str = wmi_PM["SerialNumber"];
}

At first I thought it was working and retrieved the correct serial number. After trying to use it with a comparison though, I found out the number the WMI reports is not exactly correct. The WMI serial number is padded with a bunch of spaces as well as the characters are transposed.

The actual drive serial number printed on the sticker and returned by some tools (probably using DeviceIoControl) is "3RH8B1BG", WMI however returns "                R38H1BGB".

Real Serial#: 3RH8B1BG
WMI Serial#: R38H1BGB

Some tools like SiSoftware Sandra, return this padded and transposed number, its not actual serial number though. The WMI value is the serial number if you transpose every other position. Is this normal? should I just code to transpose it to the correct value?

I try to avoid using the WMI but it seems any search for how to do something on the net now bring back WMI examples.

The WMI value serial number for 2 different hard drive of different manufactures are both transposed so its not a single disk.



Update: found some code using DeviceIoControl

     http://addressof.com/blog/archive/2004/02/14/392.aspx

Surprisingly, DeviceIoControl returns a transposed serial number as well. In the code by CorySmith above it has a SwapChars function

Private Shared Function SwapChars(ByVal chars() As Char) As String
  For i As Integer = 0 To chars.Length - 2 Step 2
    chars.Reverse(chars, i, 2)
  Next
  Return New String(chars).Trim
End Function

The c++ code he mentions has the flip to:

    //  function to decode the serial numbers of IDE hard drives
    //  using the IOCTL_STORAGE_QUERY_PROPERTY command 
char * flipAndCodeBytes (const char * str,
             int pos,
             int flip,
             char * buf)
{
    ...
}

guess that's standard for DeviceIoControl and WMI, cant believe any of the other solutions or examples i ran across did not have this.

like image 904
Jim Avatar asked Jun 12 '12 00:06

Jim


1 Answers

Found a working Solution to decode the real HD-Serials. The following Link contains the code to decode even without admin-rights: Decoding Source

But if you get the Serials from the Win32_PhysicalMedia WMI class above Vista it may not work in all cases. Then you have to use the Win32_DiskDrive class (according to this Link: Jiliang Ge's Answer from Tuesday, October 27, 2009 3:12 AM

I added the code (in VB, since I usually code in VB.NET). I didn't want to steal someone else's Code. I included as much Infos and still some links to the Original Coder within the code. It now also includes decoding Serialnumbers from Removable Drives (in the same routine).

Hope it helps.

   ''' <summary>
''' Decode Manufacuter Disk Serialnumbers (also for PNP USB-Drives)
''' </summary>
''' <param name="InterfaceType">InterfaceType from Win32_DiskDrive WMI-Class</param>
''' <param name="PNPDeviceID">PNPDeviceID from Win32_DiskDrive WMI-Class</param>
''' <param name="strVolumeSerial">Raw Serialnumber to be decoded</param>
''' <returns>Decoded Serialnumber</returns>
''' <remarks></remarks>
Public Shared Function Decode_HD_Serial(ByVal InterfaceType As String,
                          ByVal PNPDeviceID As String,
                          ByVal strVolumeSerial As String) As String

    'HANDLE USB PNP Devices differently (Removable USB-Sticks)
    'see: http://www.experts-exchange.com/Programming/Languages/.NET/Q_24574066.html

    If InterfaceType = "USB" Then
        Dim splitDeviceId As String() = PNPDeviceID.Split("\"c)
        Dim arrayLen As Integer = splitDeviceId.Length - 1
        Dim serialArray As String() = splitDeviceId(arrayLen).Split("&"c)
        Return serialArray(0)
    Else
        'Link:https://social.msdn.microsoft.com/Forums/vstudio/en-US/8523d7b9-0dc8-4d87-be69-a482aec9ee5e/wmi-win32physicalmedia-smart-id-in-vista-and-7-permissions?forum=netfxbcl
        'After digging into the [Win32_PhysicalMedia] WMI class, I find that from Vista/Longhorn the 
        'class has been taken over by another class called [Win32_DiskDrive]. Thus, if all machines 
        'in your environment are Vista and above use the second class otherwise use the first one. 
        'Based on my tests, the class gives the unique form of serial number when you run the 
        'app as an admin or as a non-admin. 
        ' ---> IF System.Environment.OSVersion.Version.Major > 5 then its Vista or higher. USE WIN32_DiskDrive

        Dim strVolumeSerialDecoded As String = String.Empty
        'Remove all space characters ("20").
        'Example : 20202020205635424544434553 will be 5635424544434553.
        strVolumeSerial.Trim.Replace("20", "")
        'IF THE USER IS ADMINISTRATOR, THE strVolumeSerial STRING WILL ALREADY CONTAIN THE SERIAL NUMBER IN ASCII, AND NO CONVERSION IS REQUIRED (Microsoft bug ?),
        'BUT IF THE strVolumeSerial STRING IS A HEX STRING, CONVERT IT TO ASCII :
        If System.Text.RegularExpressions.Regex.IsMatch(strVolumeSerial, "^[a-fA-F0-9]+$") Then
            'Convert to ASCII. Example : 5635424544434553 will be converted to V5BEDCES.
            strVolumeSerial = HexDecode(strVolumeSerial)
            'Swap pairs of characters.
            'Example : V5BEDCES will be converted to 5VEBCDSE.
            Dim serialNumber2 As String = ""
            For i As Integer = 0 To strVolumeSerial.Length - 1 Step 2
                strVolumeSerialDecoded &= strVolumeSerial(i + 1)
                strVolumeSerialDecoded &= strVolumeSerial(i)
            Next
            'Return the serialnumber as ASCII string.
            Return strVolumeSerialDecoded.Trim
        Else 'If strVolumeSerial is ASCII, remove spaces and return the serialnumber string.
            Return strVolumeSerial.Trim
        End If
    End If
End Function

''' <summary>Decodes a HEX-string to an ASCII string.</summary>
''' <param name="strHEX">The HEX-string to decode.</param>
''' <returns>If succeeded, the decoded String, an empty String if failed.</returns>
Private Shared Function HexDecode(ByVal strHEX As String) As String
    Try
        Dim sb As StringBuilder = New StringBuilder
        For i As Integer = 0 To strHEX.Length - 1 Step 2
            sb.Append(Convert.ToChar(Convert.ToUInt32(strHEX.Substring(i, 2), 16)).ToString)
        Next
        Return sb.ToString
    Catch ex As Exception
        Return ""
    End Try
End Function
like image 70
dragonfly Avatar answered Oct 02 '22 09:10

dragonfly