takeiteasy 发表于 2015-4-22 09:42:40

使用Detours对LoadLibraryExW进行HOOK屏蔽全局钩子

全局钩子都是做为DLL挂接注入进程,假如应用程序A.exe安装了WH_GETMESSAGE的全局钩子,钩子函数在B.dll中,那么当其它程序在调用GetMessage函数从自己的消息队列中取消息的时候,系统发现程序A安装了WH_GETMESSAGE的全局钩子,就会检查调用GetMessage的进程是否加载了B.dll,如果没有,就调用LoadLibrary进行加载,然后调用B.dll中的钩子过程。这样,钩子dll就会在所有调用GetMessage的进程中加载。
如果程序本身能够对LoadLibrary进行处理,就能够防范其他DLL模块的注入。这里未经任何判断直接返回NULL,有点武断,会影响系统效率。
PS:上面这句话说了等于白说,现在谁还用这种方式啊,都是自己解析加载,要不就走LDR那套,这些都是老的不能再老的东西了,不过这里作为基础学习,还是粘下代码作为备忘了。
#include <Windows.h>
#include <stdio.h>
#include "detours.h"

#pragma comment(lib, "detours.lib")
#pragma warning(disable : 4098)

static HMODULE (WINAPI* _LoadLibraryExW)(LPCTSTR lpFileName,HANDLE hFile,DWORD dwFlags) = LoadLibraryExW;

VOID OutputDebugStringExW(const wchar_t* szFormat, ...)
{
    WCHAR Buffer = {0};

    va_list argList;
    va_start(argList, szFormat);
    vswprintf(Buffer, sizeof(Buffer), szFormat, argList);
    va_end(argList);

    OutputDebugStringW(Buffer);
}

HMODULE WINAPI NEW_LoadLibraryExW(LPCTSTR lpFileName,HANDLE hFile,DWORD dwFlags)
{
    OutputDebugStringExW(L"%s", lpFileName);
    return NULL;
    //return _LoadLibraryExW(lpFileName, hFile, dwFlags);
}

VOID Hook()
{
    DetourRestoreAfterWith();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());

    DetourAttach(&(PVOID&)_LoadLibraryExW, NEW_LoadLibraryExW ) ;

    DetourTransactionCommit();
}

VOID UnHook()
{
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());

    DetourDetach(&(PVOID&)_LoadLibraryExW, NEW_LoadLibraryExW );

    DetourTransactionCommit();
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORDul_reason_for_call, LPVOID lpReserved)
{
    if (ul_reason_for_call == DLL_PROCESS_ATTACH)
    {   
      Hook();   
    }   
    else if (ul_reason_for_call == DLL_PROCESS_DETACH)   
    {   
      UnHook();
    }   
    return TRUE;
}

以上是自己的DLL,如果需要对某个进程进行保护,只需要插入相关进程就好。当然相关进程如果不想麻烦,可以直接把HOOK过程写入程序代码。


经过调试发现,正常的加载dll函数调用都是从kernel32.dll中来的,而只有加载钩子过程是在user32.dll中进行的。可以对上面的函数进行修改,判断LoadLibrary函数的返回地址,如果是在user32.dll的地址空间,就认为是钩子dll的加载,直接返回NULL就可以了。
首先获取user32.dll的基地址
static DWORD m_dwUser32Low;    //user32.dll 的加载基址
static DWORD m_dwUser32Hi;   //user32.dll 的加载基址+ImageSize

MODULEINFO user32ModInfo = {0};

//获取user32.dll的加载基址和映象大小   
GetModuleInformation(GetCurrentProcess(), GetModuleHandle("user32.dll"),   
                     &user32ModInfo, sizeof(user32ModInfo));
m_dwUser32Low = (DWORD)user32ModInfo.lpBaseOfDll;
m_dwUser32Hi = (DWORD)user32ModInfo.lpBaseOfDll+user32ModInfo.SizeOfImage;

下面进行判断,如果来自user32.dll的调用直接进行返回。
HMODULE WINAPI NEW_LoadLibraryExW(LPCTSTR lpFileName,HANDLE hFile,DWORD dwFlags)
{
    //获取函数的返回地址
    DWORD dwCaller;
    __asm push dword ptr
    __asm popdword ptr    

    //判断是否是从User32.dll调用的
    if(dwCaller > m_dwUser32Low && dwCaller < m_dwUser32Hi)
    {
      OutputDebugStringExW(L"%s", lpFileName);
      return NULL;
    }
      
    return _LoadLibraryExW(lpFileName, hFile, dwFlags);
}

PS:函数在调用的时候,先要把参数入栈,然后把返回地址入栈,esp指向的应该就是函数的返回地址了。但是为了返回函数时恢复原来的栈和在函数中方便引用传递的参数,编译器一般都会产生两条指令。
push ebp
mov ebp,esp
先把ebp入栈,把原来的esp保存在ebp寄存器中,这样,我们的返回地址就是,第一个参数是,第二个是。
页: [1]
查看完整版本: 使用Detours对LoadLibraryExW进行HOOK屏蔽全局钩子