2012-02-17 8 views
11

私はフックに興味があり、私はいくつかの機能をフックできるかどうかを見極めることにしました。私は迂回路のような図書館を使うのに興味がありませんでした。自分でそれをやってみたいという経験がありたいからです。私はインターネット上で見つけたいくつかの情報源を使って、以下のコードを作成することができました。基本的ですが、それは問題なく動作します。しかし、複数のスレッドによって呼び出される関数をフックすると、非常に不安定であることがわかります。ほぼ同時に2回の通話が行われると、クラッシュします。いくつかの研究の後、私はトランポリン機能を作成する必要があると思う。すべての時間を探した後、私はトランポリンが何であるかについての一般的な説明以外の何かを見つけることができませんでした。私は、トランポリンの機能を書くことや、彼らが実際に働いた方法について特に何かを見つけることができませんでした。もし誰かが私が1つを書くのを手助けしたり、いくつかの情報源を投稿したり、少なくともいくつかの記事、サイト、本などを推薦することによって正しい方向に向けることができたら、私はそれを高く評価します。フックのトランポリン関数を作成する方法

以下は私が書いたコードです。それは本当に基本的ですが、私は他の人がそれから学ぶことを願っています。

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へのリンクを投稿しようとしていましたが、あなたもそのメンバーです。検索機能を使ってみましたか?それはたくさんの例が出てくる:) –

+0

あなたはEasyHookのコードをチェックするかもしれない、私はそれがオープンソースだと信じている。周囲にも多くの例があります。展開されたアプリケーションでこれを使用する予定の場合は、フック/トランポリン、スレッド、および楽しいものの再帰をすでに処理できるライブラリ(EasyHookなど)を使用することをお勧めします。 – ssube

+0

@Tom Knapen投稿する前に、GD、MPGHなどのサイトを検索しました。 GDで 'trampoline'を検索すると、少しだけ関連する記事が返されますが、私が探しているものは返しません。 – Stratus

答えて

7

あなたが複数のスレッドによって呼び出されたときに、あなたのフックが安全になりたい場合は、あなたは常にアンフックとrehookingされないようにします元のAPI。

トランポリンは元のAPIの最初の数バイトの機能(あなたのジャンプで上書きしたもの)を複製し、次に上書きしたバイトの後にAPIにジャンプするコードです。

APIのフックを外して呼び出して、再フッキングするのではなく、単にトランポリンを呼び出します。

これは、命令の境界を見つけるために(かなり最小限の)逆アセンブラが必要なため、x86ではやりにくいです。トランポリンにコピーしたコードが命令ポインタ(jmp、ブランチ、コールなど)に関係なく何もしないことを確認する必要があります。

これはフックスレッドセーフを呼び出すのには十分ですが、複数のスレッドがAPIを使用している場合はフックを作成できません。このためには、2バイト近傍ジャンプ(アトミックに書くことができる)で関数をフックする必要があります。 Windows APIには、この遠くのジャンプのターゲットを提供するために、いくつかのNOP(遠くのジャンプで上書きすることができる)が先行することがよくあります。

これをx64で実行すると、多くのが複雑になります。 64ビットの遠距離ジャンプで関数を単純にパッチすることはできません(それがないため、シミュレートするための命令はしばしば長すぎます)。そして、あなたのトランポリンが何をするかによっては、それをOSのスタックアンワインド情報に追加する必要があります。

これはあまり一般的ではないことを願っています。

+0

ありがとうございますが、これは私がすでに見つけたものであり、私が1つ書くのを助けません。 – Stratus

+0

@Stratus:あなたは何が欠けていますか?実行可能なメモリを割り当てます。関数のプロローグから割り当てられたメモリにnバイトをコピーし、関数prolog + nへのジャンプでそれをフォローします。 nは、関数プロローグで少なくとも5バイトを解放するためにコピーする必要がある命令のサイズです。それはトランポリンです。他にもいくつかのシワがあります(IPを変更する命令をコピーしないなど)が基本的です。 – arx

関連する問題