Qt设置软件使用期限【新版防修改系统时间】
在工业软件或其他领域中,经常会对软件进行授权,软件需要付费进行有期限的使用。以下是我用Qt设计的设置软件使用期限的两种方案。
主体思想:
1.软件需要绑定机器,让用户无法通过复制在另一台机器上运行。
2.由厂家提供激活码供用户激活,每个激活码只对应指定的机器有效,且激活码需包含使用期限。为保证激活码不能重复使用,激活码需包含当天的日期信息,令激活码仅当天有效,软件过期或许可证丢失后无法使用之前激活码进行激活。
3.由于2的限制,因此需要在每台机器上产生机器码,供厂家用来生成激活码。
4.需有校验功能,校验激活码是否有效,校验是否过期。为了避免用户修改系统时间来延长使用期限,需要验证许可证中最后使用日期是否大于当前日期,若是,则说明用户修改了系统时间,校验不通过,此时可以在代码中删除许可证,或直接退出软件。
5.需要保存许可证或注册表。许可证和注册表信息存储的是当前激活的密钥和最后使用日期、有效期限。并在每次开启和关闭软件时对许可证内的最后使用日期进行更新。
一、获取机器码
要绑定机器,就要获取到机器的唯一标识,网卡的MAC地址是个不错的选择,因此可以拿MAC地址当做机器码。
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;elsebreak;}QString machineCode;for(int i=0;i<6;i++){machineCode.append(text.mid(0,2));text.remove(0,3);}return machineCode;
}
二、生成带使用期限的激活码
激活码可以自己编写一些算法,通过各种计算、移位、换位等操作来生成。下面是我的示例:
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;
}
三、校验激活码
校验的过程其实就是生成激活码(不带期限),与带期限的激活码进行比对和运算,判断其是否有效,若有效则计算使用期限。
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;}elsereturn 0;
}
四、生成许可证
为了后续校验方便,许可证内的激活码可以省略最后几个步骤。许可证内容由三部分组成:激活码、最后使用日期、软件有效期。
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();
}
五、软件每次启动时校验许可证
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;}
}
六、更新许可证
为防止用户篡改系统时间以达到延长使用期限的目的,许可证中加入了最后使用日期进行校验。在每次软件启动校验成功后、软件关闭时,更新许可证中的最后使用日期。
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();
}
以上内容为本地生成许可证的方式,也可以用写注册表的方式来代替,主体思想不变。