MFC 全局钩子dll注入监听键盘消息

MFC自带的键盘监听功能只有焦点在MFC程序界面时才能监听键盘消息,要想在MFC程序界面外监听键盘消息,可以通过DLL注入使用全局钩子来监听。首先,通过Visual Studio生成全局钩子的动态库,再在MFC程序中调用动态库。
一、生成dll
1.Visual Studio新建空项目(为了方便调试,我将dll项目保存在调用项目里新建的Hook文件夹中)
在这里插入图片描述

2.项目属性将配置类型设为动态库,字符集设为Unicod字符集(如果设为多字节字符集,部分代码需作修改)
在这里插入图片描述

3.创建Keyboard.h和Keyboard.cpp这两个文件
(1)Keyboard.h声明组合键类和键位值

#pragma once

#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif

#include <iostream>
#include "windows.h"
//#include <conio.h>
using namespace std;
#pragma warning(disable:4996)
//extern "C" MATHLIBRARY_API 从cpp中将变量、类和函数导入到h中,这样调用dll的程序才能使用这些变量、类和函数
//extern "C":外部变量声明,MATHLIBRARY_API指本dll,只有声明外部变量、外部类和外部函数,调用dll的程序才能使用这些变量、类和函数
//外部变量的声明以及定义必需写在cpp文件上,再extern "C" MATHLIBRARY_API,这样dll和调用dll的程序才能使用这些变量、类和函数
extern "C" MATHLIBRARY_API BOOL InstallHook(HWND callframe);
extern "C" MATHLIBRARY_API BOOL UninstallHook();
WH_KEYBOARD时的键值wParam
//#define F1 173
//#define F2 174
//#define F3 175
//#define F4 115
//#define F5 116
//#define F6 54
//#define F7 118
//#define F8 119
//#define F9 120
//#define F10 121
//#define F11 122
//#define F12 123
//
//#define Letter_A  65
//#define Letter_B  66
//#define Letter_C  67
//#define Letter_D  68
//#define Letter_E  69
//#define Letter_F  70
//#define Letter_G  71
//#define Letter_H  72
//#define Letter_I  73
//#define Letter_J  74
//#define Letter_K  75
//#define Letter_L  76
//#define Letter_M  77
//#define Letter_N  78
//#define Letter_O  79
//#define Letter_P  80
//#define Letter_Q  81
//#define Letter_R  82
//#define Letter_S  83
//#define Letter_T  84
//#define Letter_U  85
//#define Letter_V  86
//#define Letter_W  87
//#define Letter_X  88
//#define Letter_Y  89
//#define Letter_Z  90
//
//#define Tab  9
//#define CapsLk  20
//#define Wave  192//·~
//#define Ctrl  17
BYTE Fn = FALSE;  //Fn无法监听,按Fn+F1-F12切换到F1-F12,否则键位功能为红色字体上方字体代表的功能
//#define Win  91
//#define Alt  18
//#define Enter 13
//#define Shift 16
//#define Space 32
//#define Esc  27
//#define Insert  45
//#define NumLock  144
//#define PrtSc  44
//#define Delete  46
//#define Home  36
//#define End  35
//#define PgUp  33
//#define PgDn  34
//#define BackSpace  8
//#define Mouse_RButton  93 //空格键右边第二个键位,有鼠标右键的作用
//
//#define Operator_divide  111// /   ==   ?/
//#define Operator_plus  107// +
//#define Operator_multiply  106// *
//#define Operator_subtract  109// -
//#define Decimal_point  110
//#define Number_0  96
//#define Number_1  97
//#define Number_2  98
//#define Number_3  99
//#define Number_4  100
//#define Number_5  101
//#define Number_6  102
//#define Number_7  103
//#define Number_8  104
//#define Number_9  105
//
//#define Left  37
//#define Right  39
//#define Up  38
//#define Down  40
Letter area
//#define LetterArea_number_0  48// ) 
//#define LetterArea_number_1  49// !
//#define LetterArea_number_2  50// @
//#define LetterArea_number_3  51// #
//#define LetterArea_number_4  52// $
//#define LetterArea_number_5  53// %
//#define LetterArea_number_6  54// ^
//#define LetterArea_number_7  55// &
//#define LetterArea_number_8  56// *
//#define LetterArea_number_9  57// (
//#define subtract  189// -_
//#define plus_equal  187// + =
//#define Left_brackets  219// [{
//#define Right_brackets  221// ]}
//#define Stop_sign  220// \| 
//#define Semicolon  186// ;:
//#define Quotation_marks  222// '"
//#define Comma  188// ,<
//#define Full_stop  190// .>

