Loading... # 声明 **由于传播、利用此文所提供的信息而造成的任何直接或间接的后果及损失,均由使用者本人负责,五步十阁及文章作者不为此承担任何责任。五步十阁拥有对此文章的修改和解释权。如欲转载或者传播此文章,须保证此文章的完整性,包括版权声明等全部内容。未经五步十阁允许,不得任意修改或增减此文章内容,不得以任何方式将其用于商业目的。** 拿到个用Qt写的软件,需要.lic文件进行注册,但是不方便找人家要注册码,于是有了此文。 其软件启动后界面如下所示,包含注册ID(用于注册文件生成)。  掏出IDA,自动分析完成后,搜索注册文件名"./lic",如下图所示。  根据引用提示,跳转到`sub_62F760`,按下F5,发现分析出来的伪代码逻辑非常直观明了,部分代码如下: ```c++ QString::QString((QString *)v120, "./lic"); LOBYTE(v136) = 7; checkResult = checkLic(v126, (int)v120); // 授权认证 LOBYTE(v136) = 6; QString::~QString((QString *)v120); if ( !checkResult ) // 认证不通过,弹出提示框 { sub_4071AD(0); LOBYTE(v136) = 8; v4 = type_info::raw_name((type_info *)v126); sub_405074(v4); QDialog::exec((QDialog *)v38); v55 = 0; LOBYTE(v136) = 6; sub_403E40(v38); LOBYTE(v136) = 4; sub_405911(v126); LOBYTE(v136) = 1; QFile::~QFile((QFile *)v127); LOBYTE(v136) = 0; QString::~QString((QString *)v134); v136 = -1; QApplication::~QApplication((QApplication *)v122); return v55; } ``` 然后其 `checkLic`函数分析如下: ```c++ char __thiscall sub_5ED070(char *this, const struct QString *licFileName) { DWORD v2; // eax int v3; // ecx int v5; // [esp-4h] [ebp-7Ch] BYREF int v6; // [esp+0h] [ebp-78h] char v7[8]; // [esp+4h] [ebp-74h] BYREF int *v8; // [esp+Ch] [ebp-6Ch] char v9[4]; // [esp+10h] [ebp-68h] BYREF int v10; // [esp+14h] [ebp-64h] int v11; // [esp+18h] [ebp-60h] char v12[4]; // [esp+1Ch] [ebp-5Ch] BYREF char v13[4]; // [esp+20h] [ebp-58h] BYREF int v14; // [esp+24h] [ebp-54h] int v15; // [esp+28h] [ebp-50h] int v16; // [esp+2Ch] [ebp-4Ch] int v17; // [esp+30h] [ebp-48h] char v18[4]; // [esp+34h] [ebp-44h] BYREF char v19[4]; // [esp+38h] [ebp-40h] BYREF char v20[4]; // [esp+3Ch] [ebp-3Ch] BYREF int v21; // [esp+40h] [ebp-38h] int v22; // [esp+44h] [ebp-34h] int v23; // [esp+48h] [ebp-30h] int v24; // [esp+4Ch] [ebp-2Ch] int v25; // [esp+50h] [ebp-28h] char v26[4]; // [esp+54h] [ebp-24h] BYREF char v27[4]; // [esp+58h] [ebp-20h] BYREF char v28[4]; // [esp+5Ch] [ebp-1Ch] BYREF char v29[4]; // [esp+60h] [ebp-18h] BYREF char *v30; // [esp+64h] [ebp-14h] char v31; // [esp+6Ah] [ebp-Eh] char v32; // [esp+6Bh] [ebp-Dh] int v33; // [esp+74h] [ebp-4h] v30 = this; v2 = getVolumeSerialNumber(this); v25 = QString::number(v18, v2); v24 = v25; v33 = 0; v5 = v25; v23 = getCPUID(v19); v22 = v23; LOBYTE(v33) = 1; v21 = sub_405709(v20, v23, v5); // VolumeSerialNumber与CpuID拼接 QString::operator=(v30 + 8, v21); QString::~QString((QString *)v20); LOBYTE(v33) = 0; QString::~QString((QString *)v19); v33 = -1; QString::~QString((QString *)v18); v17 = QString::toLocal8Bit(v30 + 8, v12); v16 = v17; v33 = 2; v15 = QByteArray::toBase64(v17, v6); // base64编码===>此处字符串为提示界面ID v14 = v15; LOBYTE(v33) = 3; QString::operator=(v30 + 8, v15); LOBYTE(v33) = 2; QByteArray::~QByteArray((QByteArray *)v13); v33 = -1; QByteArray::~QByteArray((QByteArray *)v12); sub_4049B2(v26, v30 + 8, &a123_0); // 尾部添加字符串:"123" v33 = 4; v11 = QString::toLocal8Bit(v26, v9); v10 = v11; LOBYTE(v33) = 5; QByteArray::toBase64(v11, v6); // base64编码 LOBYTE(v33) = 7; QByteArray::~QByteArray((QByteArray *)v9); QCryptographicHash::hash(v28, v27, 1); // 根据Qt文档对QCryptographicHash::Algorithm 的说明,hash算法采用的md5摘要算法 LOBYTE(v33) = 8; QFile::QFile((QFile *)v7, licFileName); LOBYTE(v33) = 9; v5 = v3; v8 = &v5; j_unknown_libname_74(1); if ( (unsigned __int8)QFile::open(v7, v5) ) { QIODevice::readAll(v7, v29); // 读取 ./lic 文件 =>QByteArray LOBYTE(v33) = 10; if ( (unsigned __int8)qbyteArrayCmp((int)v29, (int)v28) )// ./lic文件字符与上面hash结果对比判断 { QFileDevice::close((QFileDevice *)v7); v32 = 1; LOBYTE(v33) = 9; QByteArray::~QByteArray((QByteArray *)v29); LOBYTE(v33) = 8; QFile::~QFile((QFile *)v7); LOBYTE(v33) = 7; QByteArray::~QByteArray((QByteArray *)v28); LOBYTE(v33) = 4; QByteArray::~QByteArray((QByteArray *)v27); v33 = -1; QString::~QString((QString *)v26); return v32; } QFileDevice::close((QFileDevice *)v7); LOBYTE(v33) = 9; QByteArray::~QByteArray((QByteArray *)v29); } v31 = 0; LOBYTE(v33) = 8; QFile::~QFile((QFile *)v7); LOBYTE(v33) = 7; QByteArray::~QByteArray((QByteArray *)v28); LOBYTE(v33) = 4; QByteArray::~QByteArray((QByteArray *)v27); v33 = -1; QString::~QString((QString *)v26); return v31; } ``` 因此,有了如下注册机算法(比较简单,就不再额外分析了): ```c++ #include <QCoreApplication> #include <QCryptographicHash> #include <QFile> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QString str = "{此处为启动界面所显示的ID}"; str.append("123"); auto array = str.toLocal8Bit().toBase64(); auto result = QCryptographicHash::hash(array,QCryptographicHash::Md5); QFile file(QCoreApplication::applicationDirPath()+"/lic"); if(!file.open(QIODevice::ReadWrite)) { qDebug()<<"open file error"; } file.write(result); file.close(); return a.exec(); } ``` # 思考 目前工业领域的软件保护机制过于简单,核心验证算法和敏感字符一览无遗。 根据软件保护综合效益,可考虑结合如下多种方式强化软件保护: 1. 对敏感字符采用转码、加密、拼接等方式生成。 2. 对核心验证算法,采用分散验证方式,A函数验证一部分,B函数验证一部分,C函数验证一部分... 3. 采用多次、不同验证算法、不同时机进行验证,部分验证函数可无需信息提示,内部埋暗桩。 4. 采用插花、代码膨胀或者VM等方式对核心验证算法进行处理防静态分析。 5. 采用反调试暗桩防动态分析。 6. 对关键代码和数据进行定时crc校验。 7. 采用加密狗、网络验证等多种方式进行保护。 8. 采用强外壳加壳工具对可执行文件进行保护。 9. 发行release版本程序。 最后修改:2021 年 08 月 27 日 09 : 47 PM © 禁止转载