利用AOB通过特征码动态劫持寄存器指向的地址 - Terraria背包物品监控实现 - Cheat Engine - AssemblyX
利用AOB通过特征码动态劫持寄存器指向的地址 - Terraria背包监控实现

技术背景
动态寄存器劫持原理
寄存器级内存寻址的动态拦截技术,通过特征码定位关键指令流,建立符号化内存锚点(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]
}
脚本解释:
aobscan(INJECT,01 81 B4 00 00 00 80 7D 15): 这行代码在目标进程内存中搜索与指定字节序列匹配的地址,并将第一个匹配结果的地址赋予符号INJECT。alloc(newmem,$1000): 这行代码在可执行内存中分配了 1000 字节的空间,并将其标记为newmem。我们将在这个区域存放我们自定义的汇编代码。alloc(vinv0item, 8): 这行代码分配了 8 字节的内存空间,并将其标记为vinv0item。我们将使用这个内存地址来存储我们想要监视的物品数量的地址。之所以分配 8 字节,是为了确保可以存储 64 位地址(在 64 位游戏中)。registersymbol(vinv0item): 这行代码将vinv0item注册为 CE 的符号,这样我们可以在 CE 的地址列表中看到它,并监视其值。label(code)和label(return): 这两行代码定义了代码标签,用于在汇编代码中进行跳转。newmem:: 这是我们自定义的新代码段的起始标签。add [ecx+000000B4],eax: 这条指令是原始被劫持的指令。我们首先执行它,以保证游戏的正常逻辑不受影响。mov [vinv0item], ecx: 这条指令将ECX寄存器当前的值(我们假设它指向与背包物品相关的某个数据结构)存储到vinv0item指向的内存地址。add [vinv0item], B4: 这条指令将vinv0item中存储的地址加上偏移B4。根据我们的假设,这个偏移指向物品数量在数据结构中的位置。执行完这条指令后,vinv0item中存储的地址将直接指向物品数量的值。jmp return: 这条指令跳转到return标签指向的地址,即原始被劫持指令的下一条指令,以恢复程序的正常执行流程。
code:: 这是原始代码段的标签。add [ecx+000000B4],eax: 这是原始被劫持的指令。jmp return: 跳转回原始流程。
INJECT:: 这是我们通过 AOB 扫描找到的目标指令的地址标签。jmp newmem: 这条指令将程序的执行流程从原始指令跳转到我们自定义的新代码段newmem。nop: 这条指令是一个空操作,它占据了被我们跳转的原始指令的第一个字节。这是为了防止程序因为执行了部分原始指令而崩溃。
return:: 这是原始被劫持指令的下一条指令的地址标签。[DISABLE]: 这个部分定义了当禁用该脚本时需要执行的操作。INJECT: db 01 81 B4 00 00 00: 这行代码将INJECT地址处的字节恢复为原始值,撤销我们的跳转。注意我们只恢复了被jmp newmem覆盖的字节。dealloc(vinv0item)和unregistersymbol(vinv0item): 这两行代码释放了我们分配的vinv0item内存空间,并取消了其符号注册。unregistersymbol(INJECT)和dealloc(newmem): 这两行代码释放了我们分配的newmem内存空间,并取消了INJECT符号的注册。
AssemblyX自动分析推测结果:
Terraria.Player::GetItem_FillIntoOccupiedSlot+85 - 50 - push eax ; 将 EAX 寄存器的值压入堆栈,可能是函数调用的参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+86 - 6A 01 - push 01 { 1 } ; 将立即数 1 压入堆栈,可能是音效的音量参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+88 - 68 0000803F - push 3F800000 { (0) } ; 将单精度浮点数 1.0 (3F800000) 压入堆栈,可能是音效的音调参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+8D - 6A 00 - push 00 { 0 } ; 将立即数 0 压入堆栈,可能是音效的类型参数或标志位
Terraria.Player::GetItem_FillIntoOccupiedSlot+8F - B9 26000000 - mov ecx,00000026 { 38 } ; 将立即数 38 (音效ID,可能是拾取物品通用音效) 移动到 ECX 寄存器,准备作为函数参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+94 - FF 15 58E55908 - call dword ptr [0859E558] { ->Terraria.Audio.SoundEngine::PlaySound } ; 调用 SoundEngine::PlaySound 函数,根据前面的 push 指令,推测是在播放拾取物品的音效
Terraria.Player::GetItem_FillIntoOccupiedSlot+9A - EB 33 - jmp Terraria.Player::GetItem_FillIntoOccupiedSlot+CF ; 无条件跳转到地址 Terraria.Player::GetItem_FillIntoOccupiedSlot+CF,跳过一段代码,可能是根据条件决定是否执行播放另一种音效
Terraria.Player::GetItem_FillIntoOccupiedSlot+9C - D9 46 28 - fld dword ptr [esi+28] ; 加载 ESI 寄存器指向地址偏移 28 字节处的单精度浮点数值到 FPU 寄存器,可能是物品的 X 坐标或相关位置信息
Terraria.Player::GetItem_FillIntoOccupiedSlot+9F - DD 5D E4 - fstp qword ptr [ebp-1C] ; 将 FPU 寄存器中的值以双精度浮点数形式存储到 [EBP-1C] 内存地址,并清空 FPU 寄存器,可能是保存物品 X 坐标到局部变量
Terraria.Player::GetItem_FillIntoOccupiedSlot+A2 - F2 0F10 45 E4 - movsd xmm0,[ebp-1C] ; 将 [EBP-1C] 处双精度浮点数值加载到 XMM0 寄存器,准备转换为整数
Terraria.Player::GetItem_FillIntoOccupiedSlot+A7 - F2 0F2C D0 - cvttsd2si edx,xmm0 ; 将 XMM0 寄存器中的双精度浮点数转换为有符号整数,结果存储到 EDX 寄存器,可能是将物品 X 坐标转换为整数像素坐标
Terraria.Player::GetItem_FillIntoOccupiedSlot+AB - D9 46 2C - fld dword ptr [esi+2C] ; 加载 ESI 寄存器指向地址偏移 2C 字节处的单精度浮点数值到 FPU 寄存器,可能是物品的 Y 坐标或相关位置信息
Terraria.Player::GetItem_FillIntoOccupiedSlot+AE - DD 5D E4 - fstp qword ptr [ebp-1C] ; 将 FPU 寄存器中的值以双精度浮点数形式存储到 [EBP-1C] 内存地址,并清空 FPU 寄存器,复用 [ebp-1C] 地址,这次是保存物品 Y 坐标
Terraria.Player::GetItem_FillIntoOccupiedSlot+B1 - F2 0F10 45 E4 - movsd xmm0,[ebp-1C] ; 将 [EBP-1C] 处双精度浮点数值加载到 XMM0 寄存器,准备转换为整数
Terraria.Player::GetItem_FillIntoOccupiedSlot+B6 - F2 0F2C C0 - cvttsd2si eax,xmm0 ; 将 XMM0 寄存器中的双精度浮点数转换为有符号整数,结果存储到 EAX 寄存器,可能是将物品 Y 坐标转换为整数像素坐标
Terraria.Player::GetItem_FillIntoOccupiedSlot+BA - 50 - push eax ; 将 EAX 寄存器的值 (Y 坐标) 压入堆栈,作为 PlaySound 函数的参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+BB - 6A 01 - push 01 { 1 } ; 将立即数 1 压入堆栈,音量参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+BD - 68 0000803F - push 3F800000 { (0) } ; 将单精度浮点数 1.0 压入堆栈,音调参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+C2 - 6A 00 - push 00 { 0 } ; 将立即数 0 压入堆栈,音效类型参数或标志位
Terraria.Player::GetItem_FillIntoOccupiedSlot+C4 - B9 07000000 - mov ecx,00000007 { 7 } ; 将立即数 7 (音效ID,可能是另一种拾取物品音效,例如金币) 移动到 ECX 寄存器
Terraria.Player::GetItem_FillIntoOccupiedSlot+C9 - FF 15 58E55908 - call dword ptr [0859E558] { ->Terraria.Audio.SoundEngine::PlaySound } ; 调用 SoundEngine::PlaySound 函数,播放另一种拾取物品音效
Terraria.Player::GetItem_FillIntoOccupiedSlot+CF - 8B 45 0C - mov eax,[ebp+0C] { 断点分析 - 拾取物品时断住 EAX=7ED19CF8 EBP=0233E3B8 } ; 将 [EBP+0C] 内存地址的值移动到 EAX 寄存器,根据断点分析,[EBP+0C] 可能是函数参数,指向拾取的物品对象
Terraria.Player::GetItem_FillIntoOccupiedSlot+D2 - 8B 80 B4000000 - mov eax,[eax+000000B4] ; 将 [EAX+B4] 内存地址的值移动到 EAX 寄存器,EAX 现在是指向物品对象的指针,+B4 偏移处可能是物品堆叠数量 (stack)
Terraria.Player::GetItem_FillIntoOccupiedSlot+D8 - 89 45 EC - mov [ebp-14],eax ; 将 EAX 寄存器的值 (物品堆叠数量) 存储到 [EBP-14] 内存地址,作为局部变量保存
Terraria.Player::GetItem_FillIntoOccupiedSlot+DB - 8B 96 D8000000 - mov edx,[esi+000000D8] ; 将 [ESI+D8] 内存地址的值移动到 EDX 寄存器,ESI 可能是 Player 对象指针,+D8 偏移处可能是物品栏数组的起始地址或相关结构
Terraria.Player::GetItem_FillIntoOccupiedSlot+E1 - 3B 5A 04 - cmp ebx,[edx+04] ; 比较 EBX 寄存器的值和 [EDX+04] 内存地址的值,EBX 可能是当前OccupiedSlot的索引,[EDX+04] 可能是物品栏数组的边界或大小
Terraria.Player::GetItem_FillIntoOccupiedSlot+E4 - 0F83 52010000 - jae Terraria.Player::GetItem_FillIntoOccupiedSlot+23C ; 如果 EBX >= [EDX+04] (无符号大于等于),则跳转到地址 Terraria.Player::GetItem_FillIntoOccupiedSlot+23C,可能是检查 OccupiedSlot 索引是否超出物品栏范围
Terraria.Player::GetItem_FillIntoOccupiedSlot+EA - 8B 4C 9A 08 - mov ecx,[edx+ebx*4+08] ; 将 [EDX+EBX*4+08] 内存地址的值移动到 ECX 寄存器,EDX 是物品栏数组起始地址,EBX*4 是索引偏移,+08 可能是数组元素大小的调整,ECX 获取到的可能是指定 OccupiedSlot 中的物品槽位对象指针
Terraria.Player::GetItem_FillIntoOccupiedSlot+EE - 8B B9 B4000000 - mov edi,[ecx+000000B4] ; 将 [ECX+B4] 内存地址的值移动到 EDI 寄存器,ECX 是物品槽位对象指针,+B4 偏移处可能是当前槽位中物品的堆叠数量
Terraria.Player::GetItem_FillIntoOccupiedSlot+F4 - 03 C7 - add eax,edi ; 将 EDI 寄存器的值 (当前槽位物品堆叠数量) 加到 EAX 寄存器 (拾取物品堆叠数量),EAX 累加后表示合并后的总数量
Terraria.Player::GetItem_FillIntoOccupiedSlot+F6 - 8B 91 B8000000 - mov edx,[ecx+000000B8] ; 将 [ECX+B8] 内存地址的值移动到 EDX 寄存器,ECX 是物品槽位对象指针,+B8 偏移处可能是当前槽位物品的最大堆叠数量
Terraria.Player::GetItem_FillIntoOccupiedSlot+FC - 3B C2 - cmp eax,edx ; 比较 EAX 寄存器的值 (合并后的总数量) 和 EDX 寄存器的值 (最大堆叠数量)
Terraria.Player::GetItem_FillIntoOccupiedSlot+FE - 0F8F 7F000000 - jg Terraria.Player::GetItem_FillIntoOccupiedSlot+183 ; 如果 EAX > EDX (大于),则跳转到地址 Terraria.Player::GetItem_FillIntoOccupiedSlot+183,表示合并后超过最大堆叠数量,需要进行特殊处理
Terraria.Player::GetItem_FillIntoOccupiedSlot+104- 8B 45 EC - mov eax,[ebp-14] ; 将 [EBP-14] 内存地址的值 (之前保存的拾取物品堆叠数量) 移动到 EAX 寄存器
Terraria.Player::GetItem_FillIntoOccupiedSlot+107- 01 81 B4000000 - add [ecx+000000B4],eax { 断点分析 - 拾取物品时断住 ECX=47181D88 EAX=2 } ; 将 EAX 寄存器的值 (拾取物品堆叠数量) 加到 [ECX+B4] 内存地址,ECX 是目标物品槽位对象指针,+B4 偏移处是物品堆叠数量,实际上是将拾取的物品数量添加到已占用的槽位中,断点分析 EAX=2 说明本次拾取的物品数量为 2
Terraria.Player::GetItem_FillIntoOccupiedSlot+10D- 80 7D 15 00 - cmp byte ptr [ebp+15],00 { 0 } ; 比较 [EBP+15] 内存地址的一个字节值和 0,[EBP+15] 处可能是一个标志位,用于控制是否显示拾取文本
Terraria.Player::GetItem_FillIntoOccupiedSlot+111- 75 1E - jne Terraria.Player::GetItem_FillIntoOccupiedSlot+131 ; 如果 [EBP+15] != 0 (不等于),则跳转到地址 Terraria.Player::GetItem_FillIntoOccupiedSlot+131,根据 jne 指令,可以推测标志位非零时跳过显示拾取文本的代码
Terraria.Player::GetItem_FillIntoOccupiedSlot+113- 8B 45 0C - mov eax,[ebp+0C] ; 再次将 [EBP+0C] 内存地址的值 (拾取物品对象指针) 移动到 EAX 寄存器
Terraria.Player::GetItem_FillIntoOccupiedSlot+116- 8B 80 B4000000 - mov eax,[eax+000000B4] ; 再次将 [EAX+B4] 内存地址的值 (拾取物品堆叠数量) 移动到 EAX 寄存器
Terraria.Player::GetItem_FillIntoOccupiedSlot+11C- 89 45 EC - mov [ebp-14],eax ; 将 EAX 寄存器的值 (物品堆叠数量) 存储到 [EBP-14] 内存地址,再次保存
Terraria.Player::GetItem_FillIntoOccupiedSlot+11F- 50 - push eax ; 将 EAX 寄存器的值 (物品堆叠数量) 压入堆栈,作为 PopupText::NewText 函数的参数 (物品数量)
Terraria.Player::GetItem_FillIntoOccupiedSlot+120- 6A 00 - push 00 { 0 } ; 将立即数 0 压入堆栈,可能是颜色参数或类型参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+122- 0FB6 45 14 - movzx eax,byte ptr [ebp+14] { 断点分析 - 拾取物品时断住 EAX=2 EBP=0233E3B8 } ; 将 [EBP+14] 内存地址的字节值 (byte 类型) 零扩展移动到 EAX 寄存器,根据断点分析 EAX=2,[EBP+14] 可能是之前存储的少量数据,这里可能复用了局部变量的地址,但其字节值可能与拾取物品数量相关,此处存疑,或者 byte ptr [ebp+14] 可能是某个物品属性
Terraria.Player::GetItem_FillIntoOccupiedSlot+126- 50 - push eax ; 将 EAX 寄存器的值压入堆栈,作为 PopupText::NewText 函数的参数 (数量或类型,具体含义需进一步分析)
Terraria.Player::GetItem_FillIntoOccupiedSlot+127- 8B 55 18 - mov edx,[ebp+18] ; 将 [EBP+18] 内存地址的值移动到 EDX 寄存器,[EBP+18] 可能是 PopupText::NewText 函数的参数 (文本内容,例如物品名称)
Terraria.Player::GetItem_FillIntoOccupiedSlot+12A- 33 C9 - xor ecx,ecx ; 将 ECX 寄存器清零,ECX 寄存器通常作为 this 指针或函数调用的第一个参数,此处清零可能是表示没有 this 指针或者作为某种标志
Terraria.Player::GetItem_FillIntoOccupiedSlot+12C- E8 27737CFC - call Terraria.PopupText::NewText ; 调用 PopupText::NewText 函数,根据前面的 push 和 mov 指令,推测是在显示拾取物品的弹出文本
Terraria.Player::GetItem_FillIntoOccupiedSlot+131- 8B CE - mov ecx,esi ; 将 ESI 寄存器的值 (Player 对象指针) 移动到 ECX 寄存器,作为 Player::DoCoins 函数的 this 指针
Terraria.Player::GetItem_FillIntoOccupiedSlot+133- 8B D3 - mov edx,ebx ; 将 EBX 寄存器的值 (OccupiedSlot 索引) 移动到 EDX 寄存器,作为 Player::DoCoins 函数的参数 (槽位索引)
Terraria.Player::GetItem_FillIntoOccupiedSlot+135- E8 16F5FFFF - call Terraria.Player::DoCoins ; 调用 Player::DoCoins 函数,处理金币类物品的拾取逻辑,可能涉及到金币的增加和显示
Terraria.Player::GetItem_FillIntoOccupiedSlot+13A- 8B 45 F0 - mov eax,[ebp-10] ; 将 [EBP-10] 内存地址的值移动到 EAX 寄存器, [EBP-10] 可能是一个标志位或状态值
Terraria.Player::GetItem_FillIntoOccupiedSlot+13D- 3B 05 24107105 - cmp eax,[05711024] { (0) } ; 比较 EAX 寄存器的值和全局内存地址 [05711024] 处的值,[05711024] 可能是一个全局变量,用于控制是否查找新的配方
Terraria.Player::GetItem_FillIntoOccupiedSlot+143- 75 07 - jne Terraria.Player::GetItem_FillIntoOccupiedSlot+14C ; 如果 EAX != [05711024] (不等于),则跳转到地址 Terraria.Player::GetItem_FillIntoOccupiedSlot+14C,表示条件不满足,跳过查找配方
Terraria.Player::GetItem_FillIntoOccupiedSlot+145- 33 C9 - xor ecx,ecx ; 将 ECX 寄存器清零
Terraria.Player::GetItem_FillIntoOccupiedSlot+147- E8 EC3F6B0D - call Terraria.Recipe::FindRecipes ; 调用 Recipe::FindRecipes 函数,查找新的配方,可能是在拾取物品后检查是否解锁了新的合成配方
Terraria.Player::GetItem_FillIntoOccupiedSlot+14C- 8B CE - mov ecx,esi ; 将 ESI 寄存器的值 (Player 对象指针) 移动到 ECX 寄存器,作为 AchievementsHelper::NotifyItemPickup 函数的 this 指针
Terraria.Player::GetItem_FillIntoOccupiedSlot+14E- 8B 55 0C - mov edx,[ebp+0C] ; 将 [EBP+0C] 内存地址的值 (拾取物品对象指针) 移动到 EDX 寄存器,作为 AchievementsHelper::NotifyItemPickup 函数的参数 (拾取的物品)
Terraria.Player::GetItem_FillIntoOccupiedSlot+151- FF 15 B0CD4C1F - call dword ptr [1F4CCDB0] { ->Terraria.GameContent.Achievements.AchievementsHelper::NotifyItemPickup } ; 调用 AchievementsHelper::NotifyItemPickup 函数,通知成就系统玩家拾取了物品,用于触发和更新成就
Terraria.Player::GetItem_FillIntoOccupiedSlot+157- 8B 86 D8000000 - mov eax,[esi+000000D8] ; 再次将 [ESI+D8] 内存地址的值 (物品栏数组起始地址) 移动到 EAX 寄存器
Terraria.Player::GetItem_FillIntoOccupiedSlot+15D- 3B 58 04 - cmp ebx,[eax+04] ; 再次比较 EBX 寄存器的值 (OccupiedSlot 索引) 和 [EAX+04] 内存地址的值 (物品栏数组边界)
Terraria.Player::GetItem_FillIntoOccupiedSlot+160- 0F83 D6000000 - jae Terraria.Player::GetItem_FillIntoOccupiedSlot+23C ; 如果 EBX >= [EAX+04] (无符号大于等于),则跳转到地址 Terraria.Player::GetItem_FillIntoOccupiedSlot+23C,再次进行索引范围检查
Terraria.Player::GetItem_FillIntoOccupiedSlot+166- 8B 4C 98 08 - mov ecx,[eax+ebx*4+08] ; 再次获取指定 OccupiedSlot 的物品槽位对象指针到 ECX 寄存器
Terraria.Player::GetItem_FillIntoOccupiedSlot+16A- 8B D1 - mov edx,ecx ; 将 ECX 寄存器的值 (物品槽位对象指针) 复制到 EDX 寄存器,作为 GetItemSettings::HandlePostAction 函数的参数
Terraria.Player::GetItem_FillIntoOccupiedSlot+16C- 8D 4D 10 - lea ecx,[ebp+10] ; 将 [EBP+10] 内存地址的有效地址加载到 ECX 寄存器,[EBP+10] 可能是 GetItemSettings::HandlePostAction 函数的另一个参数 (可能是物品拾取设置或上下文信息)
Terraria.Player::GetItem_FillIntoOccupiedSlot+16F- E8 CC65E9FE - call Terraria.GetItemSettings::HandlePostAction ; 调用 GetItemSettings::HandlePostAction 函数,处理物品拾取后的后续动作,例如触发物品的特殊效果、更新玩家状态等
Terraria.Player::GetItem_FillIntoOccupiedSlot+174- B8 01000000 - mov eax,00000001 { 1 } ; 将立即数 1 移动到 EAX 寄存器,可能是设置函数返回值,表示成功
Terraria.Player::GetItem_FillIntoOccupiedSlot+179- 8D 65 F4 - lea esp,[ebp-0C] ; 将 [EBP-0C] 的地址加载到 ESP 寄存器,调整堆栈指针,清理局部变量空间
Terraria.Player::GetItem_FillIntoOccupiedSlot+17C- 5B - pop ebx ; 弹出堆栈顶端的值到 EBX 寄存器,恢复 EBX 寄存器的值 (函数调用约定的一部分)
Terraria.Player::GetItem_FillIntoOccupiedSlot+17D- 5E - pop esi ; 弹出堆栈顶端的值到 ESI 寄存器,恢复 ESI 寄存器的值
Terraria.Player::GetItem_FillIntoOccupiedSlot+17E- 5F - pop edi ; 弹出堆栈顶端的值到 EDI 寄存器,恢复 EDI 寄存器的值
Terraria.Player::GetItem_FillIntoOccupiedSlot+17F- 5D - pop ebp ; 弹出堆栈顶端的值到 EBP 寄存器,恢复 EBP 寄存器的值,恢复函数栈帧
Terraria.Player::GetItem_FillIntoOccupiedSlot+180- C2 1400 - ret 0014 { 20 } ; 函数返回,并清理堆栈上的 20 字节 (0x14 字节 = 20 字节),通常是清理函数参数占用的堆栈空间


浙公网安备 33010602011771号