ramensoftware / windhawk

The customization marketplace for Windows programs: https://windhawk.net/
https://windhawk.net
GNU General Public License v3.0
1.06k stars 28 forks source link

Hooking certain functions in user32.dll in 32-bit processes crashes the process (under 64-bit OS). #201

Closed levitation closed 2 weeks ago

levitation commented 2 weeks ago

I have successfully hooked functions using Windhawk in user32.dll before. Now I have encountered two functions which cause crashes when they are hooked, even when the hook function simply calls the original function only. The crash occurs only in 32-bit processes, 64-bit processes work as intended.

I have tested this in a 64-bit OS, did not test with a 32-bit OS. I have verified the function signatures.

The two problematic hooked functions are GetMonitorInfoW and SystemParametersInfoW. If either of them is hooked then crash will occur if the 32-bit application calls them. For example, devenv.exe cannot be started if either of these functions is hooked. And if devenv.exe is already running then it will crash when the mod is enabled.

I created a minimal mod code that reproduces the issue. The mod code follows. If you comment out the line if (bitness == 32) return FALSE; then you will encounter the crashes.

// ==WindhawkMod==
// @id              crash-32-bit-processes
// @name            Crash 32 bit processes
// @include         *
// @exclude         windhawk.exe
// @exclude         vscodium.exe
// ==/WindhawkMod==

#ifndef WH_MOD
#define WH_MOD
#include <mods_api.h>
#endif

typedef BOOL(WINAPI* GetMonitorInfo_t)(HMONITOR, LPMONITORINFO);
GetMonitorInfo_t pOriginalGetMonitorInfoW;
typedef BOOL(WINAPI* SystemParametersInfo_t)(UINT, UINT, PVOID, UINT);
SystemParametersInfo_t pOriginalSystemParametersInfoW;

BOOL GetMonitorInfoWHook(
    IN  HMONITOR      hMonitor,
    OUT LPMONITORINFO lpmi
) {
    BOOL result = pOriginalGetMonitorInfoW(
        hMonitor,
        lpmi
    );
    return result;
}

BOOL SystemParametersInfoWHook(
    IN      UINT  uiAction,
    IN      UINT  uiParam,
    IN OUT  PVOID pvParam,
    IN      UINT  fWinIni
) {
    BOOL result = pOriginalSystemParametersInfoW(
        uiAction,
        uiParam,
        pvParam,
        fWinIni
    );
    return result;
}

BOOL Wh_ModInit() {

    int bitness = sizeof(size_t) * 8;

    //comment out this line to see the crashes. For example, Visual Studio's devenv.exe will crash.
    if (bitness == 32) return FALSE;   //for some reason some user32.dll hooks do not work under 32-bit processes at the moment

    HMODULE hUser32 = GetModuleHandle(L"user32.dll");
    if (!hUser32) {
        Wh_Log(L"Loading user32.dll failed");
        return FALSE;
    }

    FARPROC pGetMonitorInfoW = GetProcAddress(hUser32, "GetMonitorInfoW");
    FARPROC pSystemParametersInfoW = GetProcAddress(hUser32, "SystemParametersInfoW");
    if (!pGetMonitorInfoW
        || !pSystemParametersInfoW
    ) {
        Wh_Log(L"Finding hookable functions from user32.dll failed");
        return FALSE;
    }

    Wh_SetFunctionHook((void*)pGetMonitorInfoW, (void*)GetMonitorInfoWHook, (void**)&pOriginalGetMonitorInfoW);
    Wh_SetFunctionHook((void*)pSystemParametersInfoW, (void*)SystemParametersInfoWHook, (void**)&pOriginalSystemParametersInfoW);

    return TRUE;
}
levitation commented 2 weeks ago

Resolved the issue: The hook functions lacked the WINAPI signature.

In the future I will use the strong typing snippet from https://github.com/ramensoftware/windhawk/issues/85 in order to avoid such mistakes.