逆向实战:不联网也能破解?深扒某采集器工具的本地卡密生成逻辑
最近在研究一些小工具的授权机制时,碰到了一个挺有意思的样本。这就跟大家分享一下分析过程,纯本地校验且没有网络请求,这种“老式”的保护在现代破解眼光下显得颇为脆弱,但其中的算法逻辑还是有值得学习的地方。
目标概况
样本是一个名为 DeviceDataProcessImgui.exe 的程序,界面看起来是个“采集器数据处理”工具(v42版本)。如果不输入正确的卡密,你根本进不去主界面。
不输入正确的卡密无法进入主界面
经过初步侦查,发现导表里压根没有 WinHttp、InternetOpen 这类网络 API。这就很明确了:授权逻辑全在本地。这意味着只要我们弄懂了它的校验算法,就能自己造“钥匙”。
第一步:搞清楚“我是谁”(设备 ID)
既然是本地校验,程序肯定会绑定你的机器。静态分析后发现,它优先读取注册表里的 MachineGuid。
路径是:
HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid
读取到 GUID 后,它会拼接一个前缀 MG-。
卡密 Payload 的拆解与重组逻辑示意图
例如:
My GUID = 69b65411-d82a-4952-81a5-63f2527a7af3
Final DeviceID = MG-69b65411-d82a-4952-81a5-63f2527a7af3
如果读不到注册表,它还有个 B 计划,就是去读 C 盘的卷序列号,前缀变成 VOL-。不过大多数 Windows 环境下 MachineGuid 都是现成的。
第二步:拆解卡密结构
程序会让你输入一串卡密。分析处理函数发现,不管你输入 2508-4070-0950-9909-909 还是纯数字 2508407009509909909,它会先把所有非数字字符去掉。所以卡密本质上就是一串数字。
这串数字有严格规定:
- 长度必须是 19 位。
- 最后一位是 Luhn 算法校验位(跟银行卡校验原理一样),防手滑输错的。
- 前 18 位才是真正的数据载荷。
载荷的拆解方式非常有趣,它是按每 3 位一组进行切割的(A,B,C,...)。然后把每组的前两位和最后一位再分别重组:
- 所有组的前两位拼成一个 12 位的字符串。
- 所有组的第三位拼成一个 6 位的字符串。
12位字符串的进一步拆分:
- 前 8 位:核心校验值(check8)
- 后 4 位:有效天数
6位字符串:
- 直接作为 nonce(随机数),用于防止同样的天数生成完全一样的卡密。
第三步:核心算法 HMAC-SHA256
接下来就是重头戏了,那 8 位的 check8 是怎么来的?
在 IDA 里翻了一通,找到了加密用的 Key,居然是硬编码在程序里的(虽然被简单的异或混淆了一下,跑起来就能看到明文):
HMJFCIXEENJQFMFSNZWXJKYKRYZS_FEXHOPSRJJAQUGD_QXJ
校验消息的拼接格式为:
{DeviceID}|{days:04d}|{nonce:06d}
逻辑如下:
- 将上面拼好的字符串作为 Message。
- 使用那个超长的 Key 进行 HMAC-SHA256 运算。
- 取 Hash 结果的前 8 个字节。
- 将这 8 个字节转为大整数,然后对 100,000,000 取模(
% 1e8)。 - 得到的 8 位数字就是
check8。
第四步:写出 KeyGen 生成器
既然逻辑跑通了,我们就可以用 Python 复刻一遍。只要指定好天数和随机数,就能生成对应本机的正版卡密。
这里提供一个简易的生成逻辑思路:
import hmac
import hashlib
def generate_local_license(device_id, days, nonce):
# 1. 固定的 Key,从程序里逆向出的
key = b"HMJFCIXEENJQFMFSNZWXJKYKRYZS_FEXHOPSRJJAQUGD_QXJ"
# 2. 拼接消息
# 注意:days必须是4位数字,nonce必须是6位数字
message = f"{device_id}|{days:04d}|{nonce:06d}".encode('utf-8')
# 3. HMAC-SHA256 计算
digest = hmac.new(key, message, hashlib.sha256).digest()
# 4. 取前8字节转整数并取模,得到8位check8
check8_int = int.from_bytes(digest[:8], 'big') % 100000000
check8 = f"{check8_int:08d}"
# 5. 组装前18位数据
# 前8位是check8,后4位是days,最后6位是nonce
payload_18 = check8 + f"{days:04d}" + f"{nonce:06d}"
# 6. 还原成每3位一组的原始格式以便混淆(逆向工程中的反操作)
# 这一步需要将18位重新打散,然后加Luhn校验,这里就不展开具体还原函数了
# 实际操作就是把上面拆解逻辑反过来走一遍
return payload_18 # 这里只是返回核心载荷,实际需还原为19位并补Luhn码
实战成果
针对我的机器,生成的 9999 天超长卡密如下(仅供技术研究):
2508-4070-0950-9909-909
输入进去,直接验证通过,顺利进入主界面。
总结
这个 CrackMe 难度其实适中,它没有加壳,也没用复杂的虚拟机保护(VMProtect),核心就是标准 HMAC 算法。对于逆向新手来说,这是一个非常好的练手素材,涵盖了:
- 字符串处理与异或解密。
- 动态调试获取运行时解密数据。
- 标准加密库 的识别与应用。
- 算法逆向还原。
对于开发者而言,如果不想自己的软件被这样轻松破解,切记:核心校验逻辑不要完全放在本地,也尽量不要硬编码 Key。 即使无法联网,也应尝试将校验逻辑与关键业务逻辑深度耦合,增加还原算法的难度。
评论已关闭