获取唯一的机器 ID

Posted

技术标签:

【中文标题】获取唯一的机器 ID【英文标题】:get unique machine id 【发布时间】:2011-01-01 13:23:24 【问题描述】:

我想获得唯一不可更改的机器 ID,例如计算机的处理器序列号,以便分发软件而无需复制。

我尝试了处理器序列号和硬盘序列号,它们在格式化和重新安装 Windows 后都会发生变化。

知道如何获得不可更改的计算机序列号吗?

【问题讨论】:

解决方案是什么?唯一的机器 ID?处理器序列号?一个不可更改的序列号? “不复制”分发软件?每个问题的答案都不一样,你想要哪个? 为什么这么害怕获取机器 ID?每个人都害怕回答这个问题......显然,这样做的目的是为了保护版权...... 【参考方案1】:

编辑:我刚刚看到你在 c# 中的意思。这是使用非托管代码的更好方法:

ManagementClass oMClass = new ManagementClass ("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection colMObj = oMCLass.GetInstances();
foreach(ManagementObject objMO in colMObj)
    Console.WriteLine(objMO["MacAddress"].ToString());

【讨论】:

你知道MAC地址也是“可欺骗”的吗?此外,您的代码看起来像 C++,而不像 C#? 有什么解决办法请给我建议 好吧,如果处理器/硬盘序列号不够用,那么您只剩下这些了。如果他描述的是他想做什么而不是他想怎么做,我可能会有更好的答复。【参考方案2】:

查看this article。它非常详尽,您将了解如何提取各种硬件信息。

引用自article:

要获取硬件信息,需要创建一个ManagementObjectSearcher类的对象。

using System.Management;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + Key);
foreach (ManagementObject share in searcher.Get()) 
    // Some Codes ...

上面代码中的 Key 是一个被适当数据替换的变量。比如要获取CPU的信息,就得把Key换成Win32_Processor。

【讨论】:

我已经添加了文章的引用。【参考方案3】:

我同意 Blindy 的建议,即使用(第一个?)网络适配器的 MAC 地址。是的,MAC 地址可以被欺骗,但这有副作用(您不希望两台具有相同 MAC 地址的 PC 在同一个网络中),而且这是“普通盗版者”不会做的事情使用您的软件。考虑到没有 100% 的软件盗版解决方案,MAC 地址是一个很好的折衷方案,IMO。

但是请注意,当用户添加、更换或移除网卡(或完全更换他的旧 PC)时,地址会发生变化,因此请准备好帮助您的客户并在他们更换硬件时为他们提供新密钥配置。

【讨论】:

我一直在使用 MAC 地址作为我正在处理的项目中的唯一设备标识符,但在使用第一个时遇到了问题,因为它是 MS 环回适配器,而这个始终具有相同的 MAC 地址!以为我会分享它并避免其他人的困惑! isLoopback() 为该接口返回true,无论如何,这里很容易跳过。正如其他地方所指出的,isVirtual() 是不可靠的,对于 VirtualBox 来说是假的,到目前为止。【参考方案4】:

如果您需要一个唯一ID,您必须首先确定唯一的定义。如果您想/打算将其用于复制保护机制,请使用简单的东西。这是因为如果有人真的想使用您的软件,只要有足够的时间和技能,他就会找到一种方法来破坏您的保护。在唯一硬件 ID 的情况下,只要想想虚拟机,您就会发现有可能欺骗任何东西,从而有人可以篡改您的软件。

您可以从 PC 中获取的信息不多,并认为它在其整个生命周期内都是独一无二的。 (硬件更改很可能需要在某些时候重新生成 ID。)如果您需要类似的东西,您应该使用可以发送给客户的身份验证 USB 加密狗 进行调查。

如果您只需要一些不那么难以获得的唯一标识符,您可以使用 MAC 地址(不可靠)、操作系统序列号或域和用户名,但所有这些都容易被伪造。但是,如果您的主要目标是阻止未经授权的人员,您将不会出售任何东西,因为如果您的软件难以安装、注册或从一台 PC 转移到另一台 PC,则没有人愿意使用您的软件,尽管最后的考虑是每台机器许可的一部分。 (这可能会经常发生。)

第一步,让事情变得简单:在目标群体中使用不易欺骗的简单事物。 (例如,域名和用户名不能轻易被企业客户欺骗,因为他们的 PC 运行在更大的环境中执行策略等。)在你拥有之前忘记其他人。

也许您可以将他们拒之门外,但这并不意味着他们会购买您的软件;他们只是不再使用它了。您必须考虑的是,有多少潜在客户不会或不愿意支付,因为您让程序的使用变得如此复杂。

【讨论】:

