010 Editor 30 天试用验证逆向与 Patch 教程
010 Editor 30 天试用验证逆向与 Patch 教程
样本信息(本教程对应版本):
项目 值 架构 x64 PE ImageBase 0x140000000MD5 1545268aa88b62e83fb9129592a77667SHA256 259a626281897ff12ce11185111a93402940a985a0c843d75fa7c9aeafc108db
0. 学习目标与整体思路
针对一些简单的商业软件试用验证逆向流程:
现象观察 → 字符串定位 → 交叉引用找函数 → 伪代码读逻辑
→ 确认关键比较/返回值 → 设计 Patch 点 → 改字节 → 验证
通过简单的测试和观察,可以得出本程序的试用机制本质是:
- 记录安装/开始日期
- 每次启动读取系统当前日期
- 计算:
过期日 = 开始日 + 30 - 若
当前日 >= 过期日→ 判定试用结束 - 另外还有系统时间被回调的检测
1. 准备工作
1.1 需要的工具
| 工具 | 用途 |
|---|---|
| IDA Pro(带 Hex-Rays 更好) | 反汇编 / 反编译 / 交叉引用 |
| 十六进制编辑器(任意,甚至 010 Editor 自己) | 直接改 exe 字节 |
| Windows 系统时间设置 | 黑盒验证试用逻辑 |
1.2 安全习惯
-
先备份:
复制 010Editor.exe → 010Editor.exe.bak -
所有实验建议在**虚拟机 **中做。
1.3 在 IDA 中打开程序
- 打开 IDA Pro
- 选择
010Editor.exe - 默认 PE 加载即可(x64)
- 等待自动分析结束(右下角进度条走完)
- 确认 ImageBase 约为
0x140000000
2. 黑盒观察:先确认「它真的看时间」
在动手看汇编前,先用最简单的方法验证「试用 = 时间相关」。
2.1 正常启动观察
启动 010Editor.exe,你会看到类似界面:

- 标题类似:
010 Editor - Evaluation Version - 按钮:
Purchase a license/Continue/Exit - 状态栏可能显示:
Evaluation Version - N Days Left
这说明程序当前处于评估/试用模式,并且剩余天数是算出来的。
2.2 用系统时间「制造过期」
- 关闭 010 Editor
- 打开 Windows「日期和时间」
- 关闭「自动设置时间」
- 把系统日期向后拨 31 天以上

6. 再启动 010 Editor
预期现象:
-
弹窗文案变成:
-
![image-20260725141128442]()
这说明程序用的是系统当前日期做判断。
2.3 用系统时间「尝试绕过」
把系统日期拨回「安装日附近 / 试用期内」再启动。
可能出现两种结果:
| 结果 | 含义 |
|---|---|
| 又能 Continue,并显示剩余天数 | 时间判断生效,改时间成功绕过 |
| 提示 Clock Error / system clock has been set back / tampered | 程序额外做了回拨检测 |
本程序里两种逻辑都存在:
- 主逻辑:当前日期是否超过「开始日 + 30」
- 防护逻辑:若发现系统时间比「上次记录日期」还早 → 判定时钟被篡改
这里我们尝试把日期往回拨到正常的范围,发现还是显示上面的过期需要提交验证的弹窗,这就证明程序做了回拨检测。
3. 静态分析:从字符串找到关键函数
逆向新手最稳的入口不是乱翻函数列表,而是:
先搜用户能看到的报错字符串,再看谁引用了它。
3.1 打开 Strings 窗口
IDA 菜单:
View → Open subviews → Strings
或快捷键(常见):Shift + F12
3.2 搜索这些关键词
在 Strings 窗口中按 Ctrl + F,依次搜:
| 关键词 | 为什么搜它 |
|---|---|
30-day trial |
试用结束提示 |
trial period |
试用相关 |
Days Left |
剩余天数 UI |
clock has been |
时钟篡改提示 |
Bad Clock Date |
时钟错误标题 |
Evaluation Version |
评估版状态文案 |
3.3 本样本中的关键字符串(已验证)
| 地址 | 字符串 | 作用 |
|---|---|---|
0x1407c3930 |
Your free 30-day trial period has now ended... |
试用结束主提示 |
0x1407c3a20 |
同上(另一份措辞) | 备用文案 |
0x1407e9030 |
30 Day Trial is now active... |
试用激活提示 |
0x1407c3e90 |
Days Left |
剩余天数显示 |
0x140800ab0 |
The system clock has been tampered with... |
时钟被篡改 |
0x1407c9150 |
The system clock has been set back... |
时钟被回拨 |
0x1407c3c58 |
Bad Clock Date |
时钟错误标题 |
3.4 对字符串做交叉引用(Xrefs)
以「试用结束」字符串为例:
- 在 Strings 里双击
Your free 30-day trial period has now ended... - 跳到数据地址
0x1407c3930 - 按
X(Jump to xref) - 会看到它被函数
sub_1402B3410引用 ![image-20260725141653284]()
同理:
| 字符串 | 引用函数 | 说明 |
|---|---|---|
| 试用结束文案 | sub_1402B3410 |
负责弹过期对话框/UI |
| 时钟篡改文案 | sub_14043C8D0 |
真正做时间判断的核心函数 |
Days Left |
sub_14043DFF0 等 |
显示剩余天数 |
新手口诀:
弹窗字符串找 UI 函数;判定逻辑找“计算日期/比较日期”的函数。
4. 核心函数全景图
本程序试用判断可以概括成 3 层:
┌─────────────────────┐
│ sub_14043C2B0 │
│ 取“今天是第几天” │
└─────────┬───────────┘
│
v
┌──────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ 启动/许可逻辑 │───▶│ sub_14043C8D0 │───▶│ 返回状态码 │
│ │ │ 30天试用核心判断 │ │ 23 / 42 / 312 │
└──────────────┘ └─────────────────────┘ └──────────┬──────────┘
│
v
┌─────────────────────┐
│ sub_14043DC50 │
│ 把状态码映射到业务 │
│ (试用中/已过期等)│
└─────────────────────┘
│
v
┌─────────────────────┐
│ UI 层 │
│ Days Left / Expired│
│ Continue / Purchase│
└─────────────────────┘
5. 第一步:看“今天”是怎么取的
5.1 跳转到取日期函数
在 IDA 中按 G,输入:
0x14043C2B0
再按 F5 看伪代码(Hex-Rays)。
5.2 伪代码(已简化理解)
__time64_t GetDayNumber()
{
__time64_t t = _time64(0); // 取当前 Unix 时间戳(秒)
struct tm *lt = _localtime64(&t); // 转成本地时间
return _mktime64(lt) / 86400; // 秒 → “从纪元起第几天”
}
5.3 新手必懂
86400 = 24 * 60 * 60,正好是一天的秒数- 所以程序内部用的不是
2026-07-25这种日历字符串,而是一个整数天数 - 例如:
- 安装日 =
20000 - 过期日 =
20000 + 30 = 20030 - 今天 =
20025→ 还剩 5 天 - 今天 =
20031→ 已过期
- 安装日 =
这也解释了为什么改系统时间会直接影响结果:_time64(0) 读的就是系统时钟。
6. 第二步:读懂 30 天核心判断函数
6.1 跳转到核心函数
G → 0x14043C8D0
F5 反编译
函数名在 IDA 里可能是:sub_14043C8D0
(你可以按 N 把它重命名为 CheckTrialPeriod,方便阅读)
6.2 结构体字段(非常关键)
函数第一个参数 a1 / 汇编里的 rdi 指向一个许可状态对象。
与试用相关的字段如下:
| 伪代码 | 汇编偏移 | 含义 |
|---|---|---|
a1[24] |
[rdi+0x60] |
试用开始日(安装/首次激活) |
a1[25] |
[rdi+0x64] |
上次记录日 / 当前判定用日期 |
a1[26] |
[rdi+0x68] |
试用结束日 |
6.3 最关键的 3 段逻辑
A. 计算过期日 = 开始日 + 30
伪代码:
if (expire < start + 30)
expire = start + 30;
对应汇编(请重点盯住):
; 地址: 0x14043CA90
add eax, 1Eh ; 1Eh = 30(十进制)
cmp ecx, eax
cmovb ecx, eax ; 若 expire < start+30,则 expire = start+30
mov [rdi+68h], ecx
1Eh = 30就是“30 天试用”的硬编码常量!
这是第一个经典 Patch 点:把 30 改成更大的数。
B. 防系统时间回拨
伪代码大意:
now = GetDayNumber(); // 系统今天
last = a1[25]; // 上次记录日
if (now < last)
return 312; // 时钟被回拨
如果日期数据明显不合理,还会弹出:
The system clock has been tampered with. Sorry, but this ends your trial period...
对应字符串地址:0x140800AB0
C. 最终是否过期
伪代码:
if (current_day < expire_day)
return 23; // 仍在试用期内
else
return 42; // 试用已过期
对应汇编:
; 地址: 0x14043CB4D
mov eax, 2Ah ; 默认返回 42(过期)
mov edx, 17h ; 备用值 23(未过期)
mov ecx, [rdi+68h] ; expire
cmp [rdi+64h], ecx ; current ? expire
cmovb eax, edx ; 若 current < expire,则 eax = 23
ret
6.4 返回值字典(务必背下来)
| 返回值 | 十六进制 | 含义 |
|---|---|---|
| 23 | 0x17 |
试用有效(Trial OK) |
| 42 | 0x2A |
试用过期(Expired) |
| 312 | 0x138 |
系统时钟被回拨/异常 |
7. 第三步:看上层如何使用返回值
7.1 调度函数
G → 0x14043DC50
F5
关键片段:
status = CheckTrialPeriod(...); // 实际经 thunk 调用 sub_14043C8D0
if (status == 23)
return 113; // 业务层:试用中
if (status == 42)
return 375; // 业务层:已过期(或相关路径)
if (status == 312)
return 47; // 业务层:时钟错误
对应汇编:
call sub_14000C8AB ; thunk → sub_14043C8D0
cmp eax, 17h ; == 23 ?
jz trial_ok
cmp eax, 2Ah ; == 42 ?
jz trial_expired
cmp eax, 138h ; == 312 ?
...
7.2 UI 显示剩余天数
函数 sub_14043DFF0 会做类似:
days_left = expire_day - current_day;
// 拼出 "N Days Left"
所以状态栏里的 Evaluation Version - 14 Days Left 不是“随便写的字符串”,而是真的在算。
8. 如何自己在 IDA 中走通这条路径(练习清单)
请你亲自操作一遍,不要只看:
Shift+F12打开字符串- 搜索
30-day trial - 对字符串按
X看引用 - 再搜索
tampered with - 跳到
sub_14043C8D0 - 在反汇编窗口搜索立即数:
- 按
Alt + I(或 Search → Immediate value) - 搜
0x1E(30) - 搜
0x17(23) - 搜
0x2A(42) - 搜
0x138(312)
- 按
- 在
0x14043CA90、0x14043CB4D按;加注释,例如:; 30 days trial length; return 23 if not expired, else 42
- 按
N重命名:sub_14043C2B0→GetDayNumbersub_14043C8D0→CheckTrialPeriodsub_14043DC50→MapTrialStatus
做完这些,你就已经完成了一次完整的静态分析。
9. Patch 思路总览
Patch 的本质只有一句话:
让程序在“该判过期”的时候,仍然走到“未过期”的分支。
常见 3 种改法(从易到难理解):
| 方案 | 改什么 | 优点 | 缺点 |
|---|---|---|---|
| 方案 A(推荐入门) | 最终返回值永远变成 23 | 只改 1 个字节,稳 | 不改“30天”语义,只改结果 |
| 方案 B | 把 +30 改成很大的天数 |
直观理解“天数常量” | 仍依赖日期字段正确 |
| 方案 C | 在调度函数把过期分支改成试用分支 | 上层逻辑清晰 | 可能有多处判断 |
下面重点讲 方案 A 和 方案 B。
10. 方案 A:一字节 Patch(最推荐新手)
10.1 原理
最终代码原本是:
mov eax, 42 ; 先假设过期
...
cmovb eax, 23 ; 只有 current < expire 才改成未过期
我们把它改成:
mov eax, 23 ; 先假设未过期
...
cmovb eax, 23 ; 无论比较结果如何,最终都是 23
于是函数永远返回“试用有效”。
10.2 要改的位置
| 项目 | 值 |
|---|---|
| 虚拟地址 VA | 0x14043CB4D |
| 文件偏移 File Offset | 0x43BF4D |
| 原始机器码 | B8 2A 00 00 00 |
| 含义 | mov eax, 0x2A(42) |
| 修改后机器码 | B8 17 00 00 00 |
| 含义 | mov eax, 0x17(23) |
实际只需要改 1 个字节:
文件偏移 0x43BF4E 处的 2A → 17
10.3 方法 1:在 IDA 中直接改
-
跳到
0x14043CB4D -
把光标放在
mov eax, 2Ah -
菜单:
Edit → Patch program → Change byte...或快捷方式:把光标移到字节
2A上,改成17 -
改完后反汇编应显示:
mov eax, 17h -
应用补丁到输入文件:
Edit → Patch program → Apply patches to input file... -
建议勾选备份,输出为新文件,例如:
010Editor_patched.exe
10.4 方法 2:用十六进制编辑器改
-
备份原文件
-
用任意 Hex 编辑器打开
010Editor.exe -
跳转到偏移
0x43BF4E -
确认附近字节为:
B8 2A 00 00 00 BA 17 00 00 00 8B 4F 68 39 4F 64 0F 42 C2 -
把
2A改成17:B8 17 00 00 00 BA 17 00 00 00 8B 4F 68 39 4F 64 0F 42 C2 -
保存
10.5 验证
- 先把系统时间拨到“肯定过期”的未来日期
- 运行未 Patch 版本 → 应提示试用结束
- 运行 Patch 版本 → 应仍可 Continue
- 再把系统时间改回正常
![image-20260725142749358]()
如果 Patch 后仍过期,检查:
- 是否改错文件偏移
- 是否改的是副本而不是正在运行的文件
- 杀毒/系统是否拦截了修改后的 exe
- 是否有多个启动路径(少见)
11. 方案 B:把 30 天改成更长(理解常量)
11.1 原理
把:
add eax, 1Eh ; +30 天
改成例如:
add eax, 7Fh ; +127 天
或:
add eax, 0FFh ; +255 天
11.2 位置
| 项目 | 值 |
|---|---|
| VA | 0x14043CA90 |
| 文件偏移 | 0x43BE90 |
| 原始字节 | 83 C0 1E |
| 含义 | add eax, 0x1E |
11.3 示例修改
把 1E 改成 FF:
原始: 83 C0 1E
修改: 83 C0 FF
11.4 注意
- 这只延长试用,不等于“永久”
- 程序还可能把过期信息写回配置文件
- 若你已经触发过“时钟篡改导致立即结束”,仅改 30 不一定够,方案 A 更干净
12. 方案 C:在上层调度处改分支(扩展练习)
地址:0x14043DC9D 附近
cmp eax, 17h
jz trial_ok
cmp eax, 2Ah
jz trial_expired
练习思路:
- 把
jz trial_expired改成jmp trial_ok - 或把对
2Ah的比较直接 NOP 掉
这能帮助你理解:
- 底层返回码
- 上层业务状态
- UI 文案
是分层的,不是“一个 if 搞定所有事”。
13. 字节级速查表(本样本)
13.1 一字节永不过期(方案 A)
VA: 0x14043CB4E
File Offset: 0x43BF4E
Before: 2A
After: 17
Instruction: mov eax, 2Ah → mov eax, 17h
完整上下文:
偏移 0x43BF4D:
原始: B8 2A 00 00 00 BA 17 00 00 00 8B 4F 68 39 4F 64 0F 42 C2
修改: B8 17 00 00 00 BA 17 00 00 00 8B 4F 68 39 4F 64 0F 42 C2
13.2 修改试用天数(方案 B)
VA: 0x14043CA92
File Offset: 0x43BE92
Before: 1E
After: FF ; 示例:30 天 → 255 天
Instruction: add eax, 1Eh → add eax, 0FFh
13.3 地址换算公式(以后换样本也能算)
对本 PE:
.text 节:
VirtualAddress = 0x1000
PointerToRawData = 0x400
FileOffset = VA - ImageBase - SectionVA + SectionRaw
= VA - 0x140000000 - 0x1000 + 0x400
= VA - 0x140000000 - 0xC00
例:
VA 0x14043CB4D
RVA = 0x43CB4D
FileOffset = 0x43CB4D - 0x1000 + 0x400 = 0x43BF4D
14. 附录:关键地址一览(本样本)
| 地址 | 名称建议 | 作用 |
|---|---|---|
0x14043C2B0 |
GetDayNumber |
_time64/86400 取当前天数 |
0x14043C8D0 |
CheckTrialPeriod |
30 天试用核心判断 |
0x14000C8AB |
CheckTrialPeriod_thunk |
跳转到核心判断 |
0x14043DC50 |
MapTrialStatus |
把 23/42/312 映射成业务状态 |
0x14043DFF0 |
FormatEvalStatusText |
生成 Days Left / Expired 文案 |
0x1402B3410 |
ShowTrialDialog |
试用结束等对话框相关 |
0x14043CA90 |
- | add eax, 1Eh(+30天) |
0x14043CB4D |
- | 最终返回 42/23 的分支 |
15. 结束语
每一个新手在学习逆向的初期都会遇到很多的迷惑的问题,这时候都会试图在网络上寻找一些教程,我初期刚学习逆向的时候也有这样的烦恼,要不就是写的一知半解,要不就是牛头不对马嘴,总是在一些对于新手可能产生疑问的地方一笔带过,所以我想写一些新手向的逆向教程,从实际出发,从实战开发一步一步拆解逆向一个程序的逻辑,当然对于各位大佬肯定是小儿科,如有不对或者有偏差的地方,敬请斧正。



浙公网安备 33010602011771号