stevemk14ebr / PolyHook_2_0

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

Calling the original function fails after hooking the same function twice or more #177

Open fligger opened 1 year ago

fligger commented 1 year ago

Hey, this might be a linux specific problem but whenever I hook a function, unhook it and hook it again the process will crash when calling the original trampoline function. This is some pseudo code that will generate the following output:

PLH::x64Detour *getFov_hook = nullptr;
uint64_t getFov_addr;

float my_getFov(class LevelRendererPlayer *base, float param_1, bool flag)
{
    return PLH::FnCast(getFov_addr, my_getFov)(base, param_1, flag);  // this will crash the process after the second hook call
}

int main() {

  getFov_hook = new PLH::x64Detour((uint64_t)base + 0x060f5930, (uint64_t)my_getFov, &getFov_addr);

    auto error_log = std::make_shared<PLH::ErrorLog>();
    PLH::Log::registerLogger(error_log);

    getFov_hook->hook(); // trampoline works without error till this point
        // do some stuff 
    getFov_hook->unHook(); // unhook after a few seconds
    getFov_hook->hook(); // rehook after a few seconds later 

}

The code will output this.

[+] Info: Original function:
7f2f2a275930 [2]: 41 57                                   push r15
7f2f2a275932 [2]: 41 56                                   push r14
7f2f2a275934 [1]: 53                                      push rbx
7f2f2a275935 [4]: 48 83 ec 30                             sub rsp, 0x30
7f2f2a275939 [6]: f3 0f 11 44 24 0c                       movss dword ptr ss:[rsp+0x0C], xmm0
7f2f2a27593f [3]: 49 89 ff                                mov r15, rdi
7f2f2a275942 [3]: 40 84 f6                                test sil, sil
7f2f2a275945 [6]: 0f 84 94 00 00 00                       jz 0x00007F2F2A2759DF -> 7f2f2a2759df
7f2f2a27594b [7]: 49 8b 87 68 15 00 00                    mov rax, qword ptr ds:[r15+0x1568]
7f2f2a275952 [7]: 48 8b b8 d8 02 00 00                    mov rdi, qword ptr ds:[rax+0x2D8]
7f2f2a275959 [5]: be 22 00 00 00                          mov esi, 0x22
7f2f2a27595e [5]: e8 4d 68 0e fe                          call 0x00007F2F2835C1B0 -> 7f2f2835c1b0
7f2f2a275963 [3]: 48 85 c0                                test rax, rax
7f2f2a275966 [6]: 0f 84 d3 01 00 00                       jz 0x00007F2F2A275B3F -> 7f2f2a275b3f
7f2f2a27596c [3]: 48 89 c3                                mov rbx, rax
7f2f2a27596f [3]: 48 89 c7                                mov rdi, rax
7f2f2a275972 [5]: e8 29 c7 0e fe                          call 0x00007F2F283620A0 -> 7f2f283620a0
7f2f2a275977 [5]: 0f 29 44 24 10                          movaps xmmword ptr ss:[rsp+0x10], xmm0
7f2f2a27597c [3]: 48 89 df                                mov rdi, rbx
7f2f2a27597f [5]: e8 1c cd 19 fe                          call 0x00007F2F284126A0 -> 7f2f284126a0
7f2f2a275984 [5]: 0f 29 44 24 20                          movaps xmmword ptr ss:[rsp+0x20], xmm0
7f2f2a275989 [3]: 48 89 df                                mov rdi, rbx
7f2f2a27598c [5]: e8 ff cc 19 fe                          call 0x00007F2F28412690 -> 7f2f28412690

[+] Info: Prologue to overwrite:
7f2f2a275930 [2]: 41 57                                   push r15
7f2f2a275932 [2]: 41 56                                   push r14
7f2f2a275934 [1]: 53                                      push rbx
7f2f2a275935 [4]: 48 83 ec 30                             sub rsp, 0x30

[+] Info: Trampoline address: 0x0000000005e93270
[+] Info: Jmp To Prol:
5e93279 [6]: ff 25 49 00 00 00                       jmp [5e932c8] ->7f2f2a275939
5e932c8 [8]: 39 59 27 2a 2f 7f 00 00                 dest holder 