这个问题比单纯的防拷贝软件更有用。所以你不应该只问这个人。我在尝试为每台机器获取一个唯一 ID 时遇到了同样的问题,而且我什至没有远程假装出售我的软件。可以在世界任何地方分发的软件的 USB 加密狗?继续做梦。 另外,复制校对企业级服务器端安装。这些类型的安装根本不会经常更改,因此复杂的激活不一定是一件坏事,因为它只完成一次,通常由供应商的现场安装团队完成。 我来到这里是出于安全原因,而不是许可 - 我需要使用一些特定于机器的密钥加密用户的数据,以便在被盗时无法检索(例如通过网络钓鱼)。 @TomášZato:在这种情况下,根据您的具体需求提出一个新问题。但是为了快速拍摄,你应该看看加盐或异步加密。 根据经验,我很清楚我的问题将作为这个问题的副本而被关闭。问题并没有那么不同。【参考方案5】:

我会远离使用 MAC 地址。在某些硬件上,MAC 地址会在您重新启动时更改。我们在研究过程中很早就学会了不要依赖它。

查看文章Developing for Software Protection and Licensing,其中提供了一些关于如何设计和实施应用以减少盗版的建议。

强制性免责声明和插件:我共同创立的公司生产OffByZero Cobalt licensing solution。因此,听到我建议外包您的许可并专注于您的核心竞争力,您可能不会感到惊讶。

【讨论】:

我同意这一点。 MAC地址非常不可靠。 mac 地址列表可能会根据机器连接到 Internet 的方式而改变。此外,每次机器启动时,主适配器可能会发生变化。 CISCO *** 等服务使问题进一步复杂化。【参考方案6】:

您可以使用WMI Code creator。我猜你可以组合使用“密钥”(处理器 ID、mac 和软件生成的密钥)。

using System.Management;
using System.Windows.Forms;

try

     ManagementObjectSearcher searcher = 
         new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor"); 

     foreach (ManagementObject queryObj in searcher.Get())
     
         Console.WriteLine("-----------------------------------");
         Console.WriteLine("Win32_Processor instance");
         Console.WriteLine("-----------------------------------");
         Console.WriteLine("Architecture: 0", queryObj["Architecture"]);
         Console.WriteLine("Caption: 0", queryObj["Caption"]);
         Console.WriteLine("Family: 0", queryObj["Family"]);
         Console.WriteLine("ProcessorId: 0", queryObj["ProcessorId"]);
     

catch (ManagementException e)

    MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);

Win32_Processor

Retrieving Hardware Identifiers in C# with WMI 彼得·布隆伯格

【讨论】:

WMI 不是唯一的。它没有任何独特的字段。我有一堆计算机,所有 WMI Bios、cpu、显卡、网络名称都具有相同的值。【参考方案7】:

是的,我们可以得到一个由物理地址、唯一驱动器 ID、硬盘驱动器 ID(卷序列号)、CPU ID 和 BIOS ID 组合而成的代码。 示例(Full example):

//Main physical hard drive ID
    private static string diskId()
    
        return identifier("Win32_DiskDrive", "Model")
        + identifier("Win32_DiskDrive", "Manufacturer")
        + identifier("Win32_DiskDrive", "Signature")
        + identifier("Win32_DiskDrive", "TotalHeads");
    
    //Motherboard ID
    private static string baseId()
    
        return identifier("Win32_BaseBoard", "Model")
        + identifier("Win32_BaseBoard", "Manufacturer")
        + identifier("Win32_BaseBoard", "Name")
        + identifier("Win32_BaseBoard", "SerialNumber");
    

【讨论】:

【参考方案8】:

你不应该使用 MAC,这是不好的方式。因为有些操作系统每天都在改变它。我的经验:Tools.CpuID.ProcessorId() + volumeSerial;

string volumeSerial = "";
    try 
        ManagementObject dsk = new ManagementObject(@"win32_logicaldisk.deviceid=""C:""");
        dsk.Get();
        volumeSerial = dsk["VolumeSerialNumber"].ToString();
     catch 
        try 
            ManagementObject dsk = new ManagementObject(@"win32_logicaldisk.deviceid=""D:""");
            dsk.Get();
            volumeSerial = dsk["VolumeSerialNumber"].ToString();
         catch  File.WriteAllText("disk.mising","need C or D"); Environment.Exit(0); 
    

