Qt设置软件使用期限【新版防修改系统时间】

在工业软件或其他领域中,经常会对软件进行授权,软件需要付费进行有期限的使用。以下是我用Qt设计的设置软件使用期限的两种方案。

主体思想:

1.软件需要绑定机器,让用户无法通过复制在另一台机器上运行。

2.由厂家提供激活码供用户激活,每个激活码只对应指定的机器有效,且激活码需包含使用期限。为保证激活码不能重复使用,激活码需包含当天的日期信息,令激活码仅当天有效,软件过期或许可证丢失后无法使用之前激活码进行激活。

3.由于2的限制,因此需要在每台机器上产生机器码,供厂家用来生成激活码。

4.需有校验功能,校验激活码是否有效,校验是否过期。为了避免用户修改系统时间来延长使用期限,需要验证许可证中最后使用日期是否大于当前日期,若是,则说明用户修改了系统时间,校验不通过,此时可以在代码中删除许可证,或直接退出软件。

5.需要保存许可证或注册表。许可证和注册表信息存储的是当前激活的密钥和最后使用日期、有效期限。并在每次开启和关闭软件时对许可证内的最后使用日期进行更新。

一、获取机器码

要绑定机器,就要获取到机器的唯一标识,网卡的MAC地址是个不错的选择,因此可以拿MAC地址当做机器码。

cpp 复制代码
QString getMachineCode(){
    QString text;
    //获取非00-00-00-00-00-00的mac地址
    QRegExp regmac("00.00.00.00.00.00");
    foreach(QNetworkInterface interface,QNetworkInterface::allInterfaces()){
        text = interface.hardwareAddress();
        if(text.indexOf(regmac)>=0)
            continue;
        else
            break;
    }
    QString machineCode;
    for(int i=0;i<6;i++){
        machineCode.append(text.mid(0,2));
        text.remove(0,3);
    }
    return machineCode;
}

二、生成带使用期限的激活码

激活码可以自己编写一些算法,通过各种计算、移位、换位等操作来生成。下面是我的示例:

cpp 复制代码
QString newFunction(QString mc, int day)//mc为机器码,day为使用期限
{
    QString newcode;
    for(int i=0;i<6;i++){
        int index=mc.mid(i*2,2).toUInt(nullptr,16);
        newcode.append(QString::number(pwd[index],16));
    }
//激活码中日期信息
    QDate date = QDate::currentDate();
    int y=date.year()-2000;
    int m=date.month();
    int d=date.day();
    QString dts = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);
    qDebug()<<dts;
    int dt = dts.toInt(nullptr,2);
    QString dthex = QString::number(dt,16);

    QString copycode = newcode;
    copycode.replace(0,1,newcode[1]);
    copycode.replace(1,1,newcode[0]);
    copycode.replace(2,1,newcode[4]);
    copycode.replace(4,1,newcode[2]);
    copycode.replace(7,1,newcode[10]);
    copycode.replace(10,1,newcode[7]);

    QStringList binlst;
    binlst.append(hexToBin(copycode.mid(0,2)));
    binlst.append(hexToBin(copycode.mid(2,2)));
    binlst.append(hexToBin(copycode.mid(4,2)));
    binlst.append(hexToBin(copycode.mid(6,2)));
    binlst.append(hexToBin(copycode.mid(8,2)));
    binlst.append(hexToBin(copycode.mid(10,2)));
    int jy = 0;
    for(int i=7;i>=0;i--){
        int nums=0;
        for(int j=0;j<6;j++){
            if(binlst.at(j).at(i)=='1')
                nums++;
        }
        if(nums%2==1)
            jy += 1<<i;
    }
    qDebug()<<jy;
    jy += day;
    int local = day%6;
    copycode.insert(local*2,tenToHexL(jy));
    copycode.append(dthex);
    qDebug()<<copycode;
    return copycode;
}

三、校验激活码

校验的过程其实就是生成激活码(不带期限),与带期限的激活码进行比对和运算,判断其是否有效,若有效则计算使用期限。

cpp 复制代码
int Widget::getValidity(QString code)
{
    QDate date = QDate::currentDate();
    int y=date.year()-2000;
    int m=date.month();
    int d=date.day();
    QString dts = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);
    int dt = dts.toInt(nullptr,2);
    QString dthex = QString::number(dt,16);
    if(dthex!=code.mid(16,4)){//激活码内日期与当前日期不符,激活码过期
        qDebug()<<"code over time!";
        return 0;
    }
    code.remove(16,4);
    QString mc = getMachineCode();
    QString newcode;
    for(int i=0;i<6;i++){
        int index=mc.mid(i*2,2).toUInt(nullptr,16);
        newcode.append(QString::number(pwd[index],16));
    }

    QString copycode = newcode;
    copycode.replace(0,1,newcode[1]);
    copycode.replace(1,1,newcode[0]);
    copycode.replace(2,1,newcode[4]);
    copycode.replace(4,1,newcode[2]);
    copycode.replace(7,1,newcode[10]);
    copycode.replace(10,1,newcode[7]);

    QStringList binlst;
    binlst.append(hexToBin(copycode.mid(0,2)));
    binlst.append(hexToBin(copycode.mid(2,2)));
    binlst.append(hexToBin(copycode.mid(4,2)));
    binlst.append(hexToBin(copycode.mid(6,2)));
    binlst.append(hexToBin(copycode.mid(8,2)));
    binlst.append(hexToBin(copycode.mid(10,2)));
    int jy = 0;
    for(int i=7;i>=0;i--){
        int nums=0;
        for(int j=0;j<6;j++){
            if(binlst.at(j).at(i)=='1')
                nums++;
        }
        if(nums%2==1)
            jy += 1<<i;
    }
    int validity=0;
    for(int i=0;i<12;i+=2){
        if(copycode.mid(i,2)!=code.mid(i,2)){
            validity = code.mid(i,4).toInt(nullptr,16)-jy;
            code.remove(i,4);
            break;
        }
    }
    if(copycode==code){//激活码有效,生成许可证
        writeLicense(copycode,dthex,validity);
        return validity;
    }
    else
        return 0;
}

