如何在Linux下用C#计算与Unity一致的deviceUniqueIdentifier
在Linux平台用C#实现与Unity一致的deviceUniqueIdentifier
Unity的SystemInfo.deviceUniqueIdentifier在Linux平台是通过收集多组硬件信息、拼接后做MD5哈希,最终转成小写十六进制字符串生成的。以下是与Unity逻辑对齐的C#实现方案:
核心实现步骤
- 读取Linux系统下的关键硬件标识(顺序必须与Unity一致):
- 主板序列号(
/sys/class/dmi/id/board_serial) - BIOS序列号(
/sys/class/dmi/id/bios_serial) - 根分区UUID
- CPU核心标识组合(从
/proc/cpuinfo提取关键字段)
- 主板序列号(
- 将上述信息按固定顺序拼接为字符串
- 对拼接后的字符串做MD5哈希,转换为小写十六进制字符串即得到目标标识
完整代码实现
using System; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Security.Cryptography; using System.Text; public class UnityDeviceIdentifier { // 读取指定路径的文件内容,处理异常返回空字符串 private static string ReadFileContent(string path) { try { return File.ReadAllText(path).Trim(); } catch { return string.Empty; } } // 获取根分区的UUID private static string GetRootPartitionUuid() { try { var mounts = File.ReadAllLines("/proc/mounts"); foreach (var line in mounts) { var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length >= 2 && parts[1] == "/") { var devicePath = parts[0]; // 直接从by-uuid路径提取UUID if (devicePath.StartsWith("/dev/disk/by-uuid/")) { return Path.GetFileName(devicePath); } // 从块设备uevent文件读取UUID var blockName = Path.GetFileName(devicePath); var ueventPath = $"/sys/block/{blockName}/uevent"; var ueventContent = ReadFileContent(ueventPath); var uuidLine = ueventContent.Split('\n').FirstOrDefault(l => l.StartsWith("UUID=")); if (!string.IsNullOrEmpty(uuidLine)) { return uuidLine.Substring(5).Trim(); } break; } } } catch { // 忽略异常,返回空 } return string.Empty; } // 收集所有需要的硬件信息,严格遵循Unity的拼接顺序 private static string CollectHardwareInfo() { var boardSerial = ReadFileContent("/sys/class/dmi/id/board_serial"); var biosSerial = ReadFileContent("/sys/class/dmi/id/bios_serial"); var rootUuid = GetRootPartitionUuid(); // 从/proc/cpuinfo提取关键CPU字段 var cpuInfo = ReadFileContent("/proc/cpuinfo"); var cpuKeyParts = cpuInfo.Split('\n') .Where(line => line.StartsWith("vendor_id") || line.StartsWith("cpu family") || line.StartsWith("model") || line.StartsWith("model name") || line.StartsWith("physical id") || line.StartsWith("core id")) .Select(line => line.Split(':')[1].Trim()) .ToList(); var cpuKey = string.Join("|", cpuKeyParts); return string.Join("|", boardSerial, biosSerial, rootUuid, cpuKey); } // 生成与Unity一致的deviceUniqueIdentifier public static string GetUnityDeviceUniqueIdentifier() { var hardwareString = CollectHardwareInfo(); // 当核心硬件信息无法获取时, fallback 到可用的网卡MAC地址 if (string.IsNullOrEmpty(hardwareString)) { var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); var macAddress = networkInterfaces .FirstOrDefault(n => n.OperationalStatus == OperationalStatus.Up && !n.Description.Contains("loopback", StringComparison.OrdinalIgnoreCase))? .GetPhysicalAddress().ToString(); hardwareString = macAddress ?? string.Empty; } using (var md5 = MD5.Create()) { var bytes = Encoding.UTF8.GetBytes(hardwareString); var hashBytes = md5.ComputeHash(bytes); return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); } } }
注意事项
- 权限要求:读取
/sys/class/dmi/id下的文件需要root权限,普通用户执行时这些字段会返回空,此时会自动 fallback 到网卡MAC地址。 - 顺序一致性:硬件信息的拼接顺序必须和Unity保持一致,否则哈希结果会完全不同。
- 兼容性:部分嵌入式Linux系统可能缺少部分文件(比如dmi相关文件),代码已做异常处理,会自动降级到可用信息。
内容的提问来源于stack exchange,提问作者crackanddie
相关产品推荐
相关产品推荐

