【问题标题】:API Hooking which takes effect across entire process - both EXE and DLLsAPI Hooking 在整个过程中生效 - EXE 和 DLL
【发布时间】:2011-06-28 03:26:18
【问题描述】:

我的应用程序由一个 EXE 和多个 DLL 组成。阅读Windows via C/C++ 后,我尝试在其中一个DLL 中对Sleep 函数执行挂钩,并期望挂钩将在EXE 和所有DLL 中工作。请注意,CAPIHook 代码来自 Windows via C/C++'s sample code

在 DLL 项目中

void WINAPI MySleep( DWORD dwMilliseconds );
CAPIHook g_Sleep("Kernel32.dll", "Sleep", (PROC)MySleep);
typedef void (WINAPI *Sleep_Type)( DWORD dwMilliseconds );

// Hook function.
void WINAPI MySleep( DWORD dwMilliseconds )
{
    printf ("-------> In MySleep\n");
    ((Sleep_Type)(PROC)g_Sleep)(dwMilliseconds);

}

// This is an example of an exported function.
DLL_API int dll_function_which_is_going_to_call_sleep(void)
{
    printf ("DLL function being called\n");
    printf ("Call Sleep in DLL function\n");
    Sleep(100);

    return 42;
}

在 EXE 项目中

void CexeDlg::OnBnClickedButton1()
{
    // TODO: Add your control notification handler code here
    printf ("Button being clicked\n");

    printf ("Call Sleep in EXE function\n");
    Sleep(100);

    dll_function_which_is_going_to_call_sleep();

    printf ("Call Sleep in EXE function\n");
    Sleep(100);

    dll_function_which_is_going_to_call_sleep();
}

这是我得到的输出

Button being clicked
Call Sleep in EXE function
-------> In MySleep
DLL function being called
Call Sleep in DLL function
Call Sleep in EXE function
-------> In MySleep
DLL function being called
Call Sleep in DLL function

让我感到奇怪的是,我期待CAPIHook 将在整个单个进程中生效。由于 EXE 和 DLL 属于同一个进程,因此两者都应该能够到达MySleep。但是,我的观察是,只有来自 EXE 的调用会到达MySleep,而不是 DLL。

我在这里找到示例代码CAPIHook-doesnt-have-effect-in-entire-process.zip,它包含dllexe 项目。

我也曾经将CHookAPI 替换为apihijack 中的代码。同样的问题仍然发生。挂钩效果不会扩散到整个流程。

有什么我错过的吗?请不要建议我使用 EasyHook、Detours 等,因为我只是想知道为什么上面的代码不起作用,以及如何修复它。

【问题讨论】:

    标签: c++ windows hook


    【解决方案1】:

    这是因为原始 CAPIHook 不会替换本地 IAT(在您的情况下,是包含 CAPIHook 二进制文件的 DLL 项目)。

    这样做的原因是为了保护自己免受导致 stackoverflow 的无限递归(用户也会在 SO 中发布问题:D)。

    为确保任何后续加载的模块都将导入 “正确” 函数,
    CAPIHook 搜索并重定向 LoadLibraryGetProcAddress 在构造时。

    但是,这些函数也被 CAPIHook 本身使用,因此将本地 IAT 更改为代理函数(CAPIHook::LoadLibrary 或 CAPIHook::GetProcAddress)将导致无限递归,因为代理在尝试调用底层 OS API 时无意中调用了自己!


    解决此问题的一种方法是修改 CAPIHook 以检查是否可以替换本地 IAT。

    1.) 将新属性 m_bIncludeLocalIAT 添加到 CAPIHook 并相应修改 ctor/dtor。

    class CAPIHook
    {
    ...
    CAPIHook(PSTR pszCalleeModName, PSTR pszFuncName, 
             PROC pfnHook, BOOL bIncludeLocalIAT = TRUE);
    ...
    BOOL m_bIncludeLocalIAT;
    ...
    };
    
    
    CAPIHook::CAPIHook( PSTR pszCalleeModName, PSTR pszFuncName, 
                        PROC pfnHook, BOOL bIncludeLocalIAT) {
        ...
        m_bIncludeLocalIAT = bIncludeLocalIAT;
        ...
        ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook, m_bIncludeLocalIAT);
    }
    
    CAPIHook::~CAPIHook() {
        ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig, m_bIncludeLocalIAT);
        ...
    }
    

    2.) 静态函数CAPIHook::ReplaceIAEntryInAllMods新增参数。

    static void WINAPI ReplaceIATEntryInAllMods(PCSTR pszCalleeModName, 
          PROC pfnOrig, PROC pfnHook, BOOL bReplaceLocalIAT){
    
       HMODULE hmodThisMod = ExcludeAPIHookMod 
          ? ModuleFromAddress(ReplaceIATEntryInAllMods) : NULL;
    
       // Get the list of modules in this process
       CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId());
    
       MODULEENTRY32 me = { sizeof(me) };
       for (BOOL bOk = th.ModuleFirst(&me); bOk; bOk = th.ModuleNext(&me)) {
    
          if (bReplaceLocalIAT || (me.hModule != hmodThisMod)) {
    
             // Hook this function in this module
             ReplaceIATEntryInOneMod(
                pszCalleeModName, pfnCurrent, pfnNew, me.hModule);
          }
       }
    }
    

    3.) 更新静态 CAPIHook 实例

    CAPIHook CAPIHook::sm_LoadLibraryA  ("Kernel32.dll", "LoadLibraryA",   
       (PROC) CAPIHook::LoadLibraryA, FALSE);
    
    CAPIHook CAPIHook::sm_LoadLibraryW  ("Kernel32.dll", "LoadLibraryW",   
       (PROC) CAPIHook::LoadLibraryW, FALSE);
    
    CAPIHook CAPIHook::sm_LoadLibraryExA("Kernel32.dll", "LoadLibraryExA", 
       (PROC) CAPIHook::LoadLibraryExA, FALSE);
    
    CAPIHook CAPIHook::sm_LoadLibraryExW("Kernel32.dll", "LoadLibraryExW", 
       (PROC) CAPIHook::LoadLibraryExW, FALSE);
    
    CAPIHook CAPIHook::sm_GetProcAddress("Kernel32.dll", "GetProcAddress", 
       (PROC) CAPIHook::GetProcAddress, FALSE);
    

    【讨论】:

    • 一个非常有用的解释为什么它不工作。竖起大拇指!
    猜你喜欢
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多