010 Editor 30 天试用验证逆向与 Patch 教程

010 Editor 30 天试用验证逆向与 Patch 教程

样本信息(本教程对应版本)

项目
架构 x64 PE
ImageBase 0x140000000
MD5 1545268aa88b62e83fb9129592a77667
SHA256 259a626281897ff12ce11185111a93402940a985a0c843d75fa7c9aeafc108db

0. 学习目标与整体思路

针对一些简单的商业软件试用验证逆向流程

现象观察 → 字符串定位 → 交叉引用找函数 → 伪代码读逻辑
    → 确认关键比较/返回值 → 设计 Patch 点 → 改字节 → 验证

通过简单的测试和观察,可以得出本程序的试用机制本质是:

  1. 记录安装/开始日期
  2. 每次启动读取系统当前日期
  3. 计算:过期日 = 开始日 + 30
  4. 当前日 >= 过期日 → 判定试用结束
  5. 另外还有系统时间被回调的检测

1. 准备工作

1.1 需要的工具

工具 用途
IDA Pro(带 Hex-Rays 更好) 反汇编 / 反编译 / 交叉引用
十六进制编辑器(任意,甚至 010 Editor 自己) 直接改 exe 字节
Windows 系统时间设置 黑盒验证试用逻辑

1.2 安全习惯

  1. 先备份

    复制 010Editor.exe → 010Editor.exe.bak
    
  2. 所有实验建议在**虚拟机 **中做。

1.3 在 IDA 中打开程序

  1. 打开 IDA Pro
  2. 选择 010Editor.exe
  3. 默认 PE 加载即可(x64)
  4. 等待自动分析结束(右下角进度条走完)
  5. 确认 ImageBase 约为 0x140000000

2. 黑盒观察:先确认「它真的看时间」

在动手看汇编前,先用最简单的方法验证「试用 = 时间相关」。

2.1 正常启动观察

启动 010Editor.exe,你会看到类似界面:

image-20260725140834681

  • 标题类似:010 Editor - Evaluation Version
  • 按钮:Purchase a license / Continue / Exit
  • 状态栏可能显示:Evaluation Version - N Days Left

这说明程序当前处于评估/试用模式,并且剩余天数是算出来的。

2.2 用系统时间「制造过期」

  1. 关闭 010 Editor
  2. 打开 Windows「日期和时间」
  3. 关闭「自动设置时间」
  4. 把系统日期向后拨 31 天以上

image-20260725141009745
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)

以「试用结束」字符串为例:

  1. 在 Strings 里双击 Your free 30-day trial period has now ended...
  2. 跳到数据地址 0x1407c3930
  3. X(Jump to xref)
  4. 会看到它被函数 sub_1402B3410 引用
  5. 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 中走通这条路径(练习清单)

请你亲自操作一遍,不要只看:

  1. Shift+F12 打开字符串
  2. 搜索 30-day trial
  3. 对字符串按 X 看引用
  4. 再搜索 tampered with
  5. 跳到 sub_14043C8D0
  6. 在反汇编窗口搜索立即数:
    • Alt + I(或 Search → Immediate value)
    • 0x1E(30)
    • 0x17(23)
    • 0x2A(42)
    • 0x138(312)
  7. 0x14043CA900x14043CB4D; 加注释,例如:
    • ; 30 days trial length
    • ; return 23 if not expired, else 42
  8. N 重命名:
    • sub_14043C2B0GetDayNumber
    • sub_14043C8D0CheckTrialPeriod
    • sub_14043DC50MapTrialStatus

做完这些,你就已经完成了一次完整的静态分析。


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 中直接改

  1. 跳到 0x14043CB4D

  2. 把光标放在 mov eax, 2Ah

  3. 菜单:

    Edit → Patch program → Change byte...
    

    或快捷方式:把光标移到字节 2A 上,改成 17

  4. 改完后反汇编应显示:

    mov eax, 17h
    
  5. 应用补丁到输入文件:

    Edit → Patch program → Apply patches to input file...
    
  6. 建议勾选备份,输出为新文件,例如:

    010Editor_patched.exe
    

10.4 方法 2:用十六进制编辑器改

  1. 备份原文件

  2. 用任意 Hex 编辑器打开 010Editor.exe

  3. 跳转到偏移 0x43BF4E

  4. 确认附近字节为:

    B8 2A 00 00 00 BA 17 00 00 00 8B 4F 68 39 4F 64 0F 42 C2
    
  5. 2A 改成 17

    B8 17 00 00 00 BA 17 00 00 00 8B 4F 68 39 4F 64 0F 42 C2
    
  6. 保存

10.5 验证

  1. 先把系统时间拨到“肯定过期”的未来日期
  2. 运行未 Patch 版本 → 应提示试用结束
  3. 运行 Patch 版本 → 应仍可 Continue
  4. 再把系统时间改回正常
  5. 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

练习思路:

  1. jz trial_expired 改成 jmp trial_ok
  2. 或把对 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. 结束语

每一个新手在学习逆向的初期都会遇到很多的迷惑的问题,这时候都会试图在网络上寻找一些教程,我初期刚学习逆向的时候也有这样的烦恼,要不就是写的一知半解,要不就是牛头不对马嘴,总是在一些对于新手可能产生疑问的地方一笔带过,所以我想写一些新手向的逆向教程,从实际出发,从实战开发一步一步拆解逆向一个程序的逻辑,当然对于各位大佬肯定是小儿科,如有不对或者有偏差的地方,敬请斧正。

posted @ 2026-07-25 14:50  桃风  阅读(1)  评论(0)    收藏  举报