c++ - 除非使用某些寄存器,否则函数 Hook 会崩溃

标签 c++ visual-c++ x86 inline-assembly calling-convention

所以我试图为游戏 Hook 一个函数,但有一个小问题。如果 eax、ebx、ecx 和 edx 等寄存器可以互换,为什么下面的第一个代码示例会导致游戏进程崩溃,但第二个代码不会崩溃并按预期工作?

// Crashes game process
void __declspec(naked) HOOK_UnfreezePlayer()
{
    __asm push eax

    if ( !state->player.frozen || !state->ready )
        __asm jmp hk_Disabled

    __asm
    {
        mov eax, g_dwBase_Addr
        mov ebx, [eax + LOCAL_PLAYER_INFO_OFFSET]
        add ebx, 0x4
        mov ecx, [ebx]
        add ecx, 0x40
        lea edx, [esi + 0x0C]
        cmp edx, ecx
        je hk_Return

        hk_Disabled:
        movss [esi + 0x0C], xmm0

        hk_Return:
        pop eax
        mov ecx, g_dwBase_Addr
        add ecx, RETURN_UnfreezePlayer
        jmp ecx
    }
}

// Works
void __declspec(naked) HOOK_UnfreezePlayer()
{
    __asm push eax

    if ( !state->player.frozen || !state->ready )
        __asm jmp hk_Disabled

    __asm
    {
        mov ecx, g_dwBase_Addr
        mov edx, [ecx + LOCAL_PLAYER_INFO_OFFSET]
        add edx, 0x4
        mov ebp, [edx]
        add ebp, 0x40
        lea ecx, [esi + 0x0C]
        cmp ecx, ebp
        je hk_Return

        hk_Disabled:
        movss [esi + 0x0C], xmm0

        hk_Return:
        pop eax
        mov ecx, g_dwBase_Addr
        add ecx, RETURN_UnfreezePlayer
        jmp ecx
    }
}

我认为崩溃可能是由于我的汇编代码覆盖了寄存器 eax、ebx、ecx 等中的重要数据引起的。例如,如果游戏在 eax 中存储了一个重要值,然后由于我的操作而导致该数据丢失,该怎么办? if 语句将结构体指针移动到 eax 中?有没有办法保留这些寄存器的内容并在返回之前将它们恢复为原始值?

最佳答案

当 Hook 一个已经编译的程序时,寄存器当然是不可互换的,因为各个寄存器的含义是由 Hook 程序的代码和该代码中 Hook 的位置定义的。因此,您必须检查被 Hook 的代码和 Hook 的位置,以确定被 Hook 的代码是否依赖于所保留的某些寄存器的内容。

通过开头的 push eax 指令和结尾的 pop eax 指令,您已经保存了 EAX 寄存器的内容并在之后恢复它。您可以对 EBX 和 EDX 寄存器执行相同的操作,或者简单地使用 PUSHAD/POPAD 指令来保存所有通用寄存器。根据游戏中 Hook 的位置,您可能还必须保留 EFLAGS 寄存器,这需要 PUSHFD/POPFD 指令。

保存和恢复 ECX 寄存器并不那么容易,因为钩子(Hook)正在使用该寄存器来计算完成后要跳转到的地址。

但是,由于您说第二个代码示例有效,而第一个代码示例导致 Hook 程序崩溃,因此问题很可能仅在于 EBX 寄存器被修改。这是因为第一个代码示例修改了 EBX 寄存器,而第二个代码示例则没有。

因此,解决您的问题的可能方法是以与保存 EAX 寄存器相同的方式保存 EBX 寄存器。为此,您只需在 push eax 指令的同一位置添加一条 push ebx 指令,并添加一条 pop ebx > 指令与 pop eax 指令位于同一位置。但是,请注意,由于堆栈的工作方式,入栈和出栈指令必须是相反的顺序,如下所示:

Hook 开始:

push eax
push ebx

钩端:

pop ebx
pop eax

关于c++ - 除非使用某些寄存器,否则函数 Hook 会崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59573227/

相关文章:

c++ - 为什么我不能定义一个一元运算符,然后在 MSVC 的模板类中声明一个同名的友元二元运算符?

c++ - 为什么我的 x86 汇编代码会导致段错误?

c++ - 在 MSVC 2012 中禁用剪贴板环

c++ - 使用 cmake 制作项目源目录的头文件后代

成员方法中的 C++11 Lambda 函数继承作用域

c++ - 在 vector 声明中初始化对象

visual-c++ - 如何使用OpenCV将Bayer转换为RGB,反之亦然

c++ - Qt 程序与另一个编译器崩溃

c - 针对 x86 架构使用 gcc 编译器的双堆栈对齐问题

c - 如何在 C 中正确创建内核并链接到引导加载程序