【问题标题】:Hooking - hotpatching挂钩 - 热补丁
【发布时间】:2014-01-05 21:41:05
【问题描述】:

我正在尝试挂钩 Windows API 函数 FindWindowA()。我用下面的代码成功地做到了,没有“热补丁”它:我已经覆盖了函数开头的字节。调用 myHook() 并在调用 FindWindowA() 时显示一个消息框。

user32.dll 启用了热补丁,我想在实际函数之前覆盖 NOP,而不是覆盖函数本身。但是,当我将热补丁设置为 TRUE 时,下面的代码将不起作用。当 FindWindowA() 被执行时它什么也不做。

#include <stdio.h>
#include <windows.h>

void myHook()
{
    MessageBoxA(NULL, "Hooked", "Hook", MB_ICONINFORMATION);
}

int main(int argc, char *argv[])
{
    BOOLEAN hotpatching = FALSE;

    LPVOID fwAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "FindWindowA");
    LPVOID fwHotpatchingAddress = (LPVOID)((DWORD)fwAddress - 5);
    LPVOID myHookAddress = &myHook;

    DWORD jmpOffset = (DWORD)&myHook - (DWORD)(!hotpatching ? fwAddress : fwHotpatchingAddress) - 5; // -5 because "JMP offset" = 5 bytes (1 + 4)

    printf("fwAddress: %X\n", fwAddress);
    printf("fwHotpatchingAddress: %X\n", fwHotpatchingAddress);
    printf("myHookAddress: %X\n", myHookAddress);
    printf("jmpOffset: %X\n", jmpOffset);
    printf("Ready?\n\n");
    getchar();


    char JMP[1] = {0xE9};
    char RETN[1] = {0xC3};

    LPVOID offset0 = NULL;
    LPVOID offset1 = NULL;
    LPVOID offset2 = NULL;

    if (!hotpatching)
        offset0 = fwAddress;
    else
        offset0 = fwHotpatchingAddress;

    offset1 = (LPVOID)((DWORD)offset0 + 1);
    offset2 = (LPVOID)((DWORD)offset1 + 4);


    DWORD oldProtect = 0;
    VirtualProtect(offset0, 6, PAGE_EXECUTE_READWRITE, &oldProtect);

    memcpy(fwAddress, JMP, 1);
    memcpy(offset1, &jmpOffset, 4);
    memcpy(offset2, RETN, 1);

    VirtualProtect(offset0, 6, oldProtect, &oldProtect);


    printf("FindWindowA() Patched");
    getchar();


    FindWindowA(NULL, "Test");
    getchar();


    return 0;
}

你能告诉我怎么了吗?

谢谢。

【问题讨论】:

  • 相当混乱的问题。如果不对函数进行热补丁,则无法对函数进行热补丁。函数入口点之前的 5 个 NOP 不会被执行。它们只是为动态创建的 JMP 指令留出空间。要访问此代码,您必须使用相对跳转覆盖函数入口点处的 2 字节 NOP (mov edi,edi)。
  • 好的,我明白了!我覆盖了 NOP,但它们永远不会被执行,因为它们在实际函数开始之前 (mov edi, edi)。我必须用跳回 5 个字节的 JMP 指令替换 mov edi, edi(或 7 个,因为 mov edi, edi 是 2 个字节长)。谢谢。

标签: c windows assembly hook hotpatching


【解决方案1】:

编译器和链接器准备启用热补丁的可执行映像,以允许在使用时替换映像。应用了以下两个更改 (x86):

  1. 函数入口点设置为 2 字节无操作 mov edi, edi (/hotpatch)。
  2. 五个连续的 nop 被附加到每个函数入口点 (/FUNCTIONPADMIN)。

为了说明这一点,下面是一个启用热补丁的功能的典型反汇编列表:

(2) 768C8D66 90                   nop  
    768C8D67 90                   nop  
    768C8D68 90                   nop  
    768C8D69 90                   nop  
    768C8D6A 90                   nop  
(1) 768C8D6B 8B FF                mov         edi,edi
(3) 768C8D6D 55                   push        ebp  
    768C8D6E 8B EC                mov         ebp,esp  

(1) 用 2 字节无操作指定函数入口点。 (2) 是链接器提供的填充,(3) 是非平凡函数实现开始的地方。

要挂钩一个函数,您必须用跳转到您的挂钩函数jmp myHook 覆盖(2),并通过将(1) 替换为相对跳转jmp $-5 来使此代码可访问。

钩子函数必须使堆栈保持一致状态。应将其声明为__declspec(naked),以防止编译器生成函数prolog 和epilog 代码。最后一条指令要么按照挂钩函数的调用约定执行堆栈清理,要么跳回到(3)指定的地址处的挂钩函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 1970-01-01
    • 2011-12-26
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    相关资源
    最近更新 更多