1 使用C++实现,根据硬件信息,生成一个标识该设备的唯一ID
思路:
先获取cpuid,硬盘id,主板id
拼接成字符串,做hash得到一个唯一的设备id(unsigned int)
根据业务需求,改造成 guid 格式的字符串。
2 代码实现
#include <windows.h>
#include <string>
#include <iostream>
#include <list>
#include <functional>
#include <sstream>
#include <iomanip>
#include <map>
#include "getDeviceFinger.h"
#define BUFFER_SIZE (128)
using namespace std;
#pragma comment(lib, "msvcprt.lib")
typedef unsigned int size_t_32;
typedef unsigned long long size_t_64;
// BKDR Hash Function
static unsigned int BKDRHash(char* str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF);
}
static std::string hash32_to_guid(size_t_32 hash_value) {
char chHex_str[BUFFER_SIZE] = { 0 };
std::stringstream ss;
ss << std::setfill('0') << std::setw(8) << std::hex << hash_value;
std::string hex_str = ss.str();
strcpy_s(chHex_str, hex_str.c_str());
std::hash <std::string> hash_hex_str1;
size_t_32 num1 = BKDRHash(chHex_str);
std::stringstream ss1;
ss1 << std::setfill('0') << std::setw(8) << std::hex << num1;
std::string hex_str1 = ss1.str();
memset(chHex_str, 0, sizeof(chHex_str));
strcpy_s(chHex_str, hex_str1.c_str());
std::hash <std::string> hash_hex_str2;
size_t_32 num2 = BKDRHash(chHex_str);
std::stringstream ss2;
ss2 << std::setfill('0') << std::setw(8) << std::hex << num2;
std::string hex_str2 = ss2.str();
memset(chHex_str, 0, sizeof(chHex_str));
strcpy_s(chHex_str, hex_str2.c_str());
std::hash <std::string> hash_hex_str3;
size_t_32 num3 = BKDRHash(chHex_str);
std::stringstream ss3;
ss3 << std::setfill('0') << std::setw(8) << std::hex << num3;
std::string hex_str3 = ss3.str();
memset(chHex_str, 0, sizeof(chHex_str));
strcpy_s(chHex_str, hex_str3.c_str());
// 构造GUID的格式
std::string guid_str = "{" + hex_str.substr(0, 8) + "-" + hex_str1.substr(0, 4) + "-" +
hex_str1.substr(4, 4) + "-" +
hex_str2.substr(0, 4) + "-" +
hex_str2.substr(4, 4) + hex_str3.substr(0, 8) + "}";
return guid_str;
}
static bool getDeviceInfo(const char* cmd, std::list<std::string>& resultList)
{
char buffer[BUFFER_SIZE] = { 0 };
bool bRet = false;
FILE* pipe = _popen(cmd, "r");
if (pipe == nullptr) return bRet;
const char* name[20] = { "UUID", "ProcessorId", "SerialNumber" };
int len0 = strlen(name[0]), len1 = strlen(name[1]), len2 = strlen(name[2]);
bool isOk = false;
while (!feof(pipe))
{
if (fgets(buffer, BUFFER_SIZE, pipe))
{
if (strncmp(name[0], buffer, len0) == 0
|| strncmp(name[1], buffer, len1) == 0
|| strncmp(name[2], buffer, len2) == 0)
{
isOk = true;
continue;
}
if (!isOk || strcmp("\r\n", buffer) == 0)
{
continue;
}
bRet = true;
resultList.push_back(string(buffer));
}
}
_pclose(pipe);
return bRet;
}
static size_t_32 getBiosCpuDiskHashID()
{
//AllocConsole();
//ShowWindow(GetConsoleWindow(), SW_HIDE);
std::list <std::string> strList;
std::list <std::string>::iterator it;
// std::hash 会在32和64位模式得到不同结果,显式指定使用仅32位hash值
size_t_32 num;
char chHex_str[BUFFER_SIZE] = { 0 };
//主板UUID存在,使用主板UUID生成机器指纹
if (getDeviceInfo("wmic csproduct get UUID", strList)
&& (*strList.begin()).compare("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF\r\n") != 0)
{
strcpy_s(chHex_str, (*strList.begin()).c_str());
num = BKDRHash(chHex_str);
return num;
}
// 主板UUID不存在,使用CPUID,BIOS序列号,硬盘序列号生成机器指纹
std::string otherStr("");
strList.clear();
if (getDeviceInfo("wmic cpu get ProcessorId", strList))
{
otherStr.append(*strList.begin());
}
strList.clear();
if (getDeviceInfo("wmic bios get SerialNumber", strList))
{
otherStr.append(*strList.begin());
}
/*strList.clear();
if (getDeviceInfo("wmic diskdrive get SerialNumber", strList))
{
std::string allDiskNum("");
for (it = strList.begin(); it != strList.end(); it++)
{
allDiskNum.append(*it);
}
otherStr.append(*strList.begin());
}*/
memset(chHex_str, 0, sizeof(chHex_str));
strcpy_s(chHex_str, otherStr.c_str());
num = BKDRHash(chHex_str);
return num;
}
std::string getDeviceGUID()
{
test01();
size_t_32 num = getBiosCpuDiskHashID();
return hash32_to_guid(num);
}
3 由于系统的 std::hash 返回值是 size_t,在 x86 和 x64 模式下得到的结果不相同。
这里使用了 BKDRHash 来 hash。
验证 BKDRHash 的碰撞概率
void test01()
{
char chTmep[64] = "123456789abcdefghijklmnopqrstuvwxyz";
char chTemp1[128] = { 0 };
map<size_t_32, char*> mapNum;
int count = 0;
int maxNum = 1000 * 10000;
size_t_32 num;
for (int i = 0; i < maxNum; i++)
{
sprintf_s(chTemp1, "%s-%d", chTmep, i);
num = BKDRHash(chTemp1);
map<size_t_32, char*>::iterator it;
it = mapNum.find(num);
if (it != mapNum.end())
{
count++;
std::cout << "生成了重复的hash值:" << it->first << ", " << it->second << std::endl;
}
else {
mapNum.insert(std::make_pair(num, chTemp1));
}
}
std::cout << "生成了" << count << "/" << maxNum << "个重复的hash值" << std::endl;
}
分别使用 x86 和 x64 运行,一千万个得到 0 个重复。
4 参考链接: