cpuid和序列号背后的那些故事

最近测试反馈了一个问题,每次重启服务器,我们某个版本的业务系统中的机器码都会改变,导致根据机器码算出来的许可证失效,从而使软件无法使用 。这个问题反馈了有一段时间了,但是本地一直没复现 。然后前几天测试说又复现了,马上去看了下测试环境,服务器是一台国产化FT S2500服务器,验证了下,果然如此,马上去看了下关键代码 。
public static String executelinuxCmd(int type) {try {String cmd = "dmidecode |grep 'Serial Number'";if (type == 1) {cmd = "fdisk -l";}//...} catch (IOException e) {//}return null;}public static String getSerialNumber(int type, String record, String symbol) {String execResult = executeLinuxCmd(type);String[] infos = execResult.split("n");for (String info : infos) {info = info.trim();if (info.indexOf(record) != -1) {String[] sn = info.replace(" ", "").split(symbol);return sn[1];}}//...}/*** 获取CPUID、硬盘序列号、mac地址、主板序列号** @return*/public static Map<String, String> getAllSn() {String os = System.getProperty("os.name");Map<String, String> snVo = new HashMap();if ("LINUX".equalsIgnoreCase(os)) {String mainboardNumber = getSerialNumber(0, "Serial Number", ":");String diskNumber = getSerialNumber(1, "Disk identifier", ":");snVo.put("diskid", diskNumber == null ? "tmpDiskId" : diskNumber.toUpperCase().replace(" ", ""));snVo.put("mainboard", mainboardNumber == null ? "tmpMainboard" : mainboardNumber.toUpperCase().replace(" ", ""));} else {这下明白了,它是取的CPU序列号作为机器码 。dmidecode的输出中有多个Serial Number,它只取了第一个,恰恰就是Processor Information,也就是我们常说的CPU序列号 。
Handle 0x0001, DMI type 4, 48 bytesProcessor InformationSocket Designation: CPU0Type: Central ProcessorFamily: ARMv8Manufacturer: PhytiumID: 33 66 1F 70 00 00 00 00Signature: Implementor 0x70, Variant 0x1, Architecture 15, Part 0x663, Revision 3Version: S2500Voltage: 0.8 VExternal Clock: 100 MHzMax Speed: 2100 MHzCurrent Speed: 2100 MHzStatus: Populated, EnabledUpgrade: UnknownL1 Cache Handle: 0x1001L2 Cache Handle: 0x1002L3 Cache Handle: 0x1003Serial Number: A5F9B0AD-E023-7E89-CF01-47772188AD003Asset Tag: 9EEC0F35-D6DB-EE11-4788-C0EE56755439Part Number: ABD15C29-35D3-1659-BFAF-AD57F39874C3Core Count: 64Core Enabled: 64Thread Count: 64Characteristics:64-bit capableMulti-CoreExecute ProtectionEnhanced VirtualizationPower/Performance ControlCPU支持过序列号功能,但是被人指责侵犯隐私,所以现在的规范中,CPU完全没有所谓的序列号 。
关于CPU序列号,其实还有一段历史 。在奔腾3中短暂地引入过这个功能,但是后来很快就被移除了 。
EAX=3: Processor Serial Number
See also: Pentium III § Controversy about privacy issues(https://en.wikipedia.org/wiki/Pentium_III#Controversy_about_privacy_issues)
This returns the processor’s serial number. The processor serial number was introduced on Intel Pentium III, but due to privacy concerns, this feature is no longer implemented on later models (PSN feature bit is always cleared). Transmeta’s Efficeon and Crusoe processors also provide this feature. AMD CPUs however, do not implement this feature in any CPU models.
For Intel Pentium III CPUs, the serial number is returned in EDX:ECX registers. For Transmeta Efficeon CPUs, it is returned in EBX:EAX registers. And for Transmeta Crusoe CPUs, it is returned in EBX register only.
Note that the processor serial number feature must be enabled in the BIOS setting in order to function.
所以,我们不应该使用CPU Serial Number来作为设备唯一性判断,而应该使用CPU ID来判断 。
1.windows下获取CPU ID如果是windows系统,根据MSDN文档:
http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx ProcessorId
Data type: string
Access type: Read-only
Processor information that describes the processor features. For an x86 class CPU, the field format depends on the processor support of the CPUID instruction. If the instruction is supported, the property contains 2 (two) Dword formatted values. The first is an offset of 08h-0Bh, which is the EAX value that a CPUID instruction returns with input EAX set to 1. The second is an offset of 0Ch-0Fh, which is the EDX value that the instruction returns. Only the first two bytes of the property are significant and contain the contents of the DX register at CPU reset—all others are set to 0 (zero), and the contents are in DWORD format."
可以用如下代码获取CPU ID
#include "stdafx.h"#include<iostream> int main(){int32_t deBuf[4];__cpuidex(deBuf, 01, 0);printf("%.8x%.8x", deBuf[3], deBuf[0]);getchar();return 0;}


推荐阅读