【问题标题】:How to create a trampoline function for hook如何为钩子创建蹦床功能
【发布时间】:2012-03-09 05:24:24
【问题描述】:

我对挂钩很感兴趣,我决定看看我是否可以挂钩一些功能。我对使用 detours 之类的库不感兴趣,因为我想拥有自己做这件事的经验。通过我在互联网上找到的一些资源,我能够创建下面的代码。这是基本的,但它工作正常。但是,当挂钩由多个线程调用的函数时,它被证明是极其不稳定的。如果几乎同时拨打两个电话,它就会崩溃。经过一些研究,我认为我需要创建一个蹦床功能。在寻找了几个小时之后,除了对蹦床的一般描述之外,我找不到任何其他东西。我找不到任何关于编写蹦床函数的具体内容,或者它们是如何工作的。如果有人可以帮助我写一篇文章,发布一些资源,或者至少通过推荐一些文章、网站、书籍等为我指明正确的方向。我将不胜感激。

下面是我写的代码。这真的很基础,但我希望其他人可以从中学习。

test.cpp

#include "stdafx.h"

Hook hook;

typedef int (WINAPI *tMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

DWORD hMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
    hook.removeHook();
    tMessageBox oMessageBox = (tMessageBox)hook.funcPtr; 
    int ret =oMessageBox(hWnd, lpText, "Hooked!", uType);
    hook.applyHook(&hMessageBox);

    return ret;
}

void hookMessageBox()
{
    printf("Hooking MessageBox...\n");
    if(hook.findFunc("User32.dll", "MessageBoxA")) 
    {
        if(hook.applyHook(&hMessageBox))
        {
            printf("hook applied! \n\n");
        } else printf("hook could not be applied\n");
    }   
}

hook.cpp

#include "stdafx.h"

bool Hook::findFunc(char* libName, char* funcName) 
{
    Hook::funcPtr = (void*)GetProcAddress(GetModuleHandleA(libName), funcName); 
    return (Hook::funcPtr != NULL);
}

bool Hook::removeHook() 
{
    DWORD dwProtect;
    if(VirtualProtect(Hook::funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect))
        {
        WriteProcessMemory(GetCurrentProcess(), (LPVOID)Hook::funcPtr, Hook::origData, 6, 0);
        VirtualProtect(Hook::funcPtr, 6, dwProtect, NULL);
        return true;
    } else return false;
}

bool Hook::reapplyHook() 
{
    DWORD dwProtect;
    if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) 
        {
        WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::hookData, 6, 0);
        VirtualProtect(funcPtr, 6, dwProtect, NULL);
        return true;
    } else return false;
}

bool Hook::applyHook(void* hook) 
{ 
    return setHookAtAddress(Hook::funcPtr, hook);
}

bool Hook::setHookAtAddress(void* funcPtr, void* hook) 
{
    Hook::funcPtr = funcPtr;
    BYTE jmp[6] = { 0xE9, //jmp
                   0x00, 0x00, 0x00, 0x00,  //address
                   0xC3 //retn 
                 };

    DWORD dwProtect;

    if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) // make memory writable
    {

        ReadProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::origData, 6, 0); // save old data
        DWORD offset = ((DWORD)hook - (DWORD)funcPtr - 5);  //((to)-(from)-5)
        memcpy(&jmp[1], &offset, 4); // write address into jmp
        memcpy(Hook::hookData, jmp, 6); // save hook data
        WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, jmp, 6, 0); // write jmp
        VirtualProtect(funcPtr, 6, dwProtect, NULL); // reprotect

        return true;
    } else return false;
}

【问题讨论】:

  • 我打算发布一个指向 GD 的链接,但我刚刚注意到你也是那里的成员。您是否尝试过使用他们的搜索功能?它提出了大量的例子:)
  • 你可以查看 EasyHook 的代码,我相信它是开源的。周围还有很多其他的例子。如果您打算在已部署的应用程序中使用它,我建议您使用已经可以处理钩子/蹦床、线程和一些有趣的东西上的递归的库(如 EasyHook)。
  • @Tom Knapen 在发布之前,我搜索了 GD、MPGH 和其他一些网站。在 GD 上搜索“蹦床”会返回一些稍微相关的帖子,但不是我要找的。​​span>
  • 你的代码对初学者很有用,谢谢

