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();
}

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

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

请大家多多关注与支持!

相关推荐
四维碎片7 小时前
【Qt】UDP跨平台调试工具
qt·学习·udp
踏过山河,踏过海8 小时前
【用ui文件做个简单工具的开发,为什么修改完ui后,程序重新编译运行后,GUI界面还是不变呢?】
qt·ui
向阳开的夏天10 小时前
麒麟V10源码编译QT5.6.3 (x86 & arm64)
开发语言·qt
打码的猿11 小时前
Qt对话框不锁死主程序的方法
开发语言·qt
小小码农Come on15 小时前
Qt Creator常用设置
qt
wkm95616 小时前
在arm64 ubuntu系统安装Qt后编译时找不到Qt3DExtras头文件
开发语言·arm开发·qt
小小码农Come on18 小时前
QT开发环境安装
开发语言·qt
小小码农Come on19 小时前
QT内存管理
开发语言·qt
有理想的打工人19 小时前
QT的安装
qt
SilentSlot20 小时前
【QT-QML】8. 输入元素
qt·qml