首页
社区
课程
招聘
[原创]010 Editor 注册算法分析与注册机实现
发表于: 2026-9-27 18:05 7162

[原创]010 Editor 注册算法分析与注册机实现

2026-9-27 18:05
7162

贴主是逆向初学菜鸟,第一次逆向学习大型软件(虽然是入门级),写下这个帖子留作纪念

在 010 的注册界面中输入任意 Name/License,弹出 Invalid name or license...。字符串窗口找到该文案,xref 定位到"宣判块"(错误提示所在基本块)。

下图中异色(青)块即错误提示所在的基本块,可以看到它位于一长串分支逻辑的末尾——所有验证失败的路径最终都汇集到这一层:

图片描述

放大分派区,可以看到一组连续的结果码比较:cmp ebx, 237、cmp ebx, 524、cmp r15d, 147 等——不同的结果码被分流到不同的提示分支,一个"结果码 → 提示文案"的分派区浮出水面:

图片描述

继续向上追溯结果码的来源:图中红块是 cmp ebx, 219(激活成功判定),其上方是 call sub_1400088A5(参数为 mgr, 17, 20300,返回值存入 ebx),再往上还有 cmp dword ptr [rcx+124], 0 的判断——0x7C(124) 这个字段是联网审查开关,后文会详细讲:

图片描述

顺着分派区继续向上,找到了结果码的"生产车间"——下面这一整块 30 多行的顺序逻辑。看着密,其实拆成三段就很好读,我们一段一段来:

图片描述

第一段(取输入):块里唯一的外部库调用是 QLineEdit::text()——Qt 函数名是现成的路标,这是从注册窗口的输入框把 License 文本取出来,再经两个内部函数(sub_1400014AB、sub_14000B9B0)存进全局对象 qword_140FFADB8。这个对象后面会反复出现,我们叫它 mgr(管理器),可以把它想象成一个"档案袋",name、license、验证结果全装在里面。

第二段(双调用):接下来是肉眼可见的"复制粘贴"——mov edx, 17 / mov r8d, 20300 / call XXX 一模一样的配方出现两次,只是调用目标不同(先 sub_140003D64 后 sub_1400088A5),返回值一个进 r15d、一个进 ebx。两个吃同样参数的调用挨在一起,大概率是"一个负责验证、一个负责转述"的关系。

第三段(路由):块尾 cmp r15d, 231 / jz,加上下面小块里的 cmp dword ptr [rcx+124], 0 / jz——两道条件跳决定流程走向:满足任一就去本地宣判(红块 cmp ebx, 219 那里),都不满足则落入另一条分支(后文会讲,那是联网审查通道)。

三段读完,这块代码的全貌就出来了:取输入 → 双调用产出两个结果码 → 按码路由。

这两个结果码分工不同:r15d 是"路由器",ebx 才是"法官"。凭什么这么说?看它们各自的"出场位置":

一个管"去哪",一个管"判什么"。还有个佐证:就算 r15d == 231 走了本地宣判这条路,由于内层验证失败(231)经翻译官映射后 ebx=375≠219,法官照样判死——所以 r15d 真的只负责指路,生杀大权全在 ebx 手里。

再看这两个函数的身份:sub_140003D64 是个 thunk,真身 sub_14043D0E0 就是内层验证器(本文主角,第二章详析);sub_1400088A5 真身 sub_14043DC50,跟进去发现它内部又调了一遍内层验证,然后对返回值做 switch 映射——它是个"翻译官":

拿着翻译官的映射表,回到分派区逐个确认各结局的提示块:

① ebx==219(内层 45)→ 激活成功。 cmp ebx, 219 / jnz:不等则跳往失败分派,相等则直接落入下方的成功提示块——拼的字符串是 License activated. This license entitles...:

图片描述

② mgr+0x7C ≠ 0 → 联网审查。 cmp dword ptr [rcx+124], 0 / jz:开关为 0 回本地宣判;非 0 则落入联网分支——跟进发现函数 sub_1402B5140 在拼接 123K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6N6$3g2W2N6s2y4U0j5i4m8W2i4K6u0W2j5$3!0E0i4K6u0r3 开头的 URL 并发起 HTTP 请求,这就是联网反盗版校验(第六章详析)。校验失败直接弹反盗版警告,通过则回到 219 的判定:

图片描述

图片描述

③ ebx==237 / 524(内层 78)→ 版本太旧。 两个比较任一成立(jz)都跳到拼 The license you entered is for an earlier version... 的提示块:

图片描述

④ r15d==147 → 试用。 cmp r15d, 147 / jnz:不等于 147 跳向 Invalid 块——就是最开始定位的那个青色块,拼 Invalid name or license,激活码错误:

图片描述

等于 147 则落入试用路径,再由 cmp ebx, 71h(113)细分两种结局:

图片描述

图片描述

图片描述

把 switch 的分支和分派区的提示块一一对应,得到完整的码表:

内层验证器的参数是 (mgr, 17, 20300)。读它之前,先搞清楚 mgr 这个"档案袋"里装了什么。

方法:x64dbg 连拍四张快照做对比——断在验证前、弹 Invalid 后、改 license 后、改 name 后,逐格对比 mgr 内存:

(快照③关键行原貌:F0 1C 72 55 E8 01 00 00 00 1D 72 55 E8 01 00 00(license 的 d/ptr)下一行 14 00 00 00——即 +0x40 = 0x14 = 20,正是删了一段后的字符数。)

两个规律自己跳出来:+0x28 只在改 name 时变(4→5),+0x40 只在改 license 时变(24→20)——控制变量,谁变跟谁走。而指针列的"变与不变"正好对应 QString 重新分配(改谁的内容谁才重建缓冲区)。由此实锤:+0x28 是 name 长度、+0x40 是 license 长度,两个 QString 都是 {d, ptr, size} 三连布局(d 与 ptr 恒差 0x10,即 16 字节管理头)。

知道了 +40/+64 是两个 size 字段,这段就一目了然:空串检查(return 147)。

sub_14000E214(a1, &v39) 把 license 字符串解码成字节数组(v39 起,记 b0~b9)。三个动作:

① 转单字节:先 QString::toLatin1 把 license 从 UTF-16 压成单字节(x64 动态跟进 ptr 可看到 31 00 31 00 → 31 31 的压缩过程)。

② 格式闸机:长度必须 19(四段)或 24(五段),且第 4/9/14(/19) 位必须是 -(45 = 0x2D),反编译原文:

③ 逐字符解码:每两行为一组——取两个字符 → B2DA 转数值 → 16 * 高 + 低 拼一个字节,注意下标跳过横杠:

其中 sub_14000B2DA(真身 43E7D0)是"字符 → 数值"转换表,从上到下挨个分类:

注意它收的是整个字母表(不只是 A-F),但只有 0-9A-F(值 0~15)才能在 16x+y 里不溢出——生成码时用标准 hex 字符即可。

动态验证:输 AAAA-BBCC-DDEE-FF00,断在解码后看 &v39:AA AA BB CC DD EE FF 00 一字不差——v39 到 v46 正好就是 b0 到 b7,实锤。

第一道:用户名撞表。 拿 name 挨个 QString::operator== 比对内置表 off_140E1C5C0,表内容就两条:999、AnyOne——都是网上流传泄露 key 的用户名,撞中 return 231。

图片描述

第二道:前缀 + 指纹组合。 name 长度 >1 时,按 12 字节表项循环(v6 += 12):

即"名字前缀 + 泄露 license 指纹"的组合黑名单,指纹倒序存放(v13 递减),dump 里直接看不出是哪条 key。第一个表项前缀是 OW。

动态验证:mgr+0x18 的 name QString 与 mgr+0x30 的 license QString 背靠背,跟进 ptr 能看到输入的明文。

b3(v42)是类型字节(-100/-4/-84 转 hex 即 0x9C/0xFC/0xAC),驱动稀疏 switch:

0x9C 主分支(注册机目标,fall-through 路径):

版本约束推演(决定 a3 能不能焊死为 0):版本=0 → 231 闸死;版本∈[1,16] → 后面 17 > 版本 给 78(旧版本)也活不成;只有版本 ≥ 17 能走到最后——而 ≥17 必然 ≥2,所以 a3 恒为 0。


传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!

最后于 2026-10-6 17:32 被0xL编辑 ,原因:
收藏
点赞 ・20
打赏
分享
最新回复 (12)
雪    币: 801
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2 楼
楼主,请问下,是 010EditorWin64Installer16.0.4  这个版本么?
2026-9-28 22:21
2
雪    币: 508
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
0xL
3 楼
是的
2026-9-28 22:40
0
雪    币: 508
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
0xL
4 楼
mb_yscnhzom 楼主,请问下,是 010EditorWin64Installer16.0.4 这个版本么?
是的
2026-9-28 22:40
0
雪    币: 369
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5 楼
强大,未入门菜鸡前来学习
2026-9-29 01:46
0
雪    币: 7355
活跃值: (13439)
能力值: (RANK:385 )
在线值:
发帖
回帖
粉丝
6 楼
imhex 开源替代.看看16进制够用.也推荐一波.
2026-9-29 15:50
0
雪    币: 980
活跃值: (1000)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
7 楼
哪一期的?
2026-10-4 15:15
0
雪    币: 508
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
0xL
8 楼
刘宝 哪一期的?
54
2026-10-5 17:19
0
雪    币: 578
活跃值: (943)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9 楼
谢谢,学习了
2026-10-6 10:43
0
雪    币: 7178
活跃值: (5770)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10 楼
来论坛就是为了看大佬展示实力的
2026-10-8 16:42
0
雪    币: 1371
活跃值: (1775)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11 楼

大佬666.只是编译会报错.没有包含声明 system() 函数的头文件。加上 #include <stdlib.h> 就能正常编译了.已经测试能正常注册成功.

最后于 2026-10-10 10:37 被YoungBai编辑 ,原因:
2026-10-10 10:16
0
雪    币: 508
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
0xL
12 楼
YoungBai 大佬666.只是编译会报错.没有包含声明&nbsp;system()&nbsp;函数的头文件。加上&nbsp;#include&nbsp;&lt;stdlib. ...
c语言写的然后编译  你是用c++编译的吧
2026-10-10 15:09
0
雪    币: 1371
活跃值: (1775)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13 楼
0xL c语言写的然后编译 你是用c++编译的吧
我用gcc编译的.
6天前
0
游客
登录 | 注册 方可回帖
返回