因此,为了获得更多倒车经验,我目前正在尝试为名为 Borderlands 2 的视频游戏编写一个外部黑客。我一直在使用作弊引擎来扫描修改弹药和健康等值的指令地址。我发现了一条指令,似乎用于损失健康和游戏中所有实体的总弹药。(即用 NOP 填充它对我没有帮助,因为它使我以及所有敌人进入神模式并提供所有信息。弹药)。我的目标是让一个命令让我进入上帝模式,让另一个命令给我 inf。弹药而不影响任何其他生物。有谁知道我如何在使用 Godmode 命令时仅针对我的健康取消此共享指令,而在使用 inf.txt 时仅针对弹药取消此共享指令。弹药指令?我想我需要以某种方式弄清楚该指令是否被用于更新我的玩家值(可能通过将基地址的其他偏移量与该偏移量处的敌人值进行比较),但我还需要弄清楚如何确定该指令试图修改我的生命值或弹药,以便我只能在它修改与我当前激活的任何命令对应的那个时才能 NOP。欢迎任何想法!
处理多条数据访问的指令
共享指令的乐趣!这里有一些想法供您考虑,我将使用 Cheat Engine 来演示这两个想法,因为它正在迅速成为游戏黑客的卓越动态分析工具。
1 . 与其查找写入地址的指令,不如右键单击它并查找访问该地址的指令。在那里,您会找到从地址读取值的指令,以及写入地址的指令。读取值的指令示例可能是健康的 HUD 表示。要了解显示您的健康栏已填充多远,它会读取您的健康地址中的值。
这意味着,如果您找到一个读取您的健康值的地址,那可能是该指令唯一能做的事情。然后,您可以在该指令之前创建代码注入,并将自定义值移动到被引用或指向的地址中。
例如,假设这是一条从您的健康地址读取的指令:
mov eax,[ecx+0C]
在这种情况下,[ecx+0C] 指向您的健康地址。那里的值被移入 eax 寄存器。您可以做的是在该指令处创建代码注入,以便在 mov 指令运行之前将自定义健康值写入 [ecx+0C]。这将改变健康的实际值,因此该自定义值然后从地址中读取。使用 Cheat Engine(通过其本机代码注入功能)很容易做到这一点,但除此之外,您还需要研究code-caving。
2.要使用像您发现的那样的共享指令,您必须找到一种方法来区分您想要影响的内容与正在写入的其余地址。这看起来像是一项艰巨的任务,尤其是当您发现像GameMaker 游戏中的共享指令时,其中数百个地址都通过一条指令写入,但不要害怕!
您的选择是在以下位置找到差异:
A) Memory addresses (Including the stack)
B) Registers (General purpose, FPU/XMM, special purpose, etc.)
右键单击共享指令并选择“找出此指令访问的地址”。将弹出一个新窗口,显示指令写入的地址。从那个窗口,你可以做很多整洁的事情。
首先,Cheat Engine 提供了一个方便的数据/结构剖析工具,您可以使用它来比较多个内存地址之间的值。如果您选择所有地址(按住 Shift 键单击,或按住 Ctrl 键单击您感兴趣的地址),然后右键单击,您将看到一个用于打开数据剖析工具的选项。在那里,您可以比较值以找到相同偏移量的差异!
您可以从该地址列表中执行的其他操作是按 Ctrl + R 查看寄存器的内容。在那里,您通常可以找到一个包含所有地址之间不同值的寄存器(您需要寻找一个不同的值,该值不是动态内存地址,因为该值会在重新启动游戏时发生变化)。
最后,在查看寄存器内容时,该窗口上有两个小按钮:“S”和“F”。
“S”让您查看堆栈。在那里,您可以查找要区分的值。“F”可让您查看 FPU 和 XMM 寄存器的内容。有时,后者可能会有所帮助,因为,比如说,FPU 可能仅用于健康,而不用于共享指令写入的任何其他值。
综合起来,您将使用您找到的任何唯一值来编写自定义代码注入,这将允许您挑选出您感兴趣的值。假设您的指令是这样的:
mov [edx+0C],eax
假设在寄存器 ebx 中,您找到了唯一值 2B。
您注入的代码可能如下所示:
label(customCode)
label(restoreFlags)
label(originalCode)
label(return)
customCode:
pushf //Preserve flags register by pushing onto the stack
cmp ebx,2B //Does ebx contain the value you're interested in?
jne restoreFlags //If not, restore flags register and resume normal execution
mov eax,(float)100 //If so, move a custom health value into eax for originalCode
//Execution flow from here moves to restoreFlags
restoreFlags:
popf //Restore flags register
//Execution flow from here moves to originalCode
originalCode:
mov [edx+0C],eax //eax contains either value from normal execution, or custom health
jmp return //Return to the point we injected and resume normal execution
我并不是说这是无耻的自我推销,但我已经在 YouTube 频道上运营了 3 年的大部分时间,通过游戏黑客来教授倒车。如果游戏是你的媒介并且你喜欢通过视频学习,我认为你会从我的 CE 教程系列中学到很多,特别是。我还教授特定于游戏的技巧,例如 Cuphead、ELEX 等。
祝你好运!
这里有点意外,但这里有一些想法可能对您有所帮助,但不适合评论:
3D 引擎通常在灵活的继承层次结构中使用实体对象。我假设如下:
class GameObject(x, y, z)
...
class GameEntity(x, y, z, health) : GameObject
void takeDamage(x)
void decreaseAmmo(x)
...
class Player() : GameEntity
在大多数常见语言(如 C++)中,每个类都有一个单独的函数表,指向所有类方法的实现。例如,当我们调用takeDamage
一个Player
-object,我们希望所有已经实现的方法GameEntities
执行。因此,NOP 出函数将产生您之前描述的效果。
tl;博士
似乎您有两个有效的选择:找到正确的函数指针来操作或检查this
-pointer的身份,它通常作为第一个参数传递给成员函数。