//WH_KEYBOARD_LL时的键值vkCode
//BYTE Fn = FALSE;  //Fn无法监听,按Fn+F1-F12切换到F1-F12,否则键位功能为红色字体上方字体代表的功能
#define F1 112
#define F2 113
#define F3 114
#define F4 115
#define F5 116
#define F6 117
#define F7 118
#define F8 119
#define F9 120
#define F10 121
#define F11 122
#define F12 123

#define Letter_A  65
#define Letter_B  66
#define Letter_C  67
#define Letter_D  68
#define Letter_E  69
#define Letter_F  70
#define Letter_G  71
#define Letter_H  72
#define Letter_I  73
#define Letter_J  74
#define Letter_K  75
#define Letter_L  76
#define Letter_M  77
#define Letter_N  78
#define Letter_O  79
#define Letter_P  80
#define Letter_Q  81
#define Letter_R  82
#define Letter_S  83
#define Letter_T  84
#define Letter_U  85
#define Letter_V  86
#define Letter_W  87
#define Letter_X  88
#define Letter_Y  89
#define Letter_Z  90

#define Tab  9
#define CapsLk  20
#define Wave  192//·~

#define Ctrl  162
#define Ctrl_R  163
#define Alt  164
#define Alt_R  165
#define Shift 160
#define Shift_R 161

#define Space 32
#define Win  91
#define Enter 13
#define Esc  27
#define Insert  45
#define NumLock  144
#define PrtSc  44
#define Delete  46
#define Home  36
#define End  35
#define PgUp  33
#define PgDn  34
#define BackSpace  8
#define Mouse_RButton  93 //空格键右边第二个键位,有鼠标右键的作用

#define Operator_divide  111// /
#define Operator_plus  107// +
#define Operator_multiply  106// *
#define Operator_subtract  109// -
#define Decimal_point  110
#define Number_0  96
#define Number_1  97
#define Number_2  98
#define Number_3  99
#define Number_4  100
#define Number_5  101
#define Number_6  102
#define Number_7  103
#define Number_8  104
#define Number_9  105

#define Left  37
#define Right  39
#define Up  38
#define Down  40
//Letter area
#define LetterArea_number_0  48// ) 
#define LetterArea_number_1  49// !
#define LetterArea_number_2  50// @
#define LetterArea_number_3  51// #
#define LetterArea_number_4  52// $
#define LetterArea_number_5  53// %
#define LetterArea_number_6  54// ^
#define LetterArea_number_7  55// &
#define LetterArea_number_8  56// *
#define LetterArea_number_9  57// (
#define subtract  189// -_
#define plus_equal  187// + =
#define Left_brackets  219// [{
#define Right_brackets  221// ]}
#define Stop_sign  220// \| 
#define Semicolon  186// ;:
#define Quotation_marks  222// '"
#define Comma  188// ,<
#define Full_stop  190// .>
#define Question_mark  191// ?/
//加MATHLIBRARY_API后表示外部类
class  ShortcutKey
{
public:
	
	BYTE key[3];//组合键
	int index = 0;//组合键键位数量
	ShortcutKey(BYTE key_1 = 0, BYTE key_2 = 0, BYTE key_3 = 0)
	{
		this->key[0] = key_1;
		this->key[1] = key_2;
		this->key[2] = key_3;
	}			
	//在组合键中添加一个键位
	void add(BYTE key_code)
	{
		if (index < 3)
		{
			key[index] = key_code;
			index++;
		}
		
	}
	//获取组合键中最后一个键位
	BYTE getkey()
	{
		if (index != 0)
			return key[index - 1];
		else
			return 0;
	}
	//删除组合键中所有键位
	void deleteAllkey()
	{
		for (int i = 0; i < 3; i++)
			key[i] = 0;
		index = 0;
	}
	//删除组合键中最后一个键位
	void deletekey()
	{
		if (index>0)
		{
			key[index - 1] = 0;
			index--;
		}
	}
};

键位值是我电脑的键位在全局钩子中对应的数值,其它电脑可能不一样,如果不对自己调一下。
(2)Keyboard.cpp定义

#include "Keyboard.h"
//#include <conio.h>
#include <windows.h>
#include <stdio.h>

// 项目的名称,注意这个项目的名称要和最后导出的DLL的文件名一致
#define PROJECT_NAME L"Hook"
HHOOK g_hHook = NULL;// 全局钩子
HWND callframe = NULL;//发送窗口
PKBDLLHOOKSTRUCT pKeyboardHookStruct = NULL;
int keycode = 0;
boolean keydown = false;//按键标识
int lastkeycode = 0;//上次按键键值
COPYDATASTRUCT cpd;//发送数据结构
ShortcutKey Listenkey;//当前监听键
// 键盘回调
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam) 
{
	if (code < 0 || code == HC_NOREMOVE) 
	{
		// 如果代码小于零,则挂钩过程必须将消息传递给CallNextHookEx函数,而无需进一步处理,并且应返回CallNextHookEx返回的值。此参数可以是下列值之一。(来自官网手册)
		return CallNextHookEx(g_hHook, code, wParam, lParam);
	}			
	//获取键盘值
	pKeyboardHookStruct = (PKBDLLHOOKSTRUCT)lParam;
	keycode = pKeyboardHookStruct->vkCode;
	//键值纠正
	switch (keycode)
	{
	case Ctrl_R:
		keycode = Ctrl;
		break;
	case Shift_R:
		keycode = Shift;
		break;
	case Alt_R:
		keycode = Alt;
		break;
	default:
		break;
	}
	//如果按键,判断键值是否是指定键
	if (pKeyboardHookStruct->flags<128)
	{
		//如果按下键不是上次的键,组合成组合键,发送当前组合键信息到指定窗口
		if (lastkeycode != keycode)
		{
			Listenkey.add(keycode);
			lastkeycode = keycode;			
			cpd.dwData = 0;//dwData可以是任意值,
			cpd.cbData = sizeof(ShortcutKey);//指定lpData内存区域的字节数
			cpd.lpData = &Listenkey;//发送给目录窗口所在进程的数据			
			SendMessage(callframe, WM_COPYDATA, NULL, (LPARAM)&cpd);//发送组合键数据到调用窗口中
		}
	}
	//如果松开键,删除组合键键位
	if (pKeyboardHookStruct->flags >= 128)
	{
		//如果松开的是组合键最后的键位,删除最后的键位,否则删除整个组合键
		if (keycode == lastkeycode)
		{
			Listenkey.deletekey();
			lastkeycode = Listenkey.getkey();
		}
		else
		{
			Listenkey.deleteAllkey();
			lastkeycode = 0;
		}
	}
	// 将钩子往下传	
	return CallNextHookEx(g_hHook, code, wParam, lParam);
}


// 安装钩子
BOOL InstallHook(HWND frame) 
{
	// 【参数1】钩子的类型,这里代表键盘钩子
	// 【参数2】钩子处理的函数
	// 【参数3】获取模块,PROJECT_NAME为DLL的项目名称
	// 【参数4】线程的ID,如果是全局钩子的话,这里要填0,如果是某个线程的钩子,那就需要写线程的ID	
	callframe = frame;//获取调用窗口HWND
	g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, GetModuleHandle(PROJECT_NAME), 0);
	//WH_KEYBOARD wParam为各键代表值,WH_KEYBOARD_LL不是,而是键的按下和抬起状态,但WH_KEYBOARD时ctrl+space被搜狗输入法拦截,而WH_KEYBOARD_LL不会。
	if (g_hHook == NULL) 
	{
		// 钩子安装失败
		MessageBox(NULL, L"全局钩子1注册失败", L"信息", MB_OK);
		return FALSE;
	}
	return TRUE;
}