public class CpuID
    
        [DllImport("user32", EntryPoint = "CallWindowProcW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
        private static extern IntPtr CallWindowProcW([In] byte[] bytes, IntPtr hWnd, int msg, [In, Out] byte[] wParam, IntPtr lParam);

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool VirtualProtect([In] byte[] bytes, IntPtr size, int newProtect, out int oldProtect);

        const int PAGE_EXECUTE_READWRITE = 0x40;



        public static string ProcessorId()
        
            byte[] sn = new byte[8];

            if (!ExecuteCode(ref sn))
                return "ND";

            return string.Format("01", BitConverter.ToUInt32(sn, 4).ToString("X8"), BitConverter.ToUInt32(sn, 0).ToString("X8"));
        

        private static bool ExecuteCode(ref byte[] result)
    
        int num;

        /* The opcodes below implement a C function with the signature:
         * __stdcall CpuIdWindowProc(hWnd, Msg, wParam, lParam);
         * with wParam interpreted as an 8 byte unsigned character buffer.
         * */

        byte[] code_x86 = new byte[] 
            0x55,                      /* push ebp */
            0x89, 0xe5,                /* mov  ebp, esp */
            0x57,                      /* push edi */
            0x8b, 0x7d, 0x10,          /* mov  edi, [ebp+0x10] */
            0x6a, 0x01,                /* push 0x1 */
            0x58,                      /* pop  eax */
            0x53,                      /* push ebx */
            0x0f, 0xa2,                /* cpuid    */
            0x89, 0x07,                /* mov  [edi], eax */
            0x89, 0x57, 0x04,          /* mov  [edi+0x4], edx */
            0x5b,                      /* pop  ebx */
            0x5f,                      /* pop  edi */
            0x89, 0xec,                /* mov  esp, ebp */
            0x5d,                      /* pop  ebp */
            0xc2, 0x10, 0x00,          /* ret  0x10 */
        ;
        byte[] code_x64 = new byte[] 
            0x53,                                     /* push rbx */
            0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, /* mov rax, 0x1 */
            0x0f, 0xa2,                               /* cpuid */
            0x41, 0x89, 0x00,                         /* mov [r8], eax */
            0x41, 0x89, 0x50, 0x04,                   /* mov [r8+0x4], edx */
            0x5b,                                     /* pop rbx */
            0xc3,                                     /* ret */
        ;

        byte[] code;

        if (IsX64Process())
            code = code_x64;
        else 
            code = code_x86;

        IntPtr ptr = new IntPtr(code.Length);

        if (!VirtualProtect(code, ptr, PAGE_EXECUTE_READWRITE, out num))
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

        ptr = new IntPtr(result.Length);

        try 
            return (CallWindowProcW(code, IntPtr.Zero, 0, result, ptr) != IntPtr.Zero);
         catch  System.Windows.Forms.MessageBox.Show("Память повреждена"); return false; 
    

        private static bool IsX64Process()
        
            return IntPtr.Size == 8;
        

    

【讨论】:

刚刚遇到我使用 ProcessorId + VolumeSerialNumber 的情况,两台笔记本电脑的 id 完全相同... ProcessorId 似乎不再生成唯一值:这里的所有机器都具有相同的 ProcessorId(不同的 Win 版本,不同的供应商)......在 Net 4.5 上 结果阵列的例子,从实际生活BFEBFBFF000006FBQ0WNWNFQF678084A BFEBFBFF000306A9NCYRXNJZF6815BA5 BFEBFBFF00030673K1HBRQ3ZCAF70541 078BFBFF000306A9BBW0BNBX1C70EEFF BFEBFBFF0001067AE0WRWOJZ68E3340B BFEBFBFF000306A9RCHBRRNTECACAE50 BFEBFBFF000306A9NBGVQNCCCC3A320F BFEBFBFF000206A7NBXGBRGDC642137D BFEBFBFF000306A9K0INZMKB12D2C5C7 BFEBFBFF00040651PAW0BOZV22B7BECF BFEBFBFF000306A9BCGRQMBRE829E19B 1F8BFBFF000306A9M1IWCMNYE2A678BC 跨度>【参考方案9】:

据我所知,有两种可能的方法:

    获取系统的处理器ID:

    public string getCPUId()
    
        string cpuInfo = string.Empty;
        ManagementClass mc = new ManagementClass("win32_processor");
        ManagementObjectCollection moc = mc.GetInstances();
    
        foreach (ManagementObject mo in moc)
        
            if (cpuInfo == "")
            
                //Get only the first CPU's ID
                cpuInfo = mo.Properties["processorID"].Value.ToString();
                break;
            
        
        return cpuInfo;
    
    

    获取系统的UUID:

    public string getUUID()
    
            Process process = new Process();
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
            startInfo.FileName = "CMD.exe";
            startInfo.Arguments = "/C wmic csproduct get UUID";
            process.StartInfo = startInfo;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.Start();
            process.WaitForExit();
            string output = process.StandardOutput.ReadToEnd();
            return output;
    
    

【讨论】:

【参考方案10】:

也许最简单的方法是。 获取DeviceId Nuget 包

并像使用它一样

string deviceId = new DeviceIdBuilder()
.AddMachineName()
.AddMacAddress()
.AddProcessorId()
.AddMotherboardSerialNumber()
.ToString();

您可以个性化用于生成 ID 的信息

Github Project

【讨论】:

你知道这是否适用于任何设备? iPhone、android 它不再起作用了【参考方案11】:

下面的site 使用System.Management 来完成同样的事情是在控制台应用程序中非常简洁的方式

【讨论】:

以上是关于获取唯一的机器 ID的主要内容,如果未能解决你的问题,请参考以下文章

windows和Linux获取系统和硬件编号ID作为唯一标识

用java获得机器的唯一号

雪花算法中机器id保证全局唯一

如何获取Android唯一标识

如何生成唯一的数字ID并在java中保持计数?

oracle生成主键唯一的id,函数SYS_GUID()