VB.NET 取唯一识别码机器码

VB.NET · 6 天前 · 31 人浏览

创建一个模块,优点在于,如果插入移动硬盘什么的。也不会导致机器码的变化。

Imports System.Management
Imports System.Security.Cryptography
Imports System.Text

Module 机器码
    Public 全局变量_机器码 As String = GetUniqueId16()
    Private Function GetCpuId() As String
        Return GetWMIProperty("SELECT ProcessorId FROM Win32_Processor", "ProcessorId")
    End Function

    ' 获取 BIOS 序列号
    Private Function GetBiosId() As String
        Return GetWMIProperty("SELECT SerialNumber FROM Win32_BIOS", "SerialNumber")
    End Function
    ' 获取主板序列号
    Private Function GetBoardId() As String
        Return GetWMIProperty("SELECT SerialNumber FROM Win32_BaseBoard", "SerialNumber")
    End Function

    ' 获取物理硬盘序列号
    Private Function GetDiskSerial() As String
        Return GetSystemDiskSerial()
    End Function
    ' 通用函数:安全获取 WMI 属性
    Private Function GetWMIProperty(query As String, prop As String) As String
        Try
            Dim searcher As New ManagementObjectSearcher(query)
            For Each obj As ManagementObject In searcher.Get()
                Dim val = obj(prop)
                If val IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(val.ToString()) Then
                    Dim s = val.ToString().Trim()
                    If Not s.ToLower().Contains("default") AndAlso
                       Not s.ToLower().Contains("to be filled") Then
                        Return s
                    End If
                End If
            Next
        Catch
        End Try
        Return ""
    End Function
    Public Function GetUniqueId16() As String
        Dim cpu = GetCpuId()
        Dim bios = GetBiosId()
        Dim board = GetBoardId()
        Dim disk = GetSystemDiskSerial()   ' ← 用系统盘对应物理盘序列号

        Dim raw = cpu & "-" & bios & "-" & board & "-" & disk
        Dim md5 = System.Security.Cryptography.MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(raw))
        Dim full = BitConverter.ToString(md5).Replace("-", "").ToLower()
        Return full.Substring(8, 16)   '取中间16位
    End Function
    ' 取系统盘盘符(通常是 C:),来自 Win32_OperatingSystem.SystemDrive
    Private Function GetSystemDriveLetter() As String
        Try
            Using searcher As New ManagementObjectSearcher("SELECT SystemDrive FROM Win32_OperatingSystem")
                For Each os As ManagementObject In searcher.Get()
                    Dim sd = TryCast(os("SystemDrive"), String)
                    If Not String.IsNullOrWhiteSpace(sd) Then
                        Return sd.Trim()
                    End If
                Next
            End Using
        Catch
        End Try
        Return "C:" ' 兜底
    End Function

    ' 从字符串中过滤掉无效/占位的序列号
    Private Function CleanSerial(s As String) As String
        If String.IsNullOrWhiteSpace(s) Then Return ""
        Dim t = s.Trim()
        Dim tl = t.ToLowerInvariant()
        If tl.Contains("default") OrElse tl.Contains("to be filled") OrElse tl = "none" Then Return ""
        Return t
    End Function

    ' 通过“系统逻辑盘 -> 分区 -> 物理磁盘”的关联,精确锁定系统盘所在物理磁盘并取序列号
    Private Function GetSystemDiskSerial() As String
        Dim sysDrive As String = GetSystemDriveLetter() ' 例如 "C:"
        Try
            ' 1) 逻辑盘 (C:)
            Dim q1 As String = $"ASSOCIATORS OF {{Win32_LogicalDisk.DeviceID='{sysDrive}'}} WHERE AssocClass = Win32_LogicalDiskToPartition"
            Using partSearcher As New ManagementObjectSearcher(q1)
                For Each part As ManagementObject In partSearcher.Get()
                    ' 2) 分区 -> 物理磁盘
                    Dim partId As String = TryCast(part("DeviceID"), String) ' 形如 "Disk #0, Partition #1"
                    If String.IsNullOrWhiteSpace(partId) Then Continue For
                    Dim q2 As String = $"ASSOCIATORS OF {{Win32_DiskPartition.DeviceID='{partId}'}} WHERE AssocClass = Win32_DiskDriveToDiskPartition"
                    Using diskSearcher As New ManagementObjectSearcher(q2)
                        For Each disk As ManagementObject In diskSearcher.Get()
                            ' 物理磁盘,如 \\.\PHYSICALDRIVE0
                            Dim iface = TryCast(disk("InterfaceType"), String) ' SATA/NVMe/USB...
                            ' 系统盘通常不会是 USB,但为了稳妥,若是 USB 则跳过
                            If Not String.IsNullOrEmpty(iface) AndAlso iface.Trim().ToUpperInvariant() = "USB" Then Continue For

                            ' 优先取 Win32_DiskDrive.SerialNumber
                            Dim serial As String = CleanSerial(TryCast(disk("SerialNumber"), String))
                            If serial <> "" Then Return serial

                            ' 取不到再从 Win32_PhysicalMedia 按 Tag 映射(Tag 通常等于 \\.\PHYSICALDRIVE#)
                            Dim devId As String = TryCast(disk("DeviceID"), String)
                            If Not String.IsNullOrWhiteSpace(devId) Then
                                Try
                                    Dim q3 As String = $"SELECT SerialNumber, Tag FROM Win32_PhysicalMedia WHERE Tag='{devId.Replace("\", "\\")}'"
                                    Using pmSearcher As New ManagementObjectSearcher(q3)
                                        For Each pm As ManagementObject In pmSearcher.Get()
                                            Dim s2 As String = CleanSerial(TryCast(pm("SerialNumber"), String))
                                            If s2 <> "" Then Return s2
                                        Next
                                    End Using
                                Catch
                                End Try
                            End If
                        Next
                    End Using
                Next
            End Using
        Catch
        End Try

        ' 如果上面都失败,最后兜底:取系统卷的 VolumeSerialNumber(会随格式化变)
        Try
            Dim qVol As String = $"SELECT VolumeSerialNumber FROM Win32_LogicalDisk WHERE DeviceID='{sysDrive}'"
            Using volSearcher As New ManagementObjectSearcher(qVol)
                For Each d As ManagementObject In volSearcher.Get()
                    Dim volSn As String = CleanSerial(TryCast(d("VolumeSerialNumber"), String))
                    If volSn <> "" Then Return volSn
                Next
            End Using
        Catch
        End Try

        Return ""
    End Function
    Function MD5Hash(input As String) As String
        Using md5 As MD5 = MD5.Create()
            Dim inputBytes As Byte() = Encoding.UTF8.GetBytes(input)
            Dim hashBytes As Byte() = md5.ComputeHash(inputBytes)
            Dim sb As New StringBuilder()
            For Each b As Byte In hashBytes
                sb.Append(b.ToString("x2"))
            Next
            Return sb.ToString()
        End Using
    End Function
    Function UnixTimeStampToDateTime(unixTimeStamp As Long) As DateTime
        Dim dateTime As DateTime = New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
        Return dateTime.AddSeconds(unixTimeStamp).ToLocalTime()
    End Function
End Module