// 卸载钩子,返回是否卸载成功
BOOL UninstallHook() 
{
	return UnhookWindowsHookEx(g_hHook);
}

在回调函数KeyboardProc中将按键组合成组合键,同时通过SendMessage将组合键数据发送给调用窗口。
4.生成Hook.dll
在这里插入图片描述
二、MFC调用
1.dll文件导入MFC项目
(1)将Hook.dll文件放在MFC项目的Debug文件夹内(即exe文件所在文件夹)
在这里插入图片描述
(2)导入Hook.lib文件和Hook.h文件
在这里插入图片描述
在这里插入图片描述
注:如果dll代码修改了,需重新生成dll,并将新生成的dll替换Debug文件夹内旧文件。
2.调用dll,通过OnCopyData接收按键信息,并作指定组合键匹配

#include "Hook/Hook/Keyboard.h"
ShortcutKey* currentkey;//当前按键
ShortcutKey* key1 = new ShortcutKey(Ctrl, Space);//快捷键
BOOL CScreenDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	//安装钩子
	//AllocConsole();//打开控制台	
	InstallHook(this->m_hWnd);//安装钩子
	return TRUE; 
}
void CScreenDlg::OnDestroy()
{
	CDialogEx::OnDestroy();	
	// TODO:  在此处添加消息处理程序代码
	UninstallHook();//卸载钩子
	//FreeConsole();//关闭控制台	
}
//接收键盘监听dll发送的键盘监听信息
BOOL CScreenDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	//获取键盘监听dll发送的键盘监听信息
	currentkey = (ShortcutKey*)(pCopyDataStruct->lpData);		
	//判断键位,如果键位和指定组合键匹配,执行指定代码
	if (currentkey->key[0] == key1->key[0] && currentkey->key[1] == key1->key[1] && currentkey->key[2] == key1->key[2])
	{
		// 在此添加键盘消息处理程序代码
		//this->OnBnClickedButton1();
	}				
 	return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}

注:
1.安装HOOK钩子,如果调试不需要调试与捕获键盘相关功能时,可注释掉安装钩子代码和OnDestroy中卸载钩子代码。因为安装钩子后,断点调试时,按键盘后断点会卡住整个桌面,要等一段时间才能正常,严重时整个屏幕一直卡着,只能重启电脑。
2.问题在于dll中的回调函数KeyboardProc,只要在KeyboardProc内发送消息或者调用KeyboardProc内使用的变量都会卡,原因不明,网上也没搜到相关解决方案。但这对功能没有影响,就是会影响调试。所以项目调试与钩子无关的功能时,可以注释掉钩子的安装和卸载代码再作调试。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
MFC DLL注入器是一种软件工具,用于将动态链接库(DLL)文件注入到运行的程序中。注入器的作用是将被选定的DLL文件加载到目标程序的内存空间,从而改变目标程序的行为或添加额外的功能。 MFC(Microsoft Foundation Classes)是一个扩展应用程序开发的库,它提供了许多用于创建Windows桌面应用程序的类和函数。MFC DLL注入器利用这些类和函数创建一个DLL文件,然后将其注入到目标程序中。 注入器的工作原理是通过操作目标程序的内存空间,将DLL文件加载到指定的地址,并修改目标程序在启动时的加载行为。通过这种方式,目标程序就可以使用DLL文件中所提供的函数和功能。 注入DLL的过程可以分为以下步骤: 1. 打开目标进程:选择要注入的目标程序,并获取其进程ID。 2. 创建注入器线程:创建一个线程,在目标进程中分配内存空间。 3. 加载DLL文件:将DLL文件加载到内存空间,并获取DLL文件的地址。 4. 注入DLL文件:通过修改目标进程内存中的代码段,将DLL文件路径注入到目标程序中。 5. 执行DLL:目标程序在运行时加载DLL文件,并调用其中的函数和功能。 MFC DLL注入器通常用于软件逆向工程、调试和途中,例如修改游戏的行为、添加插件或修复错误。然而,注入器的使用也可能存在潜在的安全风险,因此需要谨慎使用并遵守法律法规。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hzl2323406

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值