Windows下UUID常含短横线且大写,Linux下小写无横线;需过滤非十六进制字符、转小写、补足32字节,全零则跳过或用"no-uuid"替代;MAC需排除虚拟/回环接口并排序取首;CPU无真正序列号,应取vendor+signature+features三元组;拼接前标准化各字段,加固定盐值后用SHA-256生成64字符哈希。

获取主板 UUID 时 Windows 和 Linux 的行为差异
Windows 下通过 WMI 查询 Win32_ComputerSystemProduct.UUID 通常返回标准格式(含短横线、大写),但部分 OEM 机器会填入全零或占位符字符串(如 "00000000-0000-0000-0000-000000000000");Linux 下读取 /sys/class/dmi/id/product_uuid 默认小写且无短横线,需统一处理。不校验空值直接拼接会导致指纹在多台设备上重复。
实操建议:
- 读取后先用正则过滤非十六进制字符,再转为小写并补足 32 字节(不足则截断,超长则取前 32)
- 若结果全为
'0',跳过该字段或用固定盐值替代(如"no-uuid"),避免引入弱熵 - Linux 需检查
/sys/class/dmi/id是否可读(部分容器或虚拟机中被禁用),失败时 fallback 到dmidecode -s system-uuid(需 root)
网卡 MAC 地址采集要排除虚拟网卡和回环接口
直接调用 getifaddrs()(Linux/macOS)或 GetAdaptersAddresses()(Windows)会返回所有接口,包括 veth*、docker0、VirtualBox Host-Only、lo 等无效地址。用第一个非空 MAC 并不安全——虚拟机克隆后该“首选”MAC 往往相同。
实操建议:
- 过滤条件必须同时满足:
IFF_UP+!IFF_LOOPBACK+ 物理接口标识(Windows 查IF_TYPE_ETHERNET_CSMACD,Linux 检查/sys/class/net/{iface}/device是否存在) - 对所有符合条件的 MAC 做排序(字典序),取第一个——保证多网卡机器结果稳定
- MAC 全为
00:00:00:00:00:00或广播地址(ff:ff:ff:ff:ff:ff)时丢弃,不参与哈希
CPU ID 不等于 CPU 序列号,多数消费级 CPU 实际不可读
所谓“CPU ID”常被误解为唯一序列号,但 Intel 自 Pentium III 后已禁用该功能,AMD 也从未开放。当前主流方式是读取 cpuid 指令返回的 eax=1 时的 stepping/model/family(即 CPU 型号特征),或 eax=4 的 cache topology。这些值在同型号 CPU 间完全一致,无法区分个体。
实操建议:
- 放弃“CPU 序列号”幻想,改用
__cpuid(MSVC)或内联汇编(GCC/Clang)获取vendor id+signature+feature flags三元组,作为型号指纹的一部分 - 若需增强区分度,可附加
/proc/cpuinfo中的cpu cores、cache size(Linux)或Win32_Processor.NumberOfCores(Windows)——注意虚拟机中这些值可能被模拟 - 不要依赖
rdtsc或时间戳计数器,它不唯一且受频率调节影响
拼接与哈希:用 SHA-256 而非 MD5,且必须加盐
把原始字符串(如 "UUID+MAC+CPUINFO")直接喂给 SHA256 仍不够——攻击者可预计算常见硬件组合的哈希值。更糟的是,若某字段缺失(如无可用 MAC),拼接逻辑不同会导致同一机器生成不同指纹。
实操建议:
- 所有字段先标准化(UUID 小写无横线、MAC 去冒号小写、CPU info 取 ASCII 字符串),再按固定顺序用
'|'连接(如"uuid|mac|cpu_vendor|cpu_sig") - 必须加入编译期硬编码盐值(如
"f8a2b1e9-c7d4-460f-a1c3-2e9d8f7b0a5c"),拼在最前面,防止彩虹表攻击 - 使用 OpenSSL 或
std::hash不够——必须调用完整 SHA-256(如EVP_DigestInit_ex),输出 32 字节二进制再 hex 编码为 64 字符字符串
真正难的是虚拟化环境:QEMU/KVM 可伪造 DMI 信息,VMware 的 MAC 地址有固定 OUI 段,Hyper-V 的 CPU signature 会被统一重写。如果业务场景包含云主机或员工 BYOD 设备,得接受“无法 100% 防复制”,转而用指纹做辅助验证而非唯一绑定——比如结合首次运行时间、磁盘序列号(需管理员权限)、甚至 TLS 证书指纹做交叉校验。单纯靠 C++ 读几处硬件信息就想做到牢不可破,本身就是个危险假设。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











