Search code examples
c++windowshookdll-injection

Single interface for any hooked function


I'm writing a simple Dll injection and hooking program, and everything was fine when I manually declared functions to hook like this for CreateFileA:

HANDLE WINAPI Hook_CreateFileA(
    LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile
)
{
    ...

    return CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
        dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}

But now I need to write a function that handles any function from kernel32.dll, for example. It means that I don't know anything about function except its name and hence an address.

I know a little bit about calling convention - function parameters are pushed in straight order (__stdcall) and then the function is called. I tried to write a __declspec(naked) function that looks like this:

PVOID __declspec(naked) HookAnyFunction()
    {
        /* do something */

        __asm {
            mov ebx, [esp]
            add esp, 4
            call pfnFuncAddr
            sub esp, 4
            mov[esp], ebx
            ret
        }
    }

pfnFuncAddr - is an address of the original function. But it crashes an application with injected Dll. I guess my code corrupts stack or something. What am I doing wrong? Hope my explanation makes sense.


Solution

  • ebx is not volatile, you cannot just write to it, you have to save/restore the original value.

    Writing a generic hook function is going to be hard because the 32-bit Windows ABI has 3 calling conventions; stdcall (callee cleans the stack), cdecl (caller cleans the stack) and fastcall (first two parameters in registers but it is not used much in the public API).

    If you just want to log the function calls you could probably do something like this:

    push esp
    push pfnFuncAddr
    call mylogger ; assumed to be stdcall in this case, it can also change the jump
    jmp eax
    

    and FARPROC __stdcall mylogger(FARPROC function, SIZE_T stackaddress) { ...; return function; } (remember that esp has changed so you have to adjust the stack parameter by 8+4 bytes if you want to log the contents of the stack.) If you care about fastcall you also have to push the registers but there is no way for a generic logger to know if the first parameters are on the stack or in registers.