注入 C++ 代码,同时也最大化与其他注入的兼容性

逆向工程 视窗 x86 C++ 修补 游戏黑客
2021-07-03 05:17:47

使用适用于 Fallout New Vegas 的 New Vegas Script Extender C++ API,我能够在运行时通过 NVSE 注入器加载的 .dll 插件直接写入进程的内存。但它不像 CheatEngine 那样是一个调试器,因此没有自动处理将新代码插入现有程序集函数的过程。

汇编函数的开始TryOpenPipboy,我想注入代码到:https : //i.imgur.com/MQUrbXa.png

EquipPlayerPipboy()我想注入的功能:https : //i.imgur.com/IjmMRFU.png

游戏有一个循环调用MainUILoop,它运行每一帧,当玩家按下 Tab 键而不按住它时,TryOpenPipboy调用。

我的猜测是我需要以某种方式创建一个新的空内存块,编辑原始TryOpenPipboy函数的副本,然后TryOpenPipboy用我修改过的变体替换所有调用。但这将与任何其他 NVSE 插件严重不兼容,这些插件也以任何方式修改 MainUILoop,以及任何编辑TryOpenPipboy.

编辑:另一种可能的方法是将 jmp 程序集更改为我的 C++ 函数,然后TryOpenPipboy从我的函数调用这意味着TryOpenPipboy仍然被调用,但未被修改,增加了潜在的兼容性。

那么我应该怎么做呢?

3个回答

如果你只想在TryOpenPipboy被调用之前或之后运行代码,你可以用你的函数的 a 覆盖函数的序言jmp在覆盖之前,将序言中的指令复制到一个新的内存缓冲区,并在最后添加一个跳转到函数的其余部分,这称为蹦床。可以使用trampoline调用原函数。

前:

:originalFunction
push ebp ;55
mov ebp, esp ;89 E5
sub esp, 0x40 ;83 EC 40
;...
ret

打补丁后:

:originalFunction
jmp yourFunction ;?? ?? ?? ?? ??
nop ;this used to be the 0x40 byte at the end
:trampolinetarget
;...
ret

yourFunction:
;do stuff
call trampoline ;calls the original function
;do stuff
ret

trampoline:
push ebp
mov ebp, esp
sub esp, 0x40
jmp trampolinetarget

如果其他东西试图挂钩原始函数,他们只会覆盖该 jmp 指令,并且他们的蹦床将调用您的函数。Microsoft 的Detours库可以为您处理所有这些。

探针模式下的英特尔 PIN 可以做到这一点。您可以注入 C 代码,PIN 甚至会为您处理上下文切换。

所以基本上你只想要一个钩子?有很多方法可以实现这一点,但最简单的可能是 JMP 钩子。如果你愿意,我很乐意解释它是如何工作的细节,但我建议无论哪种方式都只使用像 MinHook 这样的现有库。请注意,如果此游戏在线,那么您很可能会被踢或禁止,代码部分的 CRC 哈希值是对您可以进行的不需要的修改的最简单检测。