Qt实现跨平台硬件唯一标识符生成与校验(Windows/Linux)

在软件开发过程中,经常需要实现“设备绑定”功能——让程序仅能在指定硬件设备上运行。本文基于Qt框架,实现跨平台(Windows/Linux)获取硬件唯一标识、生成加密标识符,并在程序启动时校验标识符的完整方案。

核心需求拆解

  1. 跨平台获取硬件特征(CPU、硬盘、BIOS序列号),保证唯一性;
  2. 对硬件特征进行MD5加密,生成不可逆向的唯一标识符;
  3. 程序启动时校验本地存储的标识符与当前硬件标识符是否匹配,不匹配则退出程序;
  4. 首次运行时自动生成并保存标识符到本地文件(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();
}

运行效果

  1. 首次运行
首次运行:已生成硬件标识符并保存至key.db
当前硬件唯一标识符: 5f4dcc3b5aa765d61d8327deb882cf99
  1. 非首次运行(硬件匹配)
硬件标识符校验通过!
当前硬件唯一标识符: 5f4dcc3b5aa765d61d8327deb882cf99
  1. 硬件不匹配(如拷贝程序到另一台电脑)
错误:硬件标识符不匹配,程序将退出!

注意事项

  1. 权限问题
    • Windows:wmic命令需要管理员权限,建议程序以管理员身份运行;
    • Linux:lshw需要root权限(sudo ./程序名),否则可能读取不到硬件信息。
  2. 跨平台兼容性
    • 需在Qt工程文件(.pro)中添加QT += core
    • 编译时需区分Windows/Linux平台(Qt的Q_OS_WIN/Q_OS_LINUX宏)。
  3. 安全性优化
    • 本文使用MD5加密,若需更高安全性,可替换为SHA256;
    • 可对key.db文件进行加密存储(如AES),避免文件被篡改。
  4. 硬件信息异常处理
    • 部分虚拟机/特殊硬件可能无法读取序列号,需增加兜底逻辑(如使用网卡MAC地址补充)。

总结

本文实现了一套轻量级、跨平台的硬件绑定方案,核心是通过wmic/lshw获取硬件特征,结合MD5生成唯一标识符,并在程序启动时校验。该方案可直接集成到Qt项目中,适用于需要设备授权的软件场景(如商业软件、企业内部工具)。

如需进一步优化,可扩展方向:

  • 支持网卡MAC地址、主板序列号等更多硬件特征;
  • key.db进行AES加密,防止文件被手动修改;
  • 增加网络校验逻辑(将标识符上传至服务器比对)。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