stevemk14ebr / PolyHook_2_0

C++20, x86/x64 Hooking Libary v2.0
MIT License
1.58k stars 222 forks source link

Detours getting automatically removed #160

Closed stenlan closed 1 year ago

stenlan commented 1 year ago

I have this really strange issue. Here is my DLL code:

#include "ExampleProject.h"
#include "polyhook2/Detour/x86Detour.hpp"

#pragma comment(lib, "winhttp.lib")

#include <cstdarg>

typedef BOOL(__stdcall* tWinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* pProxyConfig);
uint64_t oWinHttpGetIEProxyConfigForCurrentUser = NULL;
tWinHttpGetIEProxyConfigForCurrentUser pWinHttpGetIEProxyConfigForCurrentUser = (tWinHttpGetIEProxyConfigForCurrentUser) GetProcAddress(GetModuleHandleA("winhttp.dll"), "WinHttpGetIEProxyConfigForCurrentUser");

tWinHttpGetIEProxyConfigForCurrentUser hkWinHttpGetIEProxyConfigForCurrentUser(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* pProxyConfig) {
    printf("called!");
    return false;
}

BOOL APIENTRY DllMain(
    HANDLE hModule,    // Handle to DLL module 
    DWORD ul_reason_for_call,
    LPVOID lpReserved)     // Reserved
{
    printf("pointer: %p", pWinHttpGetIEProxyConfigForCurrentUser);
    // PLH::x86Detour detour = PLH::x86Detour((uint64_t)&printf, (uint64_t)&h_hookPrintf, &hookPrintfTramp);
    PLH::x86Detour detour = PLH::x86Detour((uint64_t)pWinHttpGetIEProxyConfigForCurrentUser, (uint64_t)hkWinHttpGetIEProxyConfigForCurrentUser, &oWinHttpGetIEProxyConfigForCurrentUser);

    printf("hooked? %d", detour.hook());

    WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ProxySettings;
    SecureZeroMemory(&ProxySettings, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG));
    bool res = WinHttpGetIEProxyConfigForCurrentUser(&ProxySettings);

    printf("proxy? %d", res);
    // printf("%s %f\n", "hi", .5f);
    // detour.unHook();

    return TRUE;
}

I then inject this DLL into a process that prints the proxy settings in a loop. The bizarre thing is that only the one call I make inside the DLL is actually detoured, the call that the process makes is not at all detoured. I have also tried a direct reference to the function instead of GetProcAddress but it isn't working. Even the printf example isn't working. I have turned off all optimizations but that does not help either. Am I going insane? How are multiple copies of the same function from a dll loaded in memory?

stenlan commented 1 year ago

Through some debugging and looking at the actual memory I have found out that during the one call I make inside the dll it's actually detoured and the memory has been changed, but then the bytes just change back for no apparent reason? I never call unhook or anything. What is going on? I feel like I'm missing something extremely obvious but I have absolutely 0 idea what that could be.

stevemk14ebr commented 1 year ago

If the bytes actually change then it's possible there's another thread that's working as a watchdog for hooks and restoring them. You can try to set a hardware write breakpoint on the bytes after the hook is written to break when they get restored. That should tell you at least which chunk of code is doing the restoring.

Polyhook2 does support extremely fast re-hookimg to combat this. Once you find the logic doing the restore you can either nop it out, or call reHook super fast in a loop constantly

stenlan commented 1 year ago

@stevemk14ebr

That's not the case, this is an extremely simple test application I have written myself. This must be a bug somewhere (maybe my code) but I can't see why. At least it doesn't seem like desired behavior. Here's the code for the application I'm injecting into:

#include <windows.h>
#include <winhttp.h>
#include <iostream>
#include <chrono>
#include <thread>

#pragma comment(lib, "winhttp.lib")

int main()
{
    while (true) {
        WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ProxySettings;
        SecureZeroMemory(&ProxySettings, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG));
        bool res = WinHttpGetIEProxyConfigForCurrentUser(&ProxySettings);

        std::printf("%d", res);
        std::printf("Proxy: ");

        if (res && ProxySettings.lpszProxy)
        {
            std::wprintf((wchar_t*) ProxySettings.lpszProxy);

            GlobalFree(ProxySettings.lpszProxy);
        }

        std::printf("\n");

        if (ProxySettings.lpszProxyBypass)
            GlobalFree(ProxySettings.lpszProxyBypass);

        if (ProxySettings.lpszAutoConfigUrl)
            GlobalFree(ProxySettings.lpszAutoConfigUrl);

        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

If you want I could send both binaries somewhere. I feel like I'm going crazy here.

stevemk14ebr commented 1 year ago

Yea if you can send a debug build lf both binaries and their PDBs it shouldn't be too hard for me to figure out what's happening. Share whatever your injector you're using too so I can reproduce exactly. Just a zip archive will work

Edit: I see a potential issue, move the x86detour to a global. It calls unhook in the class destructor

stenlan commented 1 year ago

Edit: I see a potential issue, move the x86detour to a global. It calls unhook in the class destructor

That does the trick, thanks! Thank you so much for this library! How should I have known this was the case? Is it expected that I look through the source code or is this some common sense that I am missing?

stevemk14ebr commented 1 year ago

Edit: I see a potential issue, move the x86detour to a global. It calls unhook in the class destructor

That does the trick, thanks! Thank you so much for this library! How should I have known this was the case? Is it expected that I look through the source code or is this some common sense that I am missing?

I have some docs written that do explain this, but honestly it's quite a long read and totally understandable you missed it. The next best choice would be to look into the libraries code and see how its constructors and destructors work since in C++ RAII patterns like this are common. In this case the idea is the x86Detour owns the 'concept' of the hook, so when the class is destroyed so too should be the hook it owns, therefore it should restore things to what they were before it was constructed. That could be guessed by being an experienced c++ dev. In either case, this is just a documentation problem on my part.

https://stevemk14ebr.github.io/PolyHook_2_0/

stenlan commented 1 year ago

I have some docs written that do explain this, but honestly it's quite a long read and totally understandable you missed it. The next best choice would be to look into the libraries code and see how its constructors and destructors work since in C++ RAII patterns like this are common. In this case the idea is the x86Detour owns the 'concept' of the hook, so when the class is destroyed so too should be the hook it owns, therefore it should restore things to what they were before it was constructed. That could be guessed by being an experienced c++ dev. In either case, this is just a documentation problem on my part.

Thank you! Sorry for the bother. If you have the time: what is a good way to dynamically create the hooks and not have them be destructed in this case? Let's say I first need to run code before I know the address of the function I want to hook? Because it seems I cannot just put PLH::x86Detour detour; somewhere then later on assign to it, it tells me that the assignment operator is a deleted function. As you might have guessed I am not an experienced c++ developer. I do appreciate all the help!

stevemk14ebr commented 1 year ago

To delay construction you should use a pointer type. Since we're in c++ you should use a unique_ptr since the detour represents a unique object owning the concept of a single hook. Something like this:

std::unique_ptr<PLH::x86Detour> myDetour;
void main() {
    myDetours = std::make_unique<PLH::x86Detour>(...detours ctor args...)
    myDetours->hook();
}
stenlan commented 1 year ago

Thank you so much! That's all! <3