2012-02-17 62 views
11

我对挂钩感兴趣,我决定查看是否可以挂钩一些功能。我对使用像走弯路这样的图书馆不感兴趣,因为我希望有自己的经验。有了我在互联网上找到的一些消息,我可以创建下面的代码。这是基本的,但它工作正常。但是,当挂钩函数被多线程调用时,它证明是非常不稳定的。如果两个电话几乎同时发生,它会崩溃。经过一番研究后,我认为我需要创建一个蹦床功能。在找了几个小时之后,我无法找到其他任何关于什么是蹦床的一般描述。我找不到任何关于编写蹦床功能的具体内容,或者他们的工作方式。如果有人能帮我写一篇文章,发布一些消息来源,或者至少通过推荐一些文章,网站,书籍等来指引我正确的方向。我将不胜感激。如何为挂钩创建蹦床功能

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

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

我打算发布一个链接到GD,但我只是注意到你也是那里的成员。你尝试过使用他们的搜索功能吗?它提出了大量的例子:) – 2012-02-17 20:13:08

+0

你可以检查EasyHook的代码,我相信它是开源的。还有很多其他的例子。如果你打算在部署的应用程序中使用它,我建议使用一个库(如EasyHook),它可以处理挂钩/蹦床上的递归,线程和一些有趣的事情。 – ssube 2012-02-17 20:19:52

+0

@Tom Knapen在我发布之前,我搜索了GD,MPGH和其他一些网站。在GD上搜索“蹦床”会返回一些稍微相关的帖子,但不是我正在寻找的内容。 – Stratus 2012-02-17 20:24:09

回答

7

如果您希望在多个线程调用你的钩子是安全的,你不希望被不断脱钩,并rehooking的原始的API。

蹦床只是您生成的一小段代码,它复制了原始API的前几个字节(您跳过时将其覆盖)的功能,然后在您覆盖的字节后跳入API。

而不是解开API,调用它并重新调用它,你只需调用蹦床。

在x86上这样做比较复杂,因为您需要(非常小的)反汇编程序来查找指令边界。您还需要检查您复制到蹦床内的代码是否与指令指针(如jmp,分支或调用)相关。

这足以调用钩子线程安全的,但如果多线程正在使用API​​,则无法创建钩子。为此,您需要使用两个字节的近跳(可以以原子方式写)来挂接该函数。 Windows API经常在几个NOP之前(可以用远程跳转覆盖)来提供这个近跳的目标。

在x64上做这个事情是太多更复杂。你不能简单地用64位的远程跳转来修补该功能(因为没有一个,并且模拟它的指令通常太长)。而且,根据您的蹦床的功能,您可能需要将其添加到操作系统的堆栈展开信息中。

我希望这不是太笼统。

+0

谢谢,但这已经非常多,我已经找到了,并没有帮我写一个。 – Stratus 2012-02-18 02:49:05

+0

@Stratus:你错过了什么?分配一些可执行的内存。将n个字节从函数prolog复制到分配的内存中,然后跳转到函数prolog + n。 n是您需要复制以在函数prolog中释放至少5个字节的指令的大小。这是一个蹦床。还有一些其他的皱纹(如不要复制修改IP的指令),但这是基本的。 – arx 2012-02-18 03:07:37