四、生成许可证

为了后续校验方便,许可证内的激活码可以省略最后几个步骤。许可证内容由三部分组成:激活码、最后使用日期、软件有效期。

cpp 复制代码
void writeLicense(QString code,QString dthex,int days)
{
    QDate yxq = QDate::currentDate().addDays(days);
    int y=yxq.year()-2000;
    int m=yxq.month();
    int d=yxq.day();
    QString dts = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);
    int dt = dts.toInt(nullptr,2);
    QString yxqhex = QString::number(dt,16);

    QFile file("license.dat");
    file.open(QIODevice::WriteOnly);
    QDataStream ds(&file);
    QByteArray ba;
    ba.resize(10);
    for(int i=0;i<6;i++){
        ba[i]=uchar(code.mid(i*2,2).toUInt(nullptr,16));
    }
    ba[6]=uchar(dthex.mid(0,2).toUInt(nullptr,16));
    ba[7]=uchar(dthex.mid(2,2).toUInt(nullptr,16));
    ba[8]=uchar(yxqhex.mid(0,2).toUInt(nullptr,16));
    ba[9]=uchar(yxqhex.mid(2,2).toUInt(nullptr,16));
    ds<<(QByteArray)ba;
    file.flush();
    file.close();
}

五、软件每次启动时校验许可证

cpp 复制代码
void Widget::checkLicense()
{
    QFile file("license.dat");
    file.open(QIODevice::ReadOnly);
    QDataStream ds(&file);
    QByteArray ba;
    ds>>ba;

    QString code = ba.toHex();
    QString shortcode = code.mid(0,12);

    QString mc = getMachineCode();
    QString newcode;
    for(int i=0;i<6;i++){
        int index=mc.mid(i*2,2).toUInt(nullptr,16);
        newcode.append(QString::number(pwd[index],16));
    }

    QString copycode = newcode;
    copycode.replace(0,1,newcode[1]);
    copycode.replace(1,1,newcode[0]);
    copycode.replace(2,1,newcode[4]);
    copycode.replace(4,1,newcode[2]);
    copycode.replace(7,1,newcode[10]);
    copycode.replace(10,1,newcode[7]);
    if(shortcode!=copycode){//激活码错误
        qDebug()<<"校验不通过";
        return;
    }

    QDate lastDate;
    QString dts = code.mid(12,4);
    int idt = dts.toInt(nullptr,16);
    int y = idt>>9;
    int m = (idt>>5) & ((1<<4)-1);
    int d = idt & ((1<<5)-1);
    lastDate.setDate(y+2000,m,d);
    if(lastDate.daysTo(QDate::currentDate())<0){//用户修改了系统时间
        qDebug()<<"时间非法";
        return;
    }

    QDate today = QDate::currentDate();
    y=today.year()-2000;
    m=today.month();
    d=today.day();
    QString s_today = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);
    int dt = s_today.toInt(nullptr,2);
    QString s_yxq = code.mid(16,4);
    if(s_yxq.toInt(nullptr,16)<dt){//许可证已过期
        qDebug()<<"已过期";
        return;
    }
}

六、更新许可证

为防止用户篡改系统时间以达到延长使用期限的目的,许可证中加入了最后使用日期进行校验。在每次软件启动校验成功后、软件关闭时,更新许可证中的最后使用日期。

cpp 复制代码
void updateLicense()
{
    QFile file("license.dat");
    file.open(QIODevice::ReadWrite);
    QDataStream ds(&file);
    QByteArray ba;
    ds>>ba;

    QDate today = QDate::currentDate();
    int y=today.year()-2000;
    int m=today.month();
    int d=today.day();
    QString s_today = intToBin(y,7)+intToBin(m,4)+intToBin(d,5);
    int dt = s_today.toInt(nullptr,2);
    QString hex = QString::number(dt,16);
    ba[6]=uchar(hex.mid(0,2).toUInt(nullptr,16));
    ba[7]=uchar(hex.mid(2,2).toUInt(nullptr,16));
    file.resize(0);
    file.seek(0);
    ds<<ba;
    file.flush();
    file.close();
}

以上内容为本地生成许可证的方式,也可以用写注册表的方式来代替,主体思想不变。

想要完成代码的朋友可以私信作者获取。

请大家多多关注与支持!

相关推荐
王廷胡_白嫖帝3 小时前
Qt个人通讯录项目开发教程 - 从零开始构建联系人管理系统
开发语言·qt
大橘4 小时前
【qml-5】qml与c++交互(类型单例)
qt·qml
lxmyzzs15 小时前
pyqt5无法显示opencv绘制文本和掩码信息
python·qt·opencv
大橘15 小时前
【qml-4】qml与c++交互(类型多例)
qt·qml
Forward♞1 天前
Qt——文件操作
开发语言·c++·qt
王廷胡_白嫖帝1 天前
Qt猜数字游戏项目开发教程 - 从零开始构建趣味小游戏
开发语言·qt·游戏
王廷胡_白嫖帝1 天前
Qt密码生成器项目开发教程 - 安全可靠的随机密码生成工具
开发语言·qt
莫听穿林打叶声儿1 天前
Qt中使用QString显示平方符号(如²)
c++·qt
枫叶丹41 天前
【Qt开发】常用控件(五)
服务器·前端·qt