10-运行时程序扩展工程
运行时程序扩展工程——从「分析」进入「修改与扩展」的工程化
一句话
「找到指令并调用它」不是一步,而会自然分叉出一整套运行时修改/扩展工程。 Shellcode、DLL、Hook、Trampoline、Signature、线程管理不是零散技巧,分别属于「代码载体、控制流、定位、运行时管理、维护」这些工程阶段——分析链回答「它怎么工作」,扩展链回答「我怎么改变或扩展它」。
一、从「分析」进入「执行扩展」
分析链(逆向):
1 | 游戏 → 数据 → 地址 → 状态 → 指令 → 函数 → 系统 → 机制 |
目标从「我想知道它怎么工作」变成「我想改变或扩展它」,就进入另一条链:
1 | 分析结果 → 确定修改点 → 确定执行方式 → 准备代码载体 → 建立执行入口 |
这称为运行时程序扩展工程(Runtime Modification / Extension)。
二、「调用指令」本身分三层
1 | A. 修改已有数据 Player.Health → Write → 新值(最低层) |
修改代码/调用功能都需要「自己的代码」放在哪里——于是出现代码载体(code storage)+ 控制流转移(control-flow redirection)。空白地址只是实现手段之一,不是核心概念。
三、代码载体:Shellcode vs DLL
新代码空间来自不同机制:
1 | 代码载体 |
- Shellcode:代码片段,尽量独立、直接执行——「我只需要在这里执行一小段逻辑」
- DLL:扩展模块,有数据/函数/状态/多个模块/完整工程——「我要给目标程序增加一个相对完整的软件模块」
从工程角度:Shellcode 更接近「代码片段」,DLL 更接近「扩展模块」。
四、真正困难的是运行时安全性
把逻辑注入多线程共享状态的游戏循环后:
1 | 游戏线程 → 原逻辑 → 注入的逻辑 → 原逻辑继续 |
产生大量工程问题:执行上下文、线程安全、同步、锁、阻塞、死锁、竞态、栈状态、寄存器状态、异常处理、生命周期、内存有效性、调用约定。
「能执行」只是第一层,「不破坏原程序」才是第二层。
线程处理应独立成一个阶段:
1 | 确定执行点 → 确定执行线程 → 判断执行频率 → 判断执行时间 → 判断是否允许阻塞 |
例如「每帧执行」和「后台线程执行」是完全不同的工程设计:
1 | 游戏线程 → 同步任务 / 快速任务 |
五、特征码定位 = 定位稳定性工程
它解决「程序版本变化后,原地址还能不能找到」:
1 | 固定地址 → 不稳定 → 寻找稳定特征 → Signature / Pattern → 重新定位 |
这是定位稳定性工程,不只是逆向技术。
六、整个体系:十大子系统
1 | 游戏程序分析与扩展工程 |
七、层次关系与工程方向
1 | 游戏世界 → 游戏机制 → 游戏系统 → 函数 → 指令 → 地址 → 内存 → 机器状态 |
分析手段反方向(从机器状态往上恢复),扩展手段再往下(让修改长期存在):
1 | 内存 → 地址定位 → 数据识别 → 状态识别 → 指令定位 → 函数恢复 |
最终与软件工程形成镜像:
- 软件工程:人类需求 → 游戏设计 → 系统设计 → 对象/数据 → 程序代码 → 指令 → 内存
- 逆向工程:内存 → 地址 → 数据 → 指令 → 函数 → 对象 → 系统 → 游戏机制
- 调试工程:横跨中间,找「预期状态 vs 实际状态」的差异
- 运行时修改/扩展:已知程序 → 确定修改目标 → 确定代码载体 → 建立控制流 → 处理执行上下文 → 处理线程/生命周期 → 运行 → 验证 → 版本适配
与其他线的关系
- 与逆向工程论(07-工程控制/07):07 讲逆向的分析侧(还原);本文讲逆向之后的分叉——修改与扩展的执行侧
- 与逆向信息保存方法(07-工程控制/09):09 讲如何保存找功能的思路与知识;本文讲找到之后如何把修改做对、做稳(能执行只是第一层,不破坏原程序才是第二层)
- 与拆解程序(12-游戏/13):拆解程序的修改六级(改数据/改状态/改行为/改控制流/扩展功能/调用能力)即本文「调用指令三层」的展开
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
