2012-02-17 81 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