标签: c++ winapi hook tramp trampolines


【解决方案1】:

如果您希望钩子在被多个线程调用时是安全的,您不希望不断地取消钩子并重新钩子原始 API。

蹦床只是您生成的一段代码,它复制原始 API 的前几个字节的功能(您用跳转覆盖了这些字节),然后在您覆盖的字节之后跳转到 API。

您只需调用 trampoline,而不是解开 API,调用它并重新连接它。

在 x86 上执行此操作相当复杂,因为您需要(相当少的)反汇编程序来查找指令边界。您还需要检查您复制到蹦床中的代码是否与指令指针相关(如 jmp、分支或调用)。

这足以对钩子进行线程安全调用,但如果多个线程正在使用 API,则无法创建钩子。为此,您需要使用一个两字节的近跳转来挂钩该函数(可以原子地编写)。 Windows API 前面经常带有一些 NOP(可以用远跳转覆盖),以便为这种近跳转提供目标。

在 x64 上执行此操作要复杂得多。您不能简单地用 64 位远跳转修补函数(因为没有,而且模拟它的指令通常太长)。而且,根据蹦床的功能,您可能需要将其添加到操作系统的堆栈展开信息中。

我希望这不是太笼统。

【讨论】:

  • 谢谢,但这几乎是我已经找到的,并不能帮助我写一个。
  • @Stratus:你错过了什么?分配一些可执行内存。将 n 个字节从函数 prolog 复制到分配的内存,然后跳转到函数 prolog + n。 n 是您需要复制以释放函数 prolog 中至少 5 个字节的指令的大小。那是蹦床。还有一些其他问题(比如不要复制修改 IP 的指令),但基本上就是这样。
  • x64 如果你提供一个寄存器,例如 RAX,它会有很大的跳跃。为了线程安全,你是对的,但是如果你不需要线程安全或者不想处理你提到的复杂性并且不想使用第三方库,你可以用关键区域保护区域。如果您不编写性能关键代码,或者它不在 hotpath ofc 中。
【解决方案2】:

事实上的标准挂钩教程来自 jbremer 并且可以在 here 获得

下面是一个简单的 x86 detour and trampoline hook,基于本教程,使用 Direct3D 的 EndScene() 函数为例:

bool Detour32(char* src, char* dst, const intptr_t len)
{
    if (len < 5) return false;

    DWORD  curProtection;
    VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &curProtection);

    intptr_t  relativeAddress = (intptr_t)(dst - (intptr_t)src) - 5;

    *src = (char)'\xE9';
    *(intptr_t*)((intptr_t)src + 1) = relativeAddress;

    VirtualProtect(src, len, curProtection, &curProtection);
    return true;
}

char* TrampHook32(char* src, char* dst, const intptr_t len)
{
    // Make sure the length is greater than 5
    if (len < 5) return 0;

    // Create the gateway (len + 5 for the overwritten bytes + the jmp)
    void* gateway = VirtualAlloc(0, len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    //Write the stolen bytes into the gateway
    memcpy(gateway, src, len);

    // Get the gateway to destination addy
    intptr_t  gatewayRelativeAddr = ((intptr_t)src - (intptr_t)gateway) - 5;

    // Add the jmp opcode to the end of the gateway
    *(char*)((intptr_t)gateway + len) = 0xE9;

    // Add the address to the jmp
    *(intptr_t*)((intptr_t)gateway + len + 1) = gatewayRelativeAddr;

    // Perform the detour
    Detour32(src, dst, len);

    return (char*)gateway;
}

typedef HRESULT(APIENTRY* tEndScene)(LPDIRECT3DDEVICE9 pDevice);
tEndScene oEndScene = nullptr;

HRESULT APIENTRY hkEndScene(LPDIRECT3DDEVICE9 pDevice)
{
    //do stuff in here
    return oEndScene(pDevice);
}

//just an example
int main()
{
    oEndScene = (tEndScene)TrampHook32((char*)d3d9Device[42], (char*)hkEndScene, 7);
}

【讨论】:

    猜你喜欢
    • 2014-12-30
    • 2010-09-16
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    • 1970-01-01
    • 2014-10-03
    • 1970-01-01
    • 2018-11-27
    相关资源
    最近更新 更多