cwindowsassemblyhookhotpatching

Hooking - hotpatching


I'm trying to hook the Windows API function FindWindowA(). I successfully did it with the code below without "hotpatching" it: I've overwritten the bytes at the beginning of the function. myHook() is called and a message box shows up when FindWindowA() is called.

user32.dll has hotpatching enabled and I'd like to overwrite the NOPs before the actual function instead of overwriting the function itself. However, the code below won't work when I set hotpatching to TRUE. It does nothing when FindWindowA() gets executed.

#include <stdio.h>
#include <windows.h>

void myHook()
{
    MessageBoxA(NULL, "Hooked", "Hook", MB_ICONINFORMATION);
}

int main(int argc, char *argv[])
{
    BOOLEAN hotpatching = FALSE;

    LPVOID fwAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "FindWindowA");
    LPVOID fwHotpatchingAddress = (LPVOID)((DWORD)fwAddress - 5);
    LPVOID myHookAddress = &myHook;

    DWORD jmpOffset = (DWORD)&myHook - (DWORD)(!hotpatching ? fwAddress : fwHotpatchingAddress) - 5; // -5 because "JMP offset" = 5 bytes (1 + 4)

    printf("fwAddress: %X\n", fwAddress);
    printf("fwHotpatchingAddress: %X\n", fwHotpatchingAddress);
    printf("myHookAddress: %X\n", myHookAddress);
    printf("jmpOffset: %X\n", jmpOffset);
    printf("Ready?\n\n");
    getchar();


    char JMP[1] = {0xE9};
    char RETN[1] = {0xC3};

    LPVOID offset0 = NULL;
    LPVOID offset1 = NULL;
    LPVOID offset2 = NULL;

    if (!hotpatching)
        offset0 = fwAddress;
    else
        offset0 = fwHotpatchingAddress;

    offset1 = (LPVOID)((DWORD)offset0 + 1);
    offset2 = (LPVOID)((DWORD)offset1 + 4);


    DWORD oldProtect = 0;
    VirtualProtect(offset0, 6, PAGE_EXECUTE_READWRITE, &oldProtect);

    memcpy(fwAddress, JMP, 1);
    memcpy(offset1, &jmpOffset, 4);
    memcpy(offset2, RETN, 1);

    VirtualProtect(offset0, 6, oldProtect, &oldProtect);


    printf("FindWindowA() Patched");
    getchar();


    FindWindowA(NULL, "Test");
    getchar();


    return 0;
}

Could you tell me what's wrong?

Thank you.


Solution

  • Hotpatching enabled executable images are prepared by the compiler and linker to allow replacing the image while in use. The following two changes are applied (x86):

    1. The function entry point is set to a 2-byte no-op mov edi, edi (/hotpatch).
    2. Five consecutive nop's are prepended to each function entry point (/FUNCTIONPADMIN).

    To illustrate this, here is a typical disassembly listing of a hotpaching enabled function:

    (2) 768C8D66 90                   nop  
        768C8D67 90                   nop  
        768C8D68 90                   nop  
        768C8D69 90                   nop  
        768C8D6A 90                   nop  
    (1) 768C8D6B 8B FF                mov         edi,edi
    (3) 768C8D6D 55                   push        ebp  
        768C8D6E 8B EC                mov         ebp,esp  
    

    (1) designates the function entry point with the 2-byte no-op. (2) is the padding provided by the linker, and (3) is where the non-trivial function implementation starts.

    To hook into a function you have to overwrite (2) with a jump to your hook function jmp myHook, and make this code reachable by replacing (1) with a relative jump jmp $-5.

    The hook function must leave the stack in a consistent state. It should be declared as __declspec(naked) to prevent the compiler from generating function prolog and epilog code. The final instruction must either perform stack cleanup in line with the calling convention of the hooked function, or jump back to the hooked function at the address designated by (3).