什么是一个好的独特PC标识符?

52
我正在查看这个教程中的代码,发现它使用My.Computer.Name保存不应该在计算机之间漫游的设置。然而,用户很可能会有两台同名的PC。例如,如果他们想在每台PC上使用相同的用户名,他们很可能最终会得到两台名为“用户名-PC”的PC。
有什么好的方法来识别不同的PC吗?PC是否与GUID相关联,或者我应该尝试从某些硬件上获取序列号?我不在乎标识是否会持续到重新安装Windows。
(我链接的教程是VB.Net的,但我正在用C#实现它)

https://dev59.com/B3RB5IYBdhLWcg3wSVcI - sancho.s ReinstateMonicaCellio
https://dev59.com/Z3VD5IYBdhLWcg3wE3Ro - sancho.s ReinstateMonicaCellio
13个回答

56

一些好的标识符:

  • MAC地址:很容易获取,通常是唯一的。不过它可以被欺骗/轻易更改,所以这要取决于需要多独特。
  • CPU序列号:在许多旧系统上不可用,但确实存在。查看此MSDN页面。它不会改变,但绑定在计算机上。
  • HDD序列号:可能不会改变,但如果硬盘损坏可能会有麻烦。查看此MSDN页面

4
+1 是的,小心处理MAC地址。我之前遇到了很多问题,因为MAC地址会改变和重复。非常麻烦。 - John MacIntyre
2
@Steven:好观点。不过,我认为它是系统上“最可靠”的ID之一。相对于硬盘故障而言,CPU的故障相对较少。同时需要注意的是,在处理虚拟化时,这个数字可能会变得混乱。 - mattbasta
@qJake 是哪个属性? - bubi
1
避免使用MAC地址的另一个原因是:我认为对于笔记本电脑来说,第一张启用的网卡的MAC地址不是一个好主意(我仍然没有解决我的问题,正在寻找其他解决方案)。如果您连接了网络电缆,它们会更改卡并使用WiFi。 - bubi
1
@bubi SerialNumber 属性获取 CPU 序列号。在 MSDN 链接中查找并使用 Ctrl+F 搜索 "SerialNumber",阅读该属性所适用的操作系统的最后一个注释。 - qJake
显示剩余3条评论

25
如果您使用Windows操作系统,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\ProductId 是每台机器/每个Windows安装的唯一标识,而其他答案中的MAC地址、处理器SN和硬盘SN则在Windows重装或双启动情况下保持不变。

1
这是Windows用来检测它是否已安装到不同机器上的编号吗? - Steven Sudit
2
好的,这里的积极意义在于,任何能够更改此产品ID的东西也将对Windows本身造成重大困扰。 :-) - Steven Sudit
2
@Rory 我相信执行 sysprep 将会导致在第一次启动系统时更改该ID(如果您正在使用系统映像,那么您应该 真的 使用 sysprep)。 - Scott Chamberlain
1
这个键不在我的 W8.1 64位系统上。 - pomarc
2
我知道已经有一段时间了,但今天我在这里找到了另一个答案,因此我想知道HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\ProductIdHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid中的ID之间有什么区别? - Skipper
显示剩余5条评论

11

回答这个问题的真实答案是:没有这样的东西。

有几个“接近”的解决方案,但每一个都有自己的限制。

所有硬件ID - 硬件会更改。而且,在许多情况下,您可以更改这些标识符(例如,MAC欺骗)。

正如我已经评论过的那样,SID也不太好,因为如果从映像安装计算机,则SID不会更改。SID由Windows安装生成,如果未安装Windows,而是从映像中复制,则SID不会更改(尽管通常会重新生成它,因为关于“安全风险”的错误传言 - 你不能指望它)。

计算机名称 - 好吧,正如提到的那样,它们应该是唯一的,但没有以任何方式强制执行。

您可以实施的另一种解决方案是生成自己的唯一标识符并在本地存储它(假设您可以这样做)。再次强调,如果使用您的应用程序对计算机进行了成像,则此解决方案将无法工作。

对于您来说最好的解决方案实际上取决于您想要实现什么目标。我在一个相当大的网络中遇到了同样的问题,我的情况下最好的解决方案是使用计算机名称。如果您绝对确定您的进程不会被成像,我将使用Guid生成唯一标识符,因为它可能是最安全的。


9
这里有一种独特识别计算机的方法。使用System.Management获取Win32_BIOS,您可以从计算机的BIOS中获取唯一的值。
请参阅:Win32_BIOS类,http://msdn.microsoft.com/en-us/library/aa394077.aspx
using System.Management;

string UniqueMachineId()
{
    StringBuilder builder = new StringBuilder();

    String query = "SELECT * FROM Win32_BIOS";
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
    //  This should only find one
    foreach (ManagementObject item in searcher.Get())
    {
        Object obj = item["Manufacturer"];
        builder.Append(Convert.ToString(obj));
        builder.Append(':');
        obj = item["SerialNumber"];
        builder.Append(Convert.ToString(obj));
    }

return builder.ToString();
}

使用相似的逻辑,您也可以遍历“Win32_DiskDrive”http://msdn.microsoft.com/en-us/library/aa394132.aspx,并获取每个物理驱动器的“SerialNumber”。在这种情况下,

    foreach (ManagementObject item in searcher.Get())

应该找到多个项目


6
使用 BIOS 序列号并不安全,因为你可能会得到很多相同的字符串:"To be filled by O.E.M."。 - movAX13h

8

使用三个半唯一且半恒定的标识符。根据规则,只需其中两个即可进行正确认证。更新偶尔错误的那个标识符中的注册数据。


这是唯一正确的答案。他们需要涵盖潜在可替换部件的范围。我会使用BIOS序列号、硬盘ID和Windows序列号。 - richard

2

使用网络卡的MAC地址。它应该是唯一的。但是它可以被更改。这取决于你期望用户有多恶意以及你的应用程序有多关键。

以下是一些示例代码:

public string GetMACAddress() {
    ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
    ManagementObjectCollection moc = mc.GetInstances();

    string MACAddress = String.Empty;

    foreach (ManagementObject mo in moc) {
        if (MACAddress == String.Empty) { // only return MAC Address from first card
            if ((bool)mo["IPEnabled"] == true) MACAddress = mo["MacAddress"].ToString();
        }
        mo.Dispose();
    }

    return MACAddress;
}

1

你可以使用任何网络接口的MAC地址。你也可以结合多个信息源,如硬盘序列号、MAC地址、处理器类型来计算哈希值。


2
使用的来源越多,可靠性就越高。然而,使用的来源越多,你就越容易受到误报的影响。 - Steven Sudit

1

我们使用来自Win32_processorProcessorID和来自Win32_ComputerSystemProductUUID的组合:

ManagementObjectCollection mbsList = null;
ManagementObjectSearcher mos = new ManagementObjectSearcher("Select ProcessorID From Win32_processor");
mbsList = mos.Get();
string processorId = string.Empty;
foreach (ManagementBaseObject mo in mbsList)
{
    processorId = mo["ProcessorID"] as string;
}

mos = new ManagementObjectSearcher("SELECT UUID FROM Win32_ComputerSystemProduct");
mbsList = mos.Get();
string systemId = string.Empty;
foreach (ManagementBaseObject mo in mbsList)
{
    systemId = mo["UUID"] as string;
}

var compIdStr = $"{processorId}{systemId}";

以前,我们使用了处理器ID("Select ProcessorID From Win32_processor")和主板序列号("SELECT SerialNumber FROM Win32_BaseBoard")的组合,但后来我们发现主板序列号可能没有填写,或者填写了统一值:

  • 由O.E.M.填充
  • 默认字符串

因此,值得考虑这种情况。

还要记住,ProcessorID编号在不同计算机上可能相同。


0
在托管网络环境中,最好、最可靠的标识符可能是您创建的标识符,但也存在一些缺点。
一些(很多?)制造商提供了一个实用程序,允许您设置存储在固件中的资产标记。这可能是一个可引导的实用程序,或者它可能在 Windows 中运行,甚至可能内置于固件设置中。这个“标记”是一个任意文本字符串,您可以将其设置为任何您想要的内容,然后使用 WMIWin32_SystemEnclosure 读取它...
string[] selectedProperties = new string[] { "SMBIOSAssetTag" };
ObjectQuery enclosureQuery = new SelectQuery("Win32_SystemEnclosure", null, selectedProperties);

using (ManagementObjectSearcher enclosureSearcher = new ManagementObjectSearcher(enclosureQuery))
using (ManagementObjectCollection enclosureCollection = enclosureSearcher.Get())
{
    foreach (ManagementObject enclosure in enclosureCollection)
    {
        string assetTag = (string) enclosure.GetPropertyValue("SMBIOSAssetTag");
    }
}

优点:

  • 您可以使用任何方案(例如,包括日期、部门、递增整数、GUID等)。
  • 您可以为所有机器使用一个方案,而不必处理特定于制造商的方案。
  • 通过自己分配和跟踪标识符,您可以保证它们是唯一的。不依赖于制造商设置的标识符意味着在制造商内部或制造商之间没有重复的风险。
  • 标识符存储在固件中,而不是硬盘上,因此它将在重新格式化、升级等情况下存活,但也不会被备份/镜像/克隆复制。

缺点:

  • 你需要实际设置资产标签;在你这样做之前,它们都是空白的。
  • 设置机器的资产标签可能需要物理访问和重新启动。
  • 资产标签不是只写一次的,因此可能会被更改或删除。
    • 受密码保护的固件应该在更改标签之前要求输入密码,但这并不保证。
  • 通过自己分配和跟踪标识符,不仅有分配和跟踪标识符的开销,而且如果不小心,还可能引入重复。
  • 为此目的使用资产标签需要所有机器支持设置资产标签并正确报告给WMI。

0

我认为在同一域中不可能有两台计算机具有相同的名称。您尝试过捕获域名吗?


2
我有两台在同一域中且名称相同的电脑 :-) 我在XP和Win 7之间双启动...虽然已经超过两个星期没有启动XP了。 - SQLMenace
@SQLMenace:除非它们具有相同的SID,否则网络仍将它们视为不同的盒子。 - Steven Sudit
这是同一个盒子..我不得不做一些花哨的东西,使它从两个盒子中都能访问到该域。 - SQLMenace

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接