アンマネージ関数のフック、呼び出し規約に関するスタック/レジスタの問題?

assembly c# calling-convention detours hook
アンマネージ関数のフック、呼び出し規約に関するスタック/レジスタの問題?

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

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

これは明らかにアンマネージコードであり、EasyHookを使用してマネージc#コードでフックしようとしていますが、ここでは問題を引き起こすEasyHookではなく、呼び出し規約などに関する私の知識があります…​ +これは、DllImportを定義して削除する方法です。

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

    [DllImport("Connection.dll", EntryPoint = "[email protected][email protected]@[email protected]", 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"), "[email protected][email protected]@[email protected]");
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 の例は両方ともビジュアルc でコンパイルされています)。 元の関数を呼び出す前に、いくつかのレジスタを保存してスタックを修復する必要があると思いますか? または、私がここで間違っていることを他に考えていますか?

  1  1


ベストアンサー

C ++クラスインスタンスメソッドをフックしようとしています。 _this_という隠し引数があります。 通常、この引数は__this呼び出し規約を使用してECXレジスタに渡されます。 それがDetoursバージョンの動作です。

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

7


私はそれを理解したように見えます。 @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 = "[email protected][email protected]@[email protected]", 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);

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

0


タイトルとURLをコピーしました