如何为挂钩创建蹦床功能 [英] How to create a trampoline function for hook

查看:165
本文介绍了如何为挂钩创建蹦床功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对挂钩很感兴趣,因此决定看看是否可以挂钩某些功能.我对使用绕道而行的库不感兴趣,因为我想拥有自己动手做的经验.通过在互联网上找到的一些资源,我能够创建以下代码.这是基本的,但是可以正常工作.但是,当钩住由多个线程调用的函数时,它被证明是极其不稳定的.如果几乎同时拨打两个电话,它将崩溃.经过一些研究,我认为我需要创建一个蹦床功能.找了几个小时,除了蹦床的一般描述之外,我什么都找不到.我找不到关于编写蹦床功能或它们如何工作的任何具体信息.如果有人可以帮助我写一篇文章,发布一些资料或者通过推荐一些文章,网站,书籍等来使我指向正确的方向,我将不胜感激.

I'm interested in hooking and I decided to see if I could hook some functions. I wasn't interested in using a library like detours because I want to have the experience of doing it on my own. With some sources I found on the internet, I was able to create the code below. It's basic, but it works alright. However when hooking functions that are called by multiple threads it proves to be extremely unstable. If two calls are made at nearly the same time, it'll crash. After some research I think I need to create a trampoline function. After looking for hours all I was not able to find anything other that a general description on what a trampoline was. I could not find anything specifically about writing a trampoline function, or how they really worked. If any one could help me write one, post some sources, or at least point me in the right direction by recommending some articles, sites, books, etc. I would greatly appreciate it.

下面是我编写的代码.这确实很基础,但我希望其他人可以从中学到东西.

Below is the code I've written. It's really basic but I hope others might learn from it.

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;
}

推荐答案

如果您希望钩子在被多个线程调用时是安全的,那么您就不想不断地摘钩和摘钩原始API.

If you want your hook to be safe when called by multiple threads, you don't want to be constantly unhooking and rehooking the original API.

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

A trampoline is simply a bit of code you generate that replicates the functionality of the first few bytes of the original API (which you overwrote with your jump), then jumps into the API after the bytes you overwrote.

您无需调用API而是重新调用它,而只需取消调用该蹦床就可以了.

Rather than unhooking the API, calling it and rehooking it you simply call the trampoline.

在x86上执行此操作相当复杂,因为您需要(相当少的)反汇编程序来查找指令边界.您还需要检查复制到蹦床上的代码相对于指令指针没有任何作用(例如,jmp,分支或调用).

This is moderately complicated to do on x86 because you need (a fairly minimal) disassembler to find the instruction boundaries. You also need to check that the code you copy into your trampoline doesn't do anything relative to the instruction pointer (like a jmp, branch or call).

这足以使钩子线程安全调用,但是如果多个线程正在使用API​​,则无法创建钩子.为此,您需要将函数与两个字节的近跳转挂钩(可以原子编写). Windows API经常在前面加上一些NOP(可以用远距离跳转来覆盖)来为这种近距离跳转提供目标.

This is sufficient to make calls to the hook thread-safe, but you can't create the hook if multiple threads are using the API. For this, you need to hook the function with a two-byte near jump (which can be written atomically). Windows APIs are frequently preceded by a few NOPs (which can be overwritten with a far jump) to provide a target for this near jump.

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

Doing this on x64 is much more complicated. You can't simply patch the function with a 64-bit far jump (because there isn't one, and instructions to simulate it are often too long). And, depending on what your trampoline does, you may need to add it to the OS's stack unwind information.

我希望这不是太笼统.

这篇关于如何为挂钩创建蹦床功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