DLL注入之消息钩子

1、函数准备

  通过查阅资料可知,实现消息钩子的核心函数是SetWindowsHookEx() ,通过查阅官方文档,可知:

HHOOK SetWindowsHookExA(

  [in] int       idHook,  //要安装的挂钩过程的类型

[in] HOOKPROC  lpfn,  //指向挂钩过程的指针

[in] HINSTANCE hmod,  //包含 lpfn 参数指向的挂钩过程的 DLL 的句柄

  [in] DWORD     dwThreadId //要与挂钩过程关联的线程的标识符

);

具体如下:

idHook有许多取值,可以看到2是我需要的:

剩下3个参数的解释:

根据上图红框内的提示可知,还有一个核心的函数:

LRESULT CALLBACK KeyboardProc(

  _In_ int    code,     //决定hook如何处理消息,可取0、3

  _In_ WPARAM wParam,    //击键对应系统中的虚拟代码,如Tab键对应0x09

  _In_ LPARAM lParam     //击键消息的标志位们

);

IParam:

我们想要在击键松开的时候拦截消息,所以IParam对应的值为0x80000000。

根据官方文档,安装钩子需要两个必要的部分:一个安装钩子的程序和一个全局钩子处理DLL。

2、编写DLL

DLL部分实现hook到消息之后,如何对消息进行处理。

DLL可以定义两类函数,导出函数和内部函数。导出函数可以被其他模块调用,也可以在定义它们的DLL中调用,而内部函数只能在定义它们的DLL中调用。

通过官方的example,可以看到对键盘进行拦截,核心就是实现KeyboardProc()函数:

在VS2022中新建一个DLL项目名为HookKeyBoard,在属性中c/c++->预编译中选择不使用编译头。在DLLMAIN.cpp中写入如下代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include<stdio.h>
#include<windows.h>

#define HOOK_PROCESS_NAME "notepad.exe"

HINSTANCE hInstance = NULL;
HHOOK hHook = NULL;
HWND hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID IpvReserved)//DLL入口
{
	switch (dwReason)
	{
	case DLL_PROCESS_ATTACH:
		hInstance = hinstDLL;
		break;
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	char szPath[MAX_PATH] = { 0, };
	CHAR* p = NULL;
	if (!(lParam & 0x80000000))//释放按键时
	{
		GetModuleFileNameA(NULL, szPath, MAX_PATH );//获取当前可执行程序或DLL的路径
		p = strrchr(szPath, '\\');
		//比较进程名称是否为记事本
		if (!_stricmp(p + 1, "notepad.exe"))
		{
			return 1;
		}
	}
	//不是记事本则传递给下一个hook
	return CallNextHookEx(hHook, nCode, wParam, lParam);
}
extern "C" __declspec(dllexport) void HookStart()
{
	hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hInstance, 0);
}

extern "C" __declspec(dllexport) void HookStop()
{
	if (hHook)
	{
		UnhookWindowsHookEx(hHook);
		hHook = NULL;
	}
}

Hookstart和Hookstop不是必须的,这里只是做了封装,直接给应用程序提供调用接口。

生成解决方案后,可在如下位置找到.dll文件:

3、编写安装程序

安装应用程序必须具有 DLL 模块的句柄,然后才能安装挂钩。要检索 DLL 模块的句柄,需使用 DLL 的名称调用 LoadLibrary函数。获取句柄后,可以调用 GetProcAddress 函数来检索指向挂钩过程的指针。最后,使用 SetWindowsHookEx 将挂钩过程地址安装在相应的挂钩链中。SetWindowsHookEx 传递模块句柄、指向挂钩过程入口点的指针和线程标识符的 0,指示挂钩过程应与调用线程位于同一桌面中的所有线程相关联。

官方示例如下:

  

   新建一个控制台项目,我编写的程序如下:

//#include<stdio.h>
//#include<conio.h>
//#include<windows.h>
//
//HHOOK hHook = NULL;
//HINSTANCE hInstance = NULL;
//
//int main()
//{
//    HMODULE hDll = NULL;
//    char ch = 0;
//
//    //加载dll
//    hDll = LoadLibraryA("HookKeyBoard.dll");
//    if (hDll == NULL) exit(0);
//    //获取导出函数的地址
//
//    FARPROC KeyboardProc = (FARPROC)GetProcAddress(hDll, "KeyboardProc");
//
//    //开始hook
//    hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, hInstance, 0);
//
//    //输入Q退出hook
//    printf("输入q退出hook!\n");
//    while (1)
//    {
//        char h = _getch();
//        _putch(h);
//        if (h == 'q')
//            break;
//    }
//    //结束hook
//    if (hHook)
//    {
//        UnhookWindowsHookEx(hHook);
//        hHook = NULL;
//    }
//    //卸载dll
//    FreeLibrary(hDll);
//    return 0;
//}
#include<stdio.h>
#include<conio.h>
#include<windows.h>

#define HOOK_START "HookStart"
#define HOOK_STOP  "HookStop"

typedef void(*PFN_HOOKSTART)();
typedef void(*PFN_HOOKSTOP)();

int main()
{
    HMODULE hDll = NULL;
    PFN_HOOKSTART HookStart = NULL;
    PFN_HOOKSTOP  HookStop = NULL;
    char ch = 0;

    //加载dll
    hDll = LoadLibraryA("HookKeyBoard.dll");

    //获取导出函数的地址
    HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, HOOK_START);
    HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, HOOK_STOP);

    //开始hook
    HookStart();

    //输入q退出hook
    printf("输入q退出hook!\n");
    while (1)
    {
        char h = _getch();
       _putch(h);
        if (h == 'q')
            break;
    }
    //结束hook
    HookStop();

    //卸载dll
    FreeLibrary(hDll);
    return 0;
}

  

   将第二步中生成的.dll复制到项目目录下,运行。

4、效果

  可以看到,记事本中无法键入内容

而其他软件成功键入(word不行,推测是调用了记事本?)

  输入q退出,恢复。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值