前言
程序员为了避免本人的成绩被白嫖,或者公布的软件在公司层面上要做一些防剽窃解决,这时就须要在软件层面上加锁、加密等操作。
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 源码