Qt实现跨平台硬件唯一标识符生成与校验(Windows/Linux)
·
Qt实现跨平台硬件唯一标识符生成与校验(Windows/Linux)
在软件开发过程中,经常需要实现“设备绑定”功能——让程序仅能在指定硬件设备上运行。本文基于Qt框架,实现跨平台(Windows/Linux)获取硬件唯一标识、生成加密标识符,并在程序启动时校验标识符的完整方案。
核心需求拆解
- 跨平台获取硬件特征(CPU、硬盘、BIOS序列号),保证唯一性;
- 对硬件特征进行MD5加密,生成不可逆向的唯一标识符;
- 程序启动时校验本地存储的标识符与当前硬件标识符是否匹配,不匹配则退出程序;
- 首次运行时自动生成并保存标识符到本地文件(key.db)。
核心函数实现与解析
1. 跨平台获取硬件信息(getHardwareIdentifier)
该函数针对Windows/Linux分别实现硬件信息采集:
- Windows:通过
wmic命令获取CPU、硬盘、BIOS序列号; - Linux:通过
lshw命令提取CPU、硬盘、BIOS核心特征。
#include <QProcess>
#include <QString>
#include <QStringList>
// 获取CPU、硬盘、BIOS等硬件信息,生成原始特征串(跨平台适配)
QString getHardwareIdentifier()
{
#ifdef Q_OS_WIN
// Windows平台:通过wmic命令获取硬件序列号
QProcess process;
QString cpuId, diskSerial, biosSerial;
// 1. 获取CPU序列号(ProcessorId)
process.start("wmic CPU get ProcessorId");
process.waitForFinished(5000); // 超时5秒,避免进程卡死
QString cpuOutput = QString::fromLocal8Bit(process.readAllStandardOutput());
QStringList cpuLines = cpuOutput.split("\n", QString::SkipEmptyParts);
if (cpuLines.size() >= 2) { // 第1行是表头,第2行是实际CPU序列号
cpuId = cpuLines.at(1).trimmed();
}
process.close(); // 释放进程资源
// 2. 获取硬盘序列号(DISKDRIVE SerialNumber)
process.start("wmic DISKDRIVE get SerialNumber");
process.waitForFinished(5000);
QString diskOutput = QString::fromLocal8Bit(process.readAllStandardOutput());
QStringList diskLines = diskOutput.split("\n", QString::SkipEmptyParts);
if (diskLines.size() >= 2) {
diskSerial = diskLines.at(1).trimmed();
// 兼容多硬盘场景:取第一个非空序列号
if (diskSerial.isEmpty() && diskLines.size() > 2) {
for (int i = 1; i < diskLines.size(); i++) {
diskSerial = diskLines.at(i).trimmed();
if (!diskSerial.isEmpty()) break;
}
}
}
process.close();
// 3. 获取BIOS序列号
process.start("wmic BIOS get SerialNumber");
process.waitForFinished(5000);
QString biosOutput = QString::fromLocal8Bit(process.readAllStandardOutput());
QStringList biosLines = biosOutput.split("\n", QString::SkipEmptyParts);
if (biosLines.size() >= 2) {
biosSerial = biosLines.at(1).trimmed();
}
process.close();
// 空值兜底,避免特征串缺失
cpuId = cpuId.isEmpty() ? "未知CPU序列号" : cpuId;
diskSerial = diskSerial.isEmpty() ? "未知硬盘序列号" : diskSerial;
biosSerial = biosSerial.isEmpty() ? "未知BIOS序列号" : biosSerial;
// 组合硬件特征串(加分隔符,提升可读性)
return cpuId + "-" + diskSerial + "-" + biosSerial;
#else
// Linux平台:通过lshw命令提取硬件特征
QProcess process;
process.start("lshw -short");
process.waitForFinished(5000);
QString output = process.readAllStandardOutput();
QString cpuInfo, diskInfo, biosInfo;
QStringList lines = output.split("\n", QString::SkipEmptyParts);
// 解析lshw输出,提取CPU、硬盘、BIOS核心信息
for (const QString& line : lines) {
if (line.contains("processor")) {
cpuInfo = line.split(QRegExp("\\s+"), QString::SkipEmptyParts).last().trimmed();
} else if (line.contains("disk")) {
diskInfo = line.split(QRegExp("\\s+"), QString::SkipEmptyParts).last().trimmed();
} else if (line.contains("bios")) {
biosInfo = line.split(QRegExp("\\s+"), QString::SkipEmptyParts).last().trimmed();
}
}
return cpuInfo + diskInfo + biosInfo; // 组合硬件特征串
#endif
}
关键说明:
- Windows的
wmic命令需要管理员权限(部分系统),建议程序以管理员身份运行; - Linux的
lshw需要提前安装(sudo apt install lshw),否则无法获取硬件信息; - 对空值进行兜底处理,避免因硬件信息读取失败导致程序崩溃。
2. 生成MD5加密的唯一标识符(generateUniqueIdentifier)
基于硬件原始特征串,通过MD5加密生成不可逆的唯一标识符,避免硬件信息明文泄露。
#include <QCryptographicHash>
// 基于硬件信息生成MD5加密的唯一标识符
QString generateUniqueIdentifier()
{
// 1. 获取硬件原始特征串
QString combinedInfo = getHardwareIdentifier();
// 2. MD5加密(输出16进制字符串)
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(combinedInfo.toUtf8());
return hash.result().toHex(); // 返回32位MD5加密字符串
}
关键说明:
- MD5加密后的字符串长度固定为32位,便于存储和比对;
- 加密过程不可逆,即使标识符泄露,也无法反推出原始硬件信息。
3. 校验硬件标识符(checkUniqueIdentifier)
程序启动时校验本地key.db文件中的标识符与当前硬件标识符是否匹配:
- 首次运行:自动创建
key.db并写入当前硬件标识符; - 非首次运行:比对标识符,不匹配则退出程序。
#include <QCoreApplication>
#include <QFile>
#include <QDir>
#include <QDebug>
#include <QStandardPaths>
// 校验硬件唯一标识符,不匹配则退出程序
void checkUniqueIdentifier()
{
// 1. 生成当前硬件的唯一标识符
QString uniqueId = generateUniqueIdentifier();
// 2. 定义key.db文件路径(程序运行目录下)
QString keyFilePath = QCoreApplication::applicationDirPath() + "/key.db";
QFile keyFile(keyFilePath);
if (keyFile.exists()) {
// 3. 文件存在:读取并比对标识符
if (keyFile.open(QIODevice::ReadOnly)) {
QString storedId = QString::fromUtf8(keyFile.readAll()).trimmed();
keyFile.close();
// 比对标识符
if (storedId != uniqueId) {
qDebug() << "错误:硬件标识符不匹配,程序将退出!";
exit(1); // 标识符不匹配,强制退出程序
} else {
qDebug() << "硬件标识符校验通过!";
}
}
} else {
// 4. 文件不存在:创建并写入当前标识符
QDir().mkpath(QFileInfo(keyFilePath).absolutePath()); // 确保目录存在
if (keyFile.open(QIODevice::WriteOnly)) {
keyFile.write(uniqueId.toUtf8());
keyFile.close();
qDebug() << "首次运行:已生成硬件标识符并保存至key.db";
} else {
qDebug() << "错误:无法创建key.db文件!";
exit(1);
}
}
}
关键说明:
key.db文件存储在程序运行目录下,可根据需求修改路径(如用户目录);- 比对失败时通过
exit(1)强制退出程序,也可替换为弹窗提示(GUI程序); - 写入文件前调用
QDir().mkpath()确保目录存在,避免路径不存在导致写入失败。
完整演示示例
将上述函数整合到Qt控制台程序中,完整演示流程:
#include <QCoreApplication>
#include <QProcess>
#include <QString>
#include <QStringList>
#include <QCryptographicHash>
#include <QFile>
#include <QDir>
#include <QDebug>
// ===== 复制上述3个核心函数(getHardwareIdentifier/generateUniqueIdentifier/checkUniqueIdentifier) =====
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 程序启动第一步:校验硬件标识符
checkUniqueIdentifier();
// 后续业务逻辑(示例:输出当前硬件标识符)
qDebug() << "当前硬件唯一标识符:" << generateUniqueIdentifier();
return a.exec();
}
运行效果
- 首次运行:
首次运行:已生成硬件标识符并保存至key.db
当前硬件唯一标识符: 5f4dcc3b5aa765d61d8327deb882cf99
- 非首次运行(硬件匹配):
硬件标识符校验通过!
当前硬件唯一标识符: 5f4dcc3b5aa765d61d8327deb882cf99
- 硬件不匹配(如拷贝程序到另一台电脑):
错误:硬件标识符不匹配,程序将退出!
注意事项
- 权限问题:
- Windows:
wmic命令需要管理员权限,建议程序以管理员身份运行; - Linux:
lshw需要root权限(sudo ./程序名),否则可能读取不到硬件信息。
- Windows:
- 跨平台兼容性:
- 需在Qt工程文件(.pro)中添加
QT += core; - 编译时需区分Windows/Linux平台(Qt的
Q_OS_WIN/Q_OS_LINUX宏)。
- 需在Qt工程文件(.pro)中添加
- 安全性优化:
- 本文使用MD5加密,若需更高安全性,可替换为SHA256;
- 可对
key.db文件进行加密存储(如AES),避免文件被篡改。
- 硬件信息异常处理:
- 部分虚拟机/特殊硬件可能无法读取序列号,需增加兜底逻辑(如使用网卡MAC地址补充)。
总结
本文实现了一套轻量级、跨平台的硬件绑定方案,核心是通过wmic/lshw获取硬件特征,结合MD5生成唯一标识符,并在程序启动时校验。该方案可直接集成到Qt项目中,适用于需要设备授权的软件场景(如商业软件、企业内部工具)。
如需进一步优化,可扩展方向:
- 支持网卡MAC地址、主板序列号等更多硬件特征;
- 对
key.db进行AES加密,防止文件被手动修改; - 增加网络校验逻辑(将标识符上传至服务器比对)。
更多推荐

所有评论(0)