前言
程序员为了防止自己的成果被白嫖,或者发布的软件在公司层面上要做一些防抄袭处理,这时就需要在软件层面上加锁、加密等操作。
1、单机终端软件
这类软件一般在未联网的情况下使用,所以不能通过网络去判定是否得到授权,一般采用绑定硬件信息来对软件进行加密,这样软件与设备绑定就无法进行随意使用。
2、远程授权监控
终端软件在启动后就会跟服务器通讯来检查当前设备是否已经得到授权,可实现远程锁定程序,设置程序的使用时间和使用次数。
以上两种方法都是最基础的软件加密方法,根据加密的算法的复杂度其破解的难度也不同,不过作为普通的一种加密方法已经够用了,市面上有比较成熟的加密加壳的软件,其安全程度要远高于软件加密的方法,下次抽时间介绍一下软件VMProtect的加、解密过程。
1、思路分析
单机软件想要每次启动前去验证是否有授权,需要从软件的配置中获取授权的验证信息,这里以软件的使用次数来举例,我们需要把软件的可使用次数写入配置文件ini或数据库中。每次在软件重新启动时,我们根据配置文件中信息来判断软件剩余的使用次数。
这里设定软件的剩余使用次数为2次,不过这里有一个很明显的缺点,就是明文写到配置文件里,就可以自己修改,然后就失去了加密的属性,这里就需要对这个信息进行加密后再写入。这里使用Qt自带的base64对字符串进行加密。
2、实现函数
//加密
QString Widget::Encode(QString row)
{
QByteArray byteArray = row.toUtf8();
byteArray = byteArray.toBase64();
return byteArray;
}
//解密
QString Widget::Decode(QString passwd)
{
QByteArray byteArray = passwd.toUtf8();
byteArray = QByteArray::fromBase64(byteArray);
return byteArray;
}
//从配置文件获取信息
QString Widget::getInfoFromIni(QString str)
{
QString info;
QFile file(str);
if(!file.exists()) { //如果文件不存在
QString times_str = "RemainTime:"+time;
WriteInfo2Ini(str, times_str);
}
file.open(QFile::ReadWrite | QFile::Text);
info = file.readAll(); //读取信息
file.close();
return info;
}
//将信息写入到配置文件
void Widget::WriteInfo2Ini(QString str, QString info_text)
{
QFile file(str);
file.open(QFile::ReadWrite | QFile::Text | QFile::Truncate);
file.write(Encode(info_text).toUtf8()); //写入信息
file.close();
}
这种方法有一种弊端,就是如果重新覆盖配置文件可以跳出限制,比如这里如果事前拷贝一份ini文件,当软件次数为0后,重新拷贝原始ini文件后又获得初始的软件使用次数。
如果考虑不使用ini文件来记录授权信息,而是使用注册表来记录授权信息,这样软件使用者就不容易去发现授权信息的位置
void Widget::WriteInfo2Registry(QString str)
{
//写入注册表
QSettings settings("HKEY_CURRENT_USER\\Software\\Code_Encryption\\Settings",QSettings::NativeFormat);
settings.setValue("remain_times",Encode(str).toUtf8());
}
QString Widget::getInfoFromRegistry()
{
QString info;
//通过写入注册表来判断
QSettings settings("HKEY_CURRENT_USER\\Software\\Code_Encryption\\Settings",QSettings::NativeFormat);
info = settings.value("remain_times").toString();
return info;
}
3、示例代码
widget.cpp
#pragma execution_character_set("utf-8")
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QString str = getCpuId();
qDebug()<<"cpu id is"<<str;
// QString times_str = "RemainTime:"+time;
// WriteInfo2Registry(times_str);
Judge_Authorize_times(1);
}
Widget::~Widget()
{
delete ui;
}
QString Widget::getWMIC(const QString &cmd)
{
//获取cpu名称:wmic cpu get Name
//获取cpu核心数:wmic cpu get NumberOfCores
//获取cpu线程数:wmic cpu get NumberOfLogicalProcessors
//查询cpu序列号:wmic cpu get processorid
//查询主板序列号:wmic baseboard get serialnumber
//查询BIOS序列号:wmic bios get serialnumber
//查看硬盘:wmic diskdrive get serialnumber
QProcess p;
p.start(cmd);
p.waitForFinished();
QString result = QString::fromLocal8Bit(p.readAllStandardOutput());
QStringList list = cmd.split(" ");
result = result.remove(list.last(), Qt::CaseInsensitive);
result = result.replace("\r", "");
result = result.replace("\n", "");
result = result.simplified();
return result;
}
QString Widget::getCpuId() //获取CPU序列号
{
return getWMIC("wmic cpu get processorid");
}
QString Widget::getDiskNum() //获取硬盘序列号
{
return getWMIC("wmic diskdrive where index=0 get serialnumber");
}
QString Widget::Encode(QString row)
{
QByteArray byteArray = row.toUtf8();
byteArray = byteArray.toBase64();
return byteArray;
}
QString Widget::Decode(QString passwd)
{
QByteArray byteArray = passwd.toUtf8();
byteArray = QByteArray::fromBase64(byteArray);
return byteArray;
}
QString Widget::getInfoFromIni(QString str)
{
QString info;
QFile file(str);
if(!file.exists()) { //如果文件不存在
QString times_str = "RemainTime:"+time;
WriteInfo2Ini(str, times_str);
}
file.open(QFile::ReadWrite | QFile::Text);
info = file.readAll(); //读取信息
file.close();
return info;
}
void Widget::WriteInfo2Ini(QString str, QString info_text)
{
QFile file(str);
file.open(QFile::ReadWrite | QFile::Text | QFile::Truncate);
file.write(Encode(info_text).toUtf8()); //写入信息
file.close();
}
void Widget::WriteInfo2Registry(QString str)
{
//写入注册表
QSettings settings("HKEY_CURRENT_USER\\Software\\Code_Encryption\\Settings",QSettings::NativeFormat);
settings.setValue("remain_times",Encode(str).toUtf8());
}
QString Widget::getInfoFromRegistry()
{
QString info;
//通过写入注册表来判断
QSettings settings("HKEY_CURRENT_USER\\Software\\Code_Encryption\\Settings",QSettings::NativeFormat);
info = settings.value("remain_times").toString();
return info;
}
void Widget::Judge_Authorize_times(int type)
{
QString times_info;
if(type == 0)
{
//通过ini配置文件进行判断
times_info = getInfoFromIni("System.ini");
}
else
{
//通过写入注册表来判断
times_info = getInfoFromRegistry();
}
if(times_info.isEmpty())
{
QMessageBox::about(this,"提示","授权信息为空,请检查!");
exit(0);
}
else
{
QString info = Decode(times_info); //解码
if(info.contains(':')) { //信息正确
QStringList list = info.split(':');
if(list.length()>=1) {
if(list[1].toInt() <= 0) { //程序剩余使用次数不足
QMessageBox::about(this,"提示","请注册后再使用!");
exit(0); //程序退出
}else { //程序还有剩余使用次数
QString time_remain = QString::number(list[1].toInt()-1); //程序剩余使用次数减1
QString str = "RemainTime:"+time_remain;
if(type == 0) WriteInfo2Ini("System.ini", str);
else {
//写入注册表
WriteInfo2Registry(str);
}
QMessageBox::about(this,"提示","程序剩余使用次数:"+time_remain+"次");
}
}
}
}
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFile>
#include <QMessageBox>
#include <QProcess>
#include <QDebug>
#include <QSettings>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
QString getWMIC(const QString &cmd);
QString getCpuId();
QString getDiskNum();
//加密
QString Encode(QString row);
//解密
QString Decode(QString passwd);
//判断授权信息 0:使用ini方法判断;1:使用注册表方法判断
void Judge_Authorize_times(int type);
//从ini获取授权信息
QString getInfoFromIni(QString str);
//写入授权信息到ini
void WriteInfo2Ini(QString str, QString info_text);
//从注册表获取授权信息
QString getInfoFromRegistry();
//写入授权信息到注册表
void WriteInfo2Registry(QString str);
//授权程序使用次数为2次
QString time = "2";
};
#endif // WIDGET_H
4、效果展示
源码放到gitee仓库:Code_Encryption源码
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。