[+] Info: m_trampoline: 0x0000000005e93270

[+] Info: m_trampolineSz: 0x0064

[+] Info: Trampoline:
5e93270 [2]: 41 57                                   push r15
5e93272 [2]: 41 56                                   push r14
5e93274 [1]: 53                                      push rbx
5e93275 [4]: 48 83 ec 30                             sub rsp, 0x30
5e93279 [6]: ff 25 49 00 00 00                       jmp qword ptr ds:[0x0000000005E932C8] -> 7f2f2a275939

[+] Info: Hook instructions: 
7f2f2a275930 [6]: ff 25 ca 56 eb 31                       jmp [7f2f5c12b000] ->7f2ed44e8ad8
7f2f5c12b000 [8]: d8 8a 4e d4 2e 7f 00 00                 dest holder 

[+] Info: Hook size: 9

[+] Info: Prologue offset: 6

**First hook done**
**Second hook started**

[+] Info: Original function:
7f2f2a275930 [2]: 41 57                                   push r15
7f2f2a275932 [2]: 41 56                                   push r14
7f2f2a275934 [1]: 53                                      push rbx
7f2f2a275935 [4]: 48 83 ec 30                             sub rsp, 0x30
7f2f2a275939 [6]: f3 0f 11 44 24 0c                       movss dword ptr ss:[rsp+0x0C], xmm0
7f2f2a27593f [3]: 49 89 ff                                mov r15, rdi
7f2f2a275942 [3]: 40 84 f6                                test sil, sil
7f2f2a275945 [6]: 0f 84 94 00 00 00                       jz 0x00007F2F2A2759DF -> 7f2f2a2759df
7f2f2a27594b [7]: 49 8b 87 68 15 00 00                    mov rax, qword ptr ds:[r15+0x1568]
7f2f2a275952 [7]: 48 8b b8 d8 02 00 00                    mov rdi, qword ptr ds:[rax+0x2D8]
7f2f2a275959 [5]: be 22 00 00 00                          mov esi, 0x22
7f2f2a27595e [5]: e8 4d 68 0e fe                          call 0x00007F2F2835C1B0 -> 7f2f2835c1b0
7f2f2a275963 [3]: 48 85 c0                                test rax, rax
7f2f2a275966 [6]: 0f 84 d3 01 00 00                       jz 0x00007F2F2A275B3F -> 7f2f2a275b3f
7f2f2a27596c [3]: 48 89 c3                                mov rbx, rax
7f2f2a27596f [3]: 48 89 c7                                mov rdi, rax
7f2f2a275972 [5]: e8 29 c7 0e fe                          call 0x00007F2F283620A0 -> 7f2f283620a0
7f2f2a275977 [5]: 0f 29 44 24 10                          movaps xmmword ptr ss:[rsp+0x10], xmm0
7f2f2a27597c [3]: 48 89 df                                mov rdi, rbx
7f2f2a27597f [5]: e8 1c cd 19 fe                          call 0x00007F2F284126A0 -> 7f2f284126a0
7f2f2a275984 [5]: 0f 29 44 24 20                          movaps xmmword ptr ss:[rsp+0x20], xmm0
7f2f2a275989 [3]: 48 89 df                                mov rdi, rbx
7f2f2a27598c [5]: e8 ff cc 19 fe                          call 0x00007F2F28412690 -> 7f2f28412690

[+] Info: Prologue to overwrite:
7f2f2a275930 [2]: 41 57                                   push r15
7f2f2a275932 [2]: 41 56                                   push r14
7f2f2a275934 [1]: 53                                      push rbx
7f2f2a275935 [4]: 48 83 ec 30                             sub rsp, 0x30

[+] Info: Trampoline address: 0x0000000005e94030
[+] Info: Jmp To Prol:
5e94039 [6]: ff 25 49 00 00 00                       jmp [5e94088] ->7f2f2a275939
5e94088 [8]: 39 59 27 2a 2f 7f 00 00                 dest holder 

[+] Info: m_trampoline: 0x0000000005e94030

[+] Info: m_trampolineSz: 0x0064

[+] Info: Trampoline:
5e94030 [2]: 41 57                                   push r15
5e94032 [2]: 41 56                                   push r14
5e94034 [1]: 53                                      push rbx
5e94035 [4]: 48 83 ec 30                             sub rsp, 0x30
5e94039 [6]: ff 25 49 00 00 00                       jmp qword ptr ds:[0x0000000005E94088] -> 7f2f2a275939

[+] Info: Hook instructions: 
7f2f2a275930 [6]: ff 25 ca 56 eb 31                       jmp [7f2f5c12b000] ->7f2ed44e8ad8
7f2f5c12b000 [8]: d8 8a 4e d4 2e 7f 00 00                 dest holder 

[+] Info: Hook size: 9

[+] Info: Prologue offset: 6

After "rehooking" the detour will work fine but calling the tram function will crash the process. The Trampoline (with m_trampoline) is in a completely diffrent location and it's instructions wont ever get called.

0x5e94030 is somewhere close the entry process/executable. The function i hook is in an libary allocated far away from that address.

I would love to know why this happends. Sometimes "rehooking" the function works fine but most of the time it will crash the process. I Never actively encountered this error on the first hook. After using dlclose and dlopen again (to uninject and inject my custom code again) the hook will work fine again.

stevemk14ebr commented 1 year ago

Can you create a self contained reproduction case using like a winapi call as the hook target? I'd have to debug your target at the assembly level to figure out the specific issue. I've just tried this on CreateMutexA myself and it worked fine

fligger commented 1 year ago

Hey sorry for the late reply.

I tried to recreate my error in a controlled environment but just cannot do it. I tried for multiple hours. I was hoping my initial log would raise some eyebrows. The code below is closer to my actual setup but it still isn't reliably crashing my game :).

PLH::x64Detour *glfwSwapBuffers_hook = nullptr;
uint64_t glfwSwapBuffers_addr;

PLH::x64Detour *getFov_hook = nullptr;
uint64_t getFov_addr;

std::thread *thread;

void my_glfwSwapBuffers(void *window)
{
    static bool setup = false;
    if (!setup)
    {
        getFov_hook->unHook(); //normally executed by a module manager 
        // wait some time
        getFov_hook->hook(); //normally executed by a module manager 
        false = true;
    }

    PLH::FnCast(glfwSwapBuffers_addr, my_glfwSwapBuffers)(window);
}

std::string my_getFov(class LevelRendererPlayer *base, float param_1, bool flag)
{

    return PLH::FnCast(getFov_addr, my_getFov)(base, param_1, flag);
}

void main()
{
    getFov_hook = new PLH::x64Detour((uint64_t)0x7fc456275930, (uint64_t)my_getFov, &getFov_addr); 

    glfwSwapBuffers_hook = new PLH::x64Detour((uint64_t)0x79e6c0, (uint64_t)my_glfwSwapBuffers, &glfwSwapBuffers_addr); 

    auto error_log = std::make_shared<PLH::ErrorLog>();
    PLH::Log::registerLogger(error_log);

    getFov_hook->hook();
    glfwSwapBuffers_hook->hook();
}

I found out a few things though.

The first hook didn't crash my game as it was activated by an independent thread (sth like the main function). The hooks after that have been activated inside of a hooked function (glfwSwapBuffers) which causes the Trampoline to get generated in the wrong place. [Speculation: it looks like the trampoline gets generated close to the location of glfwSwapBuffers (maybe because it is generated while in glfwSwapBuffers?) This is really far away from the actual getFov method and we cannot jump there or sth like that. Could this make sence? :-|] glfwSwapBuffers is part of the entry process while getfov is part of a .so libary. Those distances seem to be normal.

if I wrap the hook and unhook method in their own thread the problem disappears and the process doesn't crash. like this:

void test()
{
    getFov_hook->unHook();
    getFov_hook->hook();
}

void my_glfwSwapBuffers(void *window) // replace 
{
    static bool setup = false;
    if (!setup)
    {
    std::thread t1(test);
    t1.join();
    }
    PLH::FnCast(glfwSwapBuffers_addr, my_glfwSwapBuffers)(window);
}

Any new ideas or comments?