Неуправляемая перехват функций, проблема со стеком / регистром с соглашением о вызовах? - PullRequest
1 голос
/ 03 декабря 2010

Это не особая функция в EasyHook, а вообще о подключении. Я хочу подключить функцию с такой подписью:

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

Это явно неуправляемый код, и я пытаюсь связать его с моим управляемым кодом на C # с помощью EasyHook. Но я думаю, что это не EasyHook вызывает проблемы здесь, но мой ноу-хау по соглашениям о вызовах и т.д. ...
Вот как я определяю DllImport и удаляю:

    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 ++). Я думаю, мне придется сохранить некоторые регистры и восстановить стек, прежде чем я вызову исходную функцию? Или какая-то другая идея, что я здесь делаю не так?

Ответы [ 2 ]

7 голосов
/ 05 декабря 2010

Вы пытаетесь подключить метод экземпляра класса C ++. У него есть скрытый аргумент, это . Этот аргумент обычно передается через регистр ECX с __this соглашения о вызовах. Это то, что вы видите, что делает версия Detours.

Получение этого права довольно нетривиально, значения регистра ЦП должны быть сохранены рано, в частности, ECX. Для этого требуется заглушка, в которой используется машинный код, конечно же, без машинного кода в управляемой заглушке. Я сомневаюсь, что EasyHook имеет какую-либо поддержку, это, конечно, не обещано в списке функций.

0 голосов
/ 05 декабря 2010

Похоже, я понял это. @ Ханс Пассант был прав: я должен сохранить скрытый аргумент 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);

Не могу реально объяснить, почему это работает (также я думаю, что я понял большую часть этого :) Я действительно должен вернуться и изучить еще немного теории ассемблера / компиляции.

...