Windows下普通用户态程序无法获取CPU序列号,因Intel自Pentium 4起已移除硬件支持,__cpuid或内联汇编调用EAX=1时EDX[31:18]和ECX[31:0]恒为0,且系统层屏蔽访问;替代方案应使用CPUID基础信息组合或TPM/SMBIOS UUID。

Windows 下普通用户态程序无法可靠获取 CPU 序列号(Serial Number),因为自 Pentium III 起,Intel 已在后续 CPU 中默认禁用该功能,且现代操作系统(包括 Windows 10/11)明确阻止用户代码读取 cpuid 的序列号字段。所谓“用 __cpuid 或内联汇编读出序列号”,多数情况下返回全 0 或触发异常。
为什么 __cpuid 返回的序列号总是 0
Intel 在 Pentium 4 及以后的 CPU 中移除了硬件级序列号支持;即使 CPU 支持 cpuid 的 EAX=1 功能,EDX[31:18](原序列号高位)和 ECX[31:0](低位)也恒为 0。这是硬件设计决定,不是权限或驱动问题。
- 调用
__cpuid(buf, 1)后检查buf[3](即EDX)和buf[2](即ECX),几乎总为 0 - Windows 内核会屏蔽对序列号相关位的访问,即使 CPU 硬件保留该字段(极少数嵌入式型号),用户态也无法绕过
-
cpuid指令本身不报错,但返回数据无意义——这不是代码写错了,而是数据源已不存在
内联汇编调用 cpuid 并不能绕过限制
用 __asm 或 GCC 的 asm volatile 手动执行 cpuid,结果与 __cpuid 完全一致:指令成功执行,寄存器被填入 0。内联汇编不提升权限,也不恢复已被废弃的硬件特性。
- VC++ 示例:
int cpuInfo[4] = {0}; __asm { mov eax, 1 cpuid mov cpuInfo[0], eax mov cpuInfo[1], ebx mov cpuInfo[2], ecx mov cpuInfo[3], edx } - GCC 示例:
unsigned int eax, ebx, ecx, edx; asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); - 无论哪种写法,
ecx和edx高位部分都不可信;依赖它做机器绑定或 license 校验必然失效
替代方案:用 CPUID 基础信息组合生成稳定标识
虽然序列号不可用,但 cpuid 仍可安全读取 CPU 厂商字符串、型号、家族、步进等只读信息。这些字段在同型号 CPU 上一致,可用于轻量级设备指纹(注意:虚拟机下易重复,不适用于强认证)。
- 厂商 ID(
EAX=0):ebx,edx,ecx拼出 "GenuineIntel" 或 "AuthenticAMD" - CPU 型号信息(
EAX=1):eax[11:8](family)、eax[7:4](model)、eax[3:0](stepping)可组合成类似0x6_5_3的标识 - 注意
model字段需按 Intel SDM 规则解码(如family=6时,extended model在eax[19:16]) - 若需更高唯一性,应结合
GetNativeSystemInfo+GetLogicalProcessorInformation获取核心数、缓存层级等,再哈希
真正需要硬件级唯一 ID 的场景(如商业 license),必须转向 TPM、主板 SMBIOS UUID 或 Windows 的 WMI Win32_ComputerSystemProduct.UUID——CPU 序列号这条路,从硬件到系统层,已经关死了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











