AIDA64 8.25.8200 Business 逆向工程与Keygen实战
0. 引言
作为一个大二大学牲,最近给PC升级了散热,就想监控一下温度,但是在网上翻来覆去扒了好多论坛都没没有找到AIDA64今年2月24日最新版本Business的Keygen,只有一些固定分享的Key容易被AIDA64标记,那么能不能做到使用Keygen实现Business自由呢?下载了几个老版本,进去还有Illegal Copy牛皮癣警告膈应人。那么新版本AIDA64对安全性做了什么升级呢?
1. 初步分析
- 解包:AIDA64一如既往的使用标准UPX打包,直接使用
upx -d aida64.exe即可将其解包为正常exe,接着放入IDA进行分析。
- 寻找序列号校验代码:我使用的是AIDA64 Portable版本,可以看到程序相同目录有一个
Language目录,找到ulang_cn.txt并搜索序列号,这里会发现序列号的本地化对应为Product Key。
- 在IDA中搜索
Product Key出现的全部位置。搜索到了这些字符串:
其中我们一眼看到The entered product key is invalid,这样我们就定位到了序列号校验窗口的代码,可以反编译并研究检查逻辑了。
2. 校验位分析
首先,按F5进行反编译,往下稍微翻一点可以看到这样的逻辑:
可以看到,如果vars113是1,就说明序列号是无效的。而往上一看,(__int64)&vars113作为sub_108B440的out参数,说明这个函数进行了关键的校验逻辑,第一个参数应该就是序列号了。点开这个函数进行分析:
首先执行了v112 = a1,接着后面的一大串处理函数将字符串分别去除-、_和空格、0x00AD、0x00C2这几种符号,仅保留25字节,然后进一步规范化为全大写,最终到这个位置:
sub_4166D0(&varsB0, vars120, 1, 24); 把序列号的前24字节提取了出来,紧接着的v17 = sub_108B370(_0, varsB0);计算了一个变种CRC16,具体逻辑详见keygen(可以靠AI逆一下)。比较重要的是,sub_108B0B0(_0, &varsB8, v17 % 0x9987u)为什么是0x9987这个数字?是因为其将CRC结果变成了34进制的3位数来处理,0x9987-1是3位34进制数能表达的最大数,而字母表是一个常量DY14UF3RHWCXLQB6IKJT9N5AGS2PM8VZ7E:
后面的
sub_4166D0(&varsC0, varsB8, 2, 1);
sub_4166D0(&varsA8, vars120, 25, 1);
v18 = sub_416680(varsC0, varsA8);
*a8 = v18 == 0;
则是将这3字符中的第2个字符作为校验字符提取了出来,与序列号的最后一位对比。
3. 序列号如何保存信息
在校验位后面,程序对输入的序列号搞了一大堆blacklist校验,我们先不看这一段(反正也不会用公开key了)
我们主要看这一段:
sub_108B270主要还是用刚才那个字母表把序列号以34进制转换成整数,其逻辑为:
result = Σ digit[i] * 34^(len-1-i)
而sub_4166D0(&vars70, vars120, 1, 2); 的后两个参数则是从第几位取几位数字,例如第一行是从23位取2个数字(也就是最后俩数),因为后面的时间运算需要seed,所以这里要先取出来。
具体定义表:
| Key 位置 |
结果 |
| 1 - 2 |
Product ID (也就是判断Business/Expert等) |
| 3 - 4, 5 - 6, 7- 8 |
不知道是什么,但是影响不大,通过校验即可 |
| 9 - 12 |
序列号 |
| 13 - 16 |
基准日期 |
| 17 - 19 |
到期偏移 |
| 20 - 22 |
版本支持偏移 |
| 23 - 24 |
种子 |
基准日期算法
先获取13 - 16 这一段
sub_4166D0(&vars48, vars120, 13, 4);
v31 = sub_108B270(_0, vars48);
vars138 = vars144 ^ v31 ^ 0x7CC1;
然后拆出来年月日:
year = ((vars138 >> 9) & 0x1F) + 2003;
month = (vars138 >> 5) & 0x0F;
day = vars138 & 0x1F;
到期偏移算法
IDA里看起来是这样:
sub_4166D0(&vars40, vars120, 17, 3);
v32 = sub_108B270(_0, vars40);
*a6 = (unsigned __int8)vars144 ^ v32 ^ 0x3FD;
可以比较明显的看出来,逻辑是:
expire_delta = (seed & 0xFF) ^ dec34(key[17..19]) ^ 0x3FD
版本支持偏移算法
可见:
sub_4166D0(&vars38, vars120, 20, 3);
v33 = sub_108B270(_0, vars38);
*a7 = (unsigned __int8)vars144 ^ v33 ^ 0x935;
也就是:
version_delta = (seed & 0xFF) ^ dec34(key[20..22]) ^ 0x935
那么另外那些字段有什么用呢?后面可以看到一段校验:
if (*v114 != 999
&& vars142 <= 0x64
&& vars146 <= 0x64
&& *v115 != 0xFFFF
&& *a6 < 3653
&& *a7 < 3653)
{
// 错误处理
}
所以我们构造的时候要让:
- 3..4 != 999
- 5..6 <= 100
- 7..8 <= 100
- 序列号 != 0xFFFF
- 到期偏移 < 3653
- 版本支持偏移 < 3653
4. 程序版本校验
核对过前面的序列号后,因为我现在用的是Business,所以程序最后的校验就像这样:
- 要求产品ID是1,也就是Business
- 要求
(unsigned __int8)sub_107FEC0((unsigned int)*v113, (unsigned int)*v114),看起来比较奇怪。前面已经写死了3..4是999,怎么这里还要做校验?其实大概率是为了给版本划区分,如图:
当产品ID是4的时候(不知道4是什么版本),要求3..4不能小于320,然后给了很大一坨候选,比较闲的人可以魔改keygen后自己试一试都有什么区别(
-
要求sub_1080250(*v113, *v115),这个函数比较小:
bool __fastcall sub_1080250(int a1, int a2)
{
int v2; // ecx
v2 = a1 - 1;
if ( v2 && v2 != 3 )
return a2 > 0 && a2 < 777;
else
return a2 > 0 && a2 < 65534;
}
也就是说如果产品ID是1或4,必须 1 <= 序列号 <= 65534,其他ID则必须 1 <= 序列号 < 777
- 要求
sub_1080290(vars142) && sub_10802C0(vars146),这两个函数我就不贴了,其要求1 <= 5..6, 7..8 <= 15,所以这两个数字在1-15之间就可以。
后面还比对了一大堆Blacklist序列号,以及ROG水冷、ROG主板专用序列号的检测,包含CROSSHAIR / MAXIMUS / RAMPAGE / ZENITH的视为ROG主板,所以看到了吗,STRIX最终还是不配了
5. 实现一个Keygen
完整实现的Keygen已经上传至附件。经测试,在联网状态下使用Keygen生成的序列号不会触发盗版检测。
AIDA64_8.25.8200_Portable网盘链接: https://www.123865.com/s/EfqLVv-B9ZVh?pwd=52pj#
Keygen:
aida64_8.25.8200_keygen.zip
(3.49 KB, 下载次数: 1077)
尾声
感谢您能够阅读到这里。在AI时代,阅读并理解伪代码更多成为了一种兴趣,虽然各路Agent能够轻松秒杀这类较为简单的验证逻辑,但我仍希望Reverse初学者(包括作者自己)能够在静态分析中重拾代码分析的乐趣,也能使我们更好的理解Agent的工作原理、调试与优化,更得心应手地分析逆向工程难题。同时也希望各路大佬进行指点纠错!