您的位置:首页 > 其它

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

2012-11-12 19:21 453 查看
全局钩子都是做为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[2048] = {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, DWORD  ul_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 [ebp+4]
__asm pop  dword ptr [dwCaller]

//判断是否是从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寄存器中,这样,我们的返回地址就是[ebp+4],第一个参数是[ebp+8],第二个是[ebp+0xC]。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: