TsudaKageyu / minhook

The Minimalistic x86/x64 API Hooking Library for Windows
http://www.codeproject.com/KB/winsdk/LibMinHook.aspx
Other
4.43k stars 897 forks source link

(VisualStudio 15/17) dllimport, calls are not resolved correctly #60

Closed jarlostensen closed 3 years ago

jarlostensen commented 6 years ago

Minhook correctly patches the location of a call to a function, Foo, when you get it using GetProcAddress, but if functions are dllimport'ed and called from your code the compiler will have inserted an indirect call directly to the "__imp_Foo" location, bypassing the trampolined section. I haven't traced through the guts of Minhook to completely understand how it decides where to patch, but from what I could see it appears not to traverse the initial jmp, to patch the actual target. Is that intentional?

I just want to make sure I'm not misunderstanding how to use Minhook here, so here's an example, with a "fix", which is an extended version of GetProcAddress, that will traverse an indirect jmp to the actual target location, before hooking it, to demonstrate that the hooking then works.


#include <windows.h>
#include "minhook-1.3.3/include/MinHook.h"

typedef BOOL(WINAPI *ExtTextOutAProcA)(HDC hdc, int X, int Y, UINT fuOptions, const RECT *lprc, LPCTSTR lpString, UINT cbCount, const INT *lpDx);

BOOL WINAPI ExtTextOutAProcAHooked(HDC hdc, int X, int Y, UINT fuOptions, const RECT *lprc, LPCTSTR lpString, UINT cbCount, const INT *lpDx)
{
 // set a breakpoint here!
    return FALSE;
}

FARPROC GetProcAddressEx(HINSTANCE module, LPCSTR name)
{
    auto org = GetProcAddress(module, name);
    if(!org)
        return org;

    auto                 target  = reinterpret_cast<const uint8_t *>(org);
    static const uint8_t jmprm[] = { 0xff, 0x25 };
    if(!memcmp(target, jmprm, sizeof(jmprm)))
    {
        // address of target is at offset
#ifdef _M_X64
        // jmp + RIP relative offset to target address
        auto jmptarget = *reinterpret_cast<const uint64_t *>(uintptr_t(target) + 6 +
                                                             *reinterpret_cast<const uint32_t *>(target + 2));
#else
        // jmp + absolute offset to target address
        auto jmptarget = *reinterpret_cast<const uint32_t *>(*reinterpret_cast<const uint32_t *>(target + 2));
#endif
        org = FARPROC(jmptarget);
    }
    return org;
}

int main()
{
    auto gdi32full = LoadLibraryA("gdi32full.dll");
    //WORKS:
    auto proc = reinterpret_cast<ExtTextOutAProcA>(GetProcAddressEx(gdi32full, "ExtTextOutA"));
    //doesn't work... auto proc = reinterpret_cast<ExtTextOutAProcA>(GetProcAddress(gdi32full, "ExtTextOutA"));

    MH_Initialize();
    MH_CreateHook(reinterpret_cast<LPVOID>(proc), reinterpret_cast<const LPVOID>(ExtTextOutAProcAHooked), nullptr);
    MH_EnableHook(MH_ALL_HOOKS);
    ExtTextOutA(nullptr, 0, 0, 0, nullptr, "foo", 0, nullptr);
    MH_Uninitialize();
    ExtTextOutA(nullptr, 0, 0, 0, nullptr, "foo", 0, nullptr);
    return 0;
}```
m417z commented 6 years ago

from what I could see it appears not to traverse the initial jmp, to patch the actual target. Is that intentional?

Yes. See my reply here.

About the story with the dllimport, I didn't quite understand what the issue is. Can you upload a compiled executable (exe file) which demonstrates the issue?

jarlostensen commented 6 years ago

Hey, got it, and it makes perfect sense that Minhook shouldn't try to unpick the target address. I've zipped up the little test project, with a 64 bit exe you can look at. The call site in question looks like this; you can see that the compiler has inserted an indirect call via the import table, bypassing the jmp I got from GetProcAddress


ExtTextOutA(nullptr, 0, 0, 0, nullptr, "foo", 0, nullptr);
00007FF6D26D10CA 48 89 6C 24 38       mov         qword ptr [rsp+38h],rbp  
00007FF6D26D10CF 48 8D 3D 16 32 00 00 lea         rdi,[string "foo" (07FF6D26D42ECh)]  
00007FF6D26D10D6 89 6C 24 30          mov         dword ptr [rsp+30h],ebp  
00007FF6D26D10DA 45 33 C9             xor         r9d,r9d  
00007FF6D26D10DD 48 89 7C 24 28       mov         qword ptr [rsp+28h],rdi  
00007FF6D26D10E2 45 33 C0             xor         r8d,r8d  
00007FF6D26D10E5 33 D2                xor         edx,edx  
00007FF6D26D10E7 48 89 6C 24 20       mov         qword ptr [rsp+20h],rbp  
00007FF6D26D10EC 33 C9                xor         ecx,ecx  
00007FF6D26D10EE FF 15 0C 2F 00 00    call        qword ptr [__imp_ExtTextOutA (07FF6D26D4000h)] 

[HookTests.zip](https://github.com/TsudaKageyu/minhook/files/1553010/HookTests.zip)
m417z commented 6 years ago

Why are you loading gdi32full.dll and not gdi32.dll?

jarlostensen commented 6 years ago

That's for "reasons", I have to. I would have to try this, but I think it is the same for any dllimport'ed function, as it resolves to a call like that in all cases (regardless of where the DLL comes from)

m417z commented 6 years ago

I mean, maybe the issue that you're seeing has to do with the fact that GetProcAddress uses gdi32full.dll, but the import uses gdi32.dll. Can you upload an example which uses only system DLLs?