为啥使用函数指针调用函数会绕过钩子?

Posted

技术标签:

【中文标题】为啥使用函数指针调用函数会绕过钩子?【英文标题】:Why does using a function pointer to call a function bypass the hook?为什么使用函数指针调用函数会绕过钩子? 【发布时间】:2015-05-26 15:19:29 【问题描述】:

使用 Microsoft Detours 库,我编写了以下简单代码:

#include <Windows.h>
#include <detours.h>
#include <stdio.h>


void RealFunc(int num) 
    printf("RealFunc %d\n", num);


void(*RealFuncPtr)(int) = &RealFunc;

void HookedFunc(int num) 
    printf("HookedFunc %d\n", num + 100);
    // RealFunc(num); // This starts an infinite loop because it calls HookedFunc which calls RealFunc which calls HookedFunc etc...
    (*RealFuncPtr)(num); // This doesn't start an infinite loop and only calls RealFunc without calling HookedFunc. Why is this?


int main() 
    RealFunc(100);

    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach((PVOID*) &RealFuncPtr, &HookedFunc); //redirect RealFunc to HookedFunc

    if (DetourTransactionCommit() != NO_ERROR) 
        return 0;
    

    printf("Hook successful!\n");

    RealFunc(100);

    getchar(); // Pause console
    return 0;

这是输出:

RealFunc 100
Hook successful!
HookedFunc 200
RealFunc 100

如你所见,由于(*RealFuncPtr)(num); 这一行,真正的函数在钩子函数调用结束时被调用。但是,如果我注释掉(*RealFuncPtr)(num); 并取消注释RealFunc(num);,它似乎开始了一个无限循环。为什么只有在我使用RealFunc(num);时才会出现循环?

另外,由于某种原因,当我将项目设置为发布模式时,输出如下:

RealFunc 100
Hook successful!
RealFunc 100

钩子在发布模式下似乎不起作用。是我的 Visual Studio 配置还是我的代码有问题?

【问题讨论】:

【参考方案1】:

这就是 Detours 的工作原理。 DetourAttach 修改 RealFuncPtr 指向一些直接调用原始函数的蹦床代码,绕过钩子。如果需要,钩子需要这样做才能调用原始函数。

至于发布模式,编译器会内联您对 RealFunc 的调用,从而使钩子无用。您可以添加一个间接级别来绕过它,或者将编译器的 no-inline 属性应用于该函数。

【讨论】:

对于任何难以理解的人,Figure 1 解释了绕道如何很好地工作。【参考方案2】:

编译器或任何标准都不完全期望函数挂钩。

显然有一个绕过钩子的优化。关闭各种编译器优化,直到找到导致问题的那个。

否则,最好不要使用函数挂钩。

【讨论】:

谢谢。我不敢相信我忽略了这一点。

以上是关于为啥使用函数指针调用函数会绕过钩子?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 useEffect 钩子在第一次渲染时不调用函数?

回调函数和钩子函数的说明

钩子函数和回调函数的区别

回调函数和钩子函数

ucos里的hook函数是啥?为啥要有这些函数?干啥用的?

通过虚函数表调用虚函数与通过虚函数表(绕过访问权限控制)