样本版本:v16.0.4(64位的)
CSDN真挺傻逼的!!!!!! 稍微有一点价值的文章都给你上付费,看不惯这种,所以自己动手搞搞,不浪费大家时间,成品直接滑到最后即可
他这个没有壳,字符串也没有被抹掉,难度较低,授权失败时弹的是 "Invalid name or license ...", 对应的 ASCII 字面量:
运行时交叉验证过:该串的实际地址 0x7FF6C55E3580 减掉 ASLR 基址 0x7FF6C4E20000 = 0x7C3580,和 IDA 里的 RVA 能够对上。
把 UI 构造、异常清理那些噪音去掉,关键骨架是这样的:
两个传入的常量 17 和 20300 一直跟着判定走:17 是当前主版本号(版本上限校验要用),20300 是个上限基准,但是我们只关心 0x9C 类型路径里不直接参与判定。
核心:授权是否通过完全由 n231 / n524 两个码决定,而这两个码是上面两个本地函数算出来的,没有走网络请求。(所以其实内存patch也行,但是我觉得不方便)
sub_14000B9B0 是 thunk,真身 sub_14043F090,它先调 sub_140008913(真身 sub_14043F0E0)做一次字符预处理,再把结果存到 mgr+48(QString),长度存 mgr+64。
很简单,逐字符替换:
判定函数一上来调它,把 license 字符串拆成一个字节数组(填进调用者栈上的 10 字节缓冲 v39..v48)。逻辑:
也就是格式是 AAAA-BBBB-CCCC-DDDD(19)或 AAAA-BBBB-CCCC-DDDD-EEEE(24),本质就是带连字符的十六进制串。
字符到半字节的映射 sub_14000B2DA → sub_14043E7D0:
还原时只用 0-9A-F 就行,标准十六进制。
sub_140003D64 是 thunk,真身 sub_14043D0E0。它干三件事:查母钥匙表 → 按类型字节分派算法 → 比对校验和。
函数开头先拿 name 去撞两张内置表:
注册机用不上它(写出来只是想说有时候商业软件感觉也就是个草台班子,想起之前看到的一篇关于医院小程序病人数据直接明文返回没有任何脱敏我就想笑)
撞表没中就进 switch (类型字节)。三个分支:0x9C(主流永久授权)、0xFC(另一种,会走 sub_14000B0FA 二次变换)、0xAC(带 mgr+132 计数)。注册机我选走最常见的 0x9C。0x9C 分支完整逻辑:
0x9C 要过校验需要同时满足:
b5 b6 b7 既是校验和的目标,又通过第 1、2 条参与 n2 和 v20 的计算——看着是循环依赖,其实不是,因为 b0 b1 b2 是三个完全自由的字节,可以反过来"吸收"掉 b5 b6 b7 的取值。
都短到可以直接抄进注册机:
因为 ad76 是双射、值域是完整的 0–255,所以版本上限想给多大给多大(我取了最大值 255,等于对所有版本永久有效)。
这是整套算法的核心,它吃 4 个参数:name 的 UTF-8 指针、一个 flag(0x9C 时为 1)、两个字节参数 a3(= n255)和 a4(= v21),返回 32 位值,取其 4 个字节小端去和 license 比。算法是基于一张 256 项 uint32 常量表 dword_140E1C0F0 的逐字符累加,带几个步进各异的游标:
这里有几个容易踩坑的实现细节,需要注意一下:
a3 在 0x9C 路径下恒为 0(因为 n2 >= 2),所以 g12 起点就是 0;a4 = v21 & 0xFF,只有 v21 的低字节进校验和。
整张码表里只有主判定返回 45 才映射到 219 "License activated",而能产出 45 的就只有 0x9C(版本够)和 0xAC(等级 >= 20300)两条路径。0xFC 给出的 147 落进 default,只会得到试用码。mgr+124 在正常激活流程里是 0,所以那条 275 旁路和 OK 处理函数里的联网分支都不触发。
| 项 |
地址 |
说明 |
字符串 aInvalidNameOrL |
0x1407C3580 |
.rdata,RVA 0x7C3580 |
| 唯一引用 |
0x1402B2DC7 |
lea rdx, aInvalidNameOrL,位于 sub_1402B24F0 |
name = a1[24] // 第一个 QLineEdit
license = a1[26] // 第二个 QLineEdit
// 一组输入合法性检查:空值 / 首尾空白 / 长度,只影响提示,不影响算法
sub_1400014AB(mgr, name) // 把 name 存进授权管理器单例 qword_140FFADB8
sub_14000B9B0(mgr, license) // 把 license 预处理后存进 mgr+48,长度存 mgr+64(见 §3)
n231 = sub_140003D64(mgr, 17, 20300) // 主判定,返回状态码
n524 = sub_1400088A5(mgr, 17, 20300) // 次判定(内部复用主判定),返回状态码
if (n231 != 231 && mgr+124 != 0) // 条件性联网(二级/激活),不参与主判定
sub_14000EA57(...) // 连 sweetscape.com
// 状态码分支(注意 n524 优先):
if (n524 == 219) -> "License activated ... Thank you for purchasing 010 Editor!" // 成功
else if (n524 == 237 || n524 == 524) -> "license is for an earlier version"
else if (n231 != 147) -> "Invalid name or license ..." // 失败,就是 §1 那句
else if (n524 != 113) -> "trial period is already over"
else -> "trial period has been extended"
// 成功后:
sub_140007789(mgr) // 把 license 写进注册表/文件
'O' (79) 或 'o' (111) -> '0' (48)
'l' (108) -> '1' (49)
[内核课程]《Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。