0x00 原理说明:
Windows系统基于消息驱动,每个进程都有各自的消息队列,每个进程都会GetMessage
调用WIN32API SetWindowsHookEx 可以在系统的钩子链中安装一个指定的钩子
如果使用 SetWindowsHookEx 安装 WH_GETMESSAGE 类型的钩子,并且钩子过程函数放在DLL中,就可以实现全局的DLL注入
0x01 关键API说明:
实现全局钩子注入DLL最核心的API:
HHOOK WINAPI SetWindowsHookEx( _In_ int idHook, _In_ HOOKPROC lpfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadId );
参数说明:
1、 idHook:钩子类型,此处填写WH_GETMESSAGE
2、 lpfn:钩子的过程函数,也就是钩子触发的时候要执行的代码
3、 hMod:钩子过程函数所在的DLL模块句柄,在DLL初始化的获得(DllMain的第一个参数)
4、 dwThreadId:与钩子程序关联的线程ID,此处填0,表示关联系统中的所有线程
返回值:
钩子句柄
0x02 编写需要注入的DLL:
DLL大致需要实现以下内容:
1、 设置钩子
2、 取消钩子
3、 钩子过程函数
4、 导出相关函数
DLL的主要实现代码如下:
#include "pch.h"
HHOOK g_hHook;
HMODULE g_hModule;LRESULT CALLBACK GetMsgProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
return CallNextHookEx(g_hHook, code, wParam, lParam);
}BOOL LoadHook(void)
{
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hModule, 0);
if (g_hHook){
MessageBox(NULL, TEXT("钩子加载成功!"), TEXT("提示"), MB_OK);
return TRUE;
}
else
return FALSE;
}VOID UnloadHook(void)
{
if(g_hHook)
UnhookWindowsHookEx(g_hHook);
}BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
MessageBox(NULL, TEXT("加载DLL!"), TEXT("提示"), MB_OK);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
新建def文件导出相关函数:
LIBRARY
EXPORTS
LoadHook
UnloadHook
编译生成 TestDLL.dll 文件
0x03 编写调用程序:
DLL不能主动执行,因此需要编写调用程序:
#include
#include#define DLL_NAME "TestDLL.dll"
int main(int argc, char* argv[])
{
do{
HMODULE hModule = LoadLibraryA(DLL_NAME);
if(hModule == NULL)
break;
FARPROC pfnLoadHook = GetProcAddress(hModule, "LoadHook");
FARPROC pfnUnloadHook = GetProcAddress(hModule, "UnloadHook");
if(pfnLoadHook==NULL || pfnUnloadHook==NULL)
break;
if (pfnLoadHook())
printf("全局钩子加载成功!\r\n");
else{
printf("全局钩子加载失败!\r\n");
break;
}
printf("按任意键卸载全局钩子!\r\n");
getchar();
pfnUnloadHook();
printf("全局钩子卸载完成!\r\n");
}while(FALSE);getchar();
return 0;
}
注:为了简化出错处理,使用了 do-while(0) 结构
0x04 验证:
先打开PCHunter查看:
可以看到此时系统应用层没有任何全局消息钩子
为了方便观察,打开系统自带的Notepad,并使用调试器附加:
将编译生成 TestDLL.dll 文件和调用程序放在同一路径下,并且执行调用程序:
可以发现已经成功将DLL注入到了当前系统的很多进程当中,用调试器附加的Notepad进程可以非常直观的看到结果
同时在PCHunter中也可以直观的看到结果: