1

これは EasyHook に関する特定の機能ではなく、一般的なフックに関する機能です。この署名で関数をフックしたい:

public: int __thiscall Connection_t::Send(unsigned int,unsigned int,void const *)

これは明らかに管理されていないコードであり、EasyHook を使用して管理されている C# コードでフックしようとしています

    public static int Send_Hooked(uint connection, uint size, IntPtr pDataBlock)
    {
        return Send(connection, size, pDataBlock);
    }

    [DllImport("Connection.dll", EntryPoint = "?Send@Connection_t@@QAEHIIPBX@Z", CallingConvention = CallingConvention.ThisCall)]
    static extern int Send(uint connection, uint size, IntPtr pDataBlock);

    [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode, SetLastError = true)]
    delegate int DSend(uint connection, uint size, IntPtr pDataBlock);

しかし、フックを挿入するとすぐに、フックされたプログラムがクラッシュし続けます-大きな驚きではありません。それは呼び出し規約の問題であり、フック関数がフックされたプログラムのスタックに何らかの形で干渉していると思います。

そこで、同じ関数をフックする別のプロジェクトを見てみましたが、c++ で回り道があります (フック部分):

Func =  (int (__stdcall *)(unsigned int, unsigned short, void const ))::GetProcAddress(::GetModuleHandle("Connection.dll"), "?Send@Connection_t@@QAEHIIPBX@Z");
PVOID DetourPtr;
PVOID TargetPtr;
DetourTransactionBegin();
DetourAttachEx(&Func, SendConnectionHook, &Trampoline, &TargetPtr, &DetourPtr );
DetourTransactionCommit();

そして、呼び出された関数:

__declspec(naked) void SendConnectionHook (CPU_CONTEXT saved_regs, void * ret_addr, WORD arg1, DWORD arg2, DWORD arg3)
{
    DWORD edi_value;
    DWORD old_last_error;

    __asm
    {
        pushad;   /* first "argument", which is also used to store registers */
        push ecx; /* padding so that ebp+8 refers to the first "argument" */

        /* set up standard prologue */
        push ebp;
        mov ebp, esp;
        sub esp, __LOCAL_SIZE;
    }

    edi_value = saved_regs.edi;
    old_last_error = GetLastError();
    OnConnectionSend((void *) saved_regs.ecx, (unsigned char *) arg3, arg2);
    SetLastError(old_last_error);

    __asm
    {
        /* standard epilogue */
        mov esp, ebp;
        pop ebp;

        pop ecx; /* clear padding */
        popad; /* clear first "argument" */
        jmp [Trampoline];
    }
}

(ターゲット アセンブリと C++ の例は、どちらも Visual C++ でコンパイルされています)。元の関数を呼び出す前に、いくつかのレジスタを保存してスタックを修復する必要があると思いますか? または、私がここで間違っていることは何か他の考えはありますか?

4

2 に答える 2

7

C++ クラスのインスタンス メソッドをフックしようとしています。隠し引数thisがあります。この引数は、通常、__this 呼び出し規則を使用して ECX レジスタを介して渡されます。それが、迂回路バージョンが行っていることです。

これを正しく行うのは非常に簡単ではありません。CPU レジスタの値は、特に ECX で早期に保存する必要があります。これにはマシン コードを使用するスタブが必要ですが、もちろんマネージ スタブにマシン コードは必要ありません。EasyHook がそれをサポートしているとは思えません。機能リストで約束されていないことは確かです。

于 2010-12-04T21:42:58.153 に答える
0

私はそれを理解したように見えます。@Hans Passantは正しかった:私は隠されたthis議論を保存しなければならない。EasyHookは、実際にはこれ以外のすべてを処理します(.netのもののクリーンアップなど)。最初の引数と同様thisに、私は単にそれを関数に追加しました(これconnectionは私のthis参照です):

    public static int Send_Hooked(IntPtr connection, uint unknown, uint size, IntPtr pDataBlock)
    {
        return Send(connection, unknown, size, pDataBlock);
    }

    [DllImport("Connection.dll", EntryPoint = "?Send@Connection_t@@QAEHIIPBX@Z", CallingConvention = CallingConvention.ThisCall)]
    static extern int Send(IntPtr connection, uint unknown, uint size, IntPtr pDataBlock);

    [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode, SetLastError = true)]
    delegate int DSend(IntPtr connection, uint unknown, uint size, IntPtr pDataBlock);

なぜこれが機能するのかを実際に説明することはできません(また、私はそれのほとんどを理解したと思います:)私は本当に戻って、いくつかのアセンブラー/コンパイル理論を学ぶ必要があります。

于 2010-12-04T23:14:11.183 に答える