利用AOB通过特征码动态劫持寄存器指向的地址 - Terraria背包监控实现
原文章来自https://www.cnblogs.com/LynCandy/p/18825333
技术背景
动态寄存器劫持原理
寄存器级内存寻址的动态拦截技术,通过特征码定位关键指令流,建立符号化内存锚点(Symbol Anchor),实现寄存器指向地址的运行时重定向。相较于传统基址+偏移量模式,具有以下核心优势:
-
动态内存适应
通过aobscan
特征签名匹配,规避ASLR和动态内存分配带来的基址失效问题 -
寄存器上下文捕获
在指令执行现场直接劫持寄存器值,避免多层指针追朔的地址稳定性风险 -
热更新支持
通过registersymbol
声明动态符号,支持运行时内存布局变更的热适应 -
跨版本兼容
特征码扫描机制可兼容不同编译版本,减少游戏更新后的维护成本
实现步骤
1. 指令流定位
; Terraria物品数量更新指令原型
0045F2D1 - 01 81 B4000000 - add [ecx+000000B4],eax
通过Cheat Engine的Find out what writes to this address
功能定位物品数量更新点,使用AOB签名:
01 81 B4 00 00 00 80 7D 15
2. 内存注入框架
aobscan(INJECT, 01 81 B4 00 00 00 80 7D 15) // 特征码模糊匹配
alloc(newmem, $1000) // 分配代码洞穴
alloc(vinv0item, 8) // 声明物品数量指针容器
registersymbol(vinv0item) // 注册动态符号
3. 寄存器劫持逻辑
newmem:
add [ecx+000000B4],eax ; 原始指令恢复
mov [vinv0item], ecx ; 捕获ECX寄存器值
add [vinv0item], B4 ; 计算实际物品数量地址
jmp return
code:
add [ecx+000000B4],eax ; 备用执行路径
jmp return
INJECT:
jmp newmem // 劫持执行流
nop // 指令对齐
实时监控实现
内存指针维护
registersymbol(vinv0item) // 声明为全局符号
通过寄存器符号动态绑定,实现:
- CE脚本内
vinv0item
变量的地址热更新 - 外部调试器可通过
vinv0item
符号直接访问 - 多线程环境下的地址一致性保证
数据流验证
- 执行物品数量变动操作
- 观察
vinv0item
指针值变化(CE内存查看器) - 验证
[vinv0item]
指向地址的数值实时性
优势对比分析
维度 | 传统基址模式 | AOB寄存器劫持 |
---|---|---|
内存稳定性 | 依赖固定偏移链 | 动态寄存器上下文捕获 |
更新成本 | 需重新追朔指针链 | 特征码自动适配 |
执行效率 | 多层指针解引用 | 寄存器直接操作 |
多版本支持 | 需适配每个版本 | 特征匹配自动定位 |
注入稳定性 | 易受内存布局变化影响 | 指令流级精准拦截 |
注意事项
-
寄存器生命周期
确保在ECX
有效期内完成劫持操作,避免使用易失性寄存器 -
指令对齐
使用nop
填充被覆盖的原始指令空间,防止执行流错位 -
符号管理
通过registersymbol/unregistersymbol
规范符号生命周期 -
内存释放
dealloc
需与alloc
严格对应,防止内存泄漏
本方案已在Terraria 1.4.4.9实测通过,可稳定捕获物品数量变更事件
[ENABLE]
aobscan(INJECT,01 81 B4 00 00 00 80 7D 15)
alloc(newmem,$1000)
alloc(vinv0item, 8)
registersymbol(vinv0item)
label(code)
label(return)
newmem:
add [ecx+000000B4],eax
mov [vinv0item], ecx
add [vinv0item], B4
jmp return
code:
add [ecx+000000B4],eax
jmp return
INJECT:
jmp newmem
nop
return:
registersymbol(INJECT)
[DISABLE]
INJECT:
db 01 81 B4 00 00 00
dealloc(vinv0item)
unregistersymbol(vinv0item)
unregistersymbol(INJECT)
dealloc(newmem)
{
// ORIGINAL CODE - INJECTION POINT: Terraria.Player::GetItem_FillIntoOccupiedSlot+107
Terraria.Player::GetItem_FillIntoOccupiedSlot+DB: 8B 96 D8 00 00 00 - mov edx,[esi+000000D8]
Terraria.Player::GetItem_FillIntoOccupiedSlot+E1: 3B 5A 04 - cmp ebx,[edx+04]
Terraria.Player::GetItem_FillIntoOccupiedSlot+E4: 0F 83 52 01 00 00 - jae Terraria.Player::GetItem_FillIntoOccupiedSlot+23C
Terraria.Player::GetItem_FillIntoOccupiedSlot+EA: 8B 4C 9A 08 - mov ecx,[edx+ebx*4+08]
Terraria.Player::GetItem_FillIntoOccupiedSlot+EE: 8B B9 B4 00 00 00 - mov edi,[ecx+000000B4]
Terraria.Player::GetItem_FillIntoOccupiedSlot+F4: 03 C7 - add eax,edi
Terraria.Player::GetItem_FillIntoOccupiedSlot+F6: 8B 91 B8 00 00 00 - mov edx,[ecx+000000B8]
Terraria.Player::GetItem_FillIntoOccupiedSlot+FC: 3B C2 - cmp eax,edx
Terraria.Player::GetItem_FillIntoOccupiedSlot+FE: 0F 8F 7F 00 00 00 - jg Terraria.Player::GetItem_FillIntoOccupiedSlot+183
Terraria.Player::GetItem_FillIntoOccupiedSlot+104: 8B 45 EC - mov eax,[ebp-14]
// ---------- INJECTING HERE ----------
Terraria.Player::GetItem_FillIntoOccupiedSlot+107: 01 81 B4 00 00 00 - add [ecx+000000B4],eax
// ---------- DONE INJECTING ----------
Terraria.Player::GetItem_FillIntoOccupiedSlot+10D: 80 7D 15 00 - cmp byte ptr [ebp+15],00
Terraria.Player::GetItem_FillIntoOccupiedSlot+111: 75 1E - jne Terraria.Player::GetItem_FillIntoOccupiedSlot+131
Terraria.Player::GetItem_FillIntoOccupiedSlot+113: 8B 45 0C - mov eax,[ebp+0C]
Terraria.Player::GetItem_FillIntoOccupiedSlot+116: 8B 80 B4 00 00 00 - mov eax,[eax+000000B4]
Terraria.Player::GetItem_FillIntoOccupiedSlot+11C: 89 45 EC - mov [ebp-14],eax
Terraria.Player::GetItem_FillIntoOccupiedSlot+11F: 50 - push eax
Terraria.Player::GetItem_FillIntoOccupiedSlot+120: 6A 00 - push 00
Terraria.Player::GetItem_FillIntoOccupiedSlot+122: 0F B6 45 14 - movzx eax,byte ptr [ebp+14]
Terraria.Player::GetItem_FillIntoOccupiedSlot+126: 50 - push eax
Terraria.Player::GetItem_FillIntoOccupiedSlot+127: 8B 55 18 - mov edx,[ebp+18]
}
转载自https://www.cnblogs.com/LynCandy/p/18825333