进程注入之全局钩子注入


全局钩子注入

Windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数比如SetWindowsHookEx函数

,根据不同的消息完成不同的功能。

消息钩子是windows提供的一种消息过滤和预处理机制,可以用来截获和监视系统中的消息。

按照钩子作用范围不同,又可以分为局部钩子和全局钩子。局部钩子是针对某个线程的,而全局钩子是作用于整个系统的基于消息的应用。全局钩子需要使用DLL文件,在DLL文件中实现相应的钩子函数。

核心函数介绍

HHOOK WINAPI SetWindowsHookExW(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_opt_ HINSTANCE hmod,
_In_ DWORD dwThreadId);

1.idHook 要安装的钩子(HOOK)的类型,它决定了HOOKPROC被调用的时机,可选参数如下。

WH_MSGFILTER = -1 线程级,截获用户与控件交互的消息
WH_JOURNALRECORD = 0 系统级,记录所有消息队列送出的输入消息
WH_JOURNALPLAYBACK = 1 系统级,回放由WH_JOURNALRECORD记录的消息
WH_KEYBOARD = 2 系统级或线程级,截获键盘消息
WH_GETMESSAGE = 3 系统级或线程级,截获从消息队列送出的消息
WH_CALLWNDPROC = 4 系统级或线程级,截获发送到目标窗口的消息
WH_CBT = 5 系统级或线程级,截获系统基本消息 例如:窗口的创建,激活,关闭,最大/最小化,移动等
WH_SYSMSGFILTER = 6 系统级,截获系统范围内用户与控件交互的消息
WH_MOUSE = 7 系统级或线程级,截获鼠标消息
WH_HARDWARE = 8 系统级或线程级,截获非标准硬件(非鼠标,键盘)的消息
WH_DEBUG = 9 系统级或线程级,在其它钩子调用前调用,用于调试钩子
WH_SHELL = 10 系统级或线程级,截获发给外壳应用程序的消息
WH_FOREGROUNDIDLE = 11 系统级或线程级,在程序前台线程空闲时调用
WH_CALLWNDPROCRET = 12 系统级或线程级,截获目标窗口处理完的消息 在SendMessage被调用后发生
WH_KEYBOARD_LL = 13 系统级,截获全局键盘消息
WH_MOUSE_LL = 14 系统级,截获全局鼠标消息

2.lpfn指向钩子回调函数的指针。如果最后一个参数dwThreadId为0或者是其它进程创建的线程标识符,则lpfn参数必须指向DLL中的钩子回调函数,即HOOKPROC函数必须在DLL中实现。否则,lpfn 可以指向与当前进程相关联的代码中的钩子过程。

3.hmod包含由lpfn参数指向的钩子回调函数的DLL句柄。如果dwThreadId参数指定由当前进程创建线程,并且钩子回调函数位于当前进程关联的代码中,则hmod参数必须设置为NULL

4.dwThreadId与钩子程序关联的线程标识符(指定要HOOK的线程 ID)。如果此参数为0,则钩子过程与系统中所有线程相关联,即全局消息钩子

实现原理

通过SetWindowsHookExW函数安装一个用于过滤特定类型消息的钩子函数(钩子WH_GETMESSAGE可以立即被触发,而某些类型的钩子需要在处理指定类型的消息时才能被触发),当其它进程中产生了我们过滤的消息,就会调用HOOKPROC,如果发现目标DLL(HOOKPROC实现的DLL)尚未加载,就会使用KeUserModeCallback函数回调User32.dll__ClientLoadLibrary() 函数,由User32.dll把这个DLL加载到目标进程中(低级键盘和鼠标钩子的加载有所不同),从而实现DLL注入其它进程的目的。

代码及其实现过程

首先新建一个dll项目

image-20220224221831169

pch.h中声明这几个我们定义的函数都是裸函数,由我们自己平衡堆栈

extern "C" _declspec(dllexport) int SetHook();
extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
extern "C" _declspec(dllexport) BOOL UnsetHook();

image-20220224222046446

pch.cpp里面写入三个函数并创建共享内存(进程通信的方法有很多,比如自定义消息、管道、dll共享节、共享内存等等,这里就用共享内存来实现进程通信)

#include "pch.h"
#include <windows.h>
#include <stdio.h>


extern HMODULE g_hDllModule;

// 共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")

//钩子回调函数
LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
	return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}

// 设置钩子
BOOL SetHook() {
	g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
	if (NULL == g_hHook) {
		return FALSE;
	}
	return TRUE;
}
//这里第二个参数是回调函数,那么我们还需要写一个回调函数的实现,这里就需要用到CallNextHookEx这个api,主要是第一个参数,这里传入钩子的句柄的话,就会把当前钩子传递给下一个钩子,若参数传入0则对钩子进行拦截

// 卸载钩子
BOOL UnsetHook() {
	if (g_hHook) {
		UnhookWindowsHookEx(g_hHook);//UnhookWindowsHookEx这个api是来卸载钩子的
	}
	return TRUE;
}

image-20220224222240772

dllmain.cpp设置DLL_PROCESS_ATTACH,然后编译生成Dll1.dll

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
HMODULE g_hDllModule = NULL;
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
	{
		g_hDllModule = hModule;
		break;
	}
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

image-20220224222424391

之后再创建一个控制台项目

image-20220224222618491

LoadLibrabryW加载dll,生成inject2.cpp文件

// inject2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>

int main()
{
	typedef BOOL(*typedef_SetGlobalHook)();
	typedef BOOL(*typedef_UnsetGlobalHook)();
	HMODULE hDll = NULL;
	typedef_SetGlobalHook SetGlobalHook = NULL;
	typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
	BOOL bRet = FALSE;

	do
	{
		hDll = ::LoadLibraryW(TEXT("D:\\vs2017project\\Dll1\\Debug\\Dll1.dll"));
		if (NULL == hDll)
		{
			printf("LoadLibrary Error[%d]\n", ::GetLastError());
			break;
		}

		SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetHook");
		if (NULL == SetGlobalHook)
		{
			printf("GetProcAddress Error[%d]\n", ::GetLastError());
			break;
		}

		bRet = SetGlobalHook();
		if (bRet)
		{
			printf("SetGlobalHook OK.\n");
		}
		else
		{
			printf("SetGlobalHook ERROR.\n");
		}

		system("pause");

		UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetHook");
		if (NULL == UnsetGlobalHook)
		{
			printf("GetProcAddress Error[%d]\n", ::GetLastError());
			break;
		}
		UnsetGlobalHook();
		printf("UnsetGlobalHook OK.\n");

	} while (FALSE);

	system("pause");
	return 0;
}

执行测试

编译inject2.cpp生成inject2.exe并运行

image-20220224223314059

打开进程工具,搜索Dll1.dll是否加载,发现在大多数进程中都得到了加载

image-20220224223458548

尝试卸载钩子

image-20220224223607471

进程中的Dll1.dll已经被卸载

image-20220224223746445


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM