孙鑫MFC学习笔记-21HOOK编程

9 篇文章 0 订阅

21.1.1基本知识

通过SetWindowsHookEx安装钩子:
HHOOK SetWindowsHookEx(
int idHook,//将要安装的钩子过程的类型
HOOKPROC lpfn,//指向相应的钩子过程
HINSTANCE hMod,//指定lpfn指向的钩子过程所在的DLL的句柄
DWORD dwThreadId//指定与钩子过程相关的线程标识
);
SetWindowsHookEx函数的作用是安装一个应用程序定义的钩子过程,并将其放到钩子链中。
最后安装的钩子过程总是排在该链的前面。

21.1.2 进程内钩子

1.安装鼠标钩子

  1. 在CinnerHookDlg::OnInitDialog()函数之前添加鼠标钩子过程代码
HHOOK g_hMouse = NULL;
LRESULT CALLBACK MouseProc(int nCode,
	WPARAM wParam,
	LPARAM lParam
	)
{
	return 1;
}
  1. 在CinnerHookDlg::OnInitDialog()函数return之前添加安装鼠标钩子过程代码
g_hMouse = SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());//GetCurrentThreadId():得到当前线程ID

安装鼠标钩子过程中,SetWindowsHookEx函数的入参:安装的钩子过程类型是WH_MOUSE,相应的钩子过程是MouseProc,钩子过程在当前进程中定义并且dwThreadId是当前进程的线程,所以这里hMod是NULL,dwThreadId设置为当前线程ID,该ID通过GetCurrentThreadId()函数获取。

2.安装键盘钩子

(1)屏蔽所有键盘按钮消息

类似于安装鼠标钩子的过程。

  1. 在CinnerHookDlg::OnInitDialog()函数之前添加键盘钩子过程代码
HHOOK g_hKeyboard = NULL;
LRESULT CALLBACK KeyboardProc(int code,
	WPARAM wParam,
	LPARAM lParam
	)
{
	return 1;
}
  1. 在CinnerHookDlg::OnInitDialog()函数return之前添加安装键盘钩子过程代码
g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
(2)仅屏蔽键盘的空格键和回车键消息
  1. 在CinnerHookDlg::OnInitDialog()函数之前添加键盘钩子过程代码
HHOOK g_hKeyboard = NULL;
LRESULT CALLBACK KeyboardProc(int code,
	WPARAM wParam,
	LPARAM lParam
	)
{
	//return 1;
	if (VK_SPACE == wParam || VK_RETURN == wParam)
	{
		return 1;
	}
	else
	{
		return CallNextHookEx(g_hKeyboard,code,wParam,lParam);//将按键消息传递给下一个钩子过程
	}
}
  1. 在CinnerHookDlg::OnInitDialog()函数return之前添加安装键盘钩子过程代码
g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());得到当前线程ID

钩子过程通过wParam参数得到当前按下的是哪个按键。VK_SPACE宏是空格键的虚拟键码。VK_RETURN是回车键的虚拟键码。

(3)仅屏蔽键盘的组合键“Alt+F4”消息
  1. 在CinnerHookDlg::OnInitDialog()函数之前添加键盘钩子过程代码
LRESULT CALLBACK KeyboardProc(int code,
	WPARAM wParam,
	LPARAM lParam
	)
{
	//return 1;
	//if (VK_SPACE == wParam || VK_RETURN == wParam)
	if (VK_F4 == wParam && (1 == (lParam>>29 & 1)))
	{
		return 1;
	}
	else
	{
		return CallNextHookEx(g_hKeyboard,code,wParam,lParam);//将键盘消息传递给下一个钩子过程
	}
}
  1. 在CinnerHookDlg::OnInitDialog()函数return之前添加安装键盘钩子过程代码
g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());

键盘按键为F4且Alt时,return 1,其余情况将键盘消息传递给下一个钩子过程。
这里按键F4用wParam 为VK_F4 宏来判断。
按键Alt用lParam第29位,值为1则是Alt键,值为0非Alt键。lParam右移29位,将第29位移动到最右侧,和1相与,值为1说明是Alt键,值为0说明非Alt键。

(4)只能按照某个特定键退出
  1. 在CinnerHookDlg::OnInitDialog()函数之前添加键盘钩子过程代码
HHOOK g_hKeyboard = NULL;
HWND g_hWnd = NULL;
LRESULT CALLBACK KeyboardProc(int code,
	WPARAM wParam,
	LPARAM lParam
	)
{
	//return 1;
	//if (VK_SPACE == wParam || VK_RETURN == wParam)
	//if (VK_F4 == wParam && (1 == (lParam>>29 & 1)))
	//{
	//	return 1;
	//}
	//else
	//{
	//	return CallNextHookEx(g_hKeyboard,code,wParam,lParam);//将消息传递给下一个钩子过程
	//}
	if (VK_F2 == wParam)
	{
		::SendMessage(g_hWnd,WM_CLOSE,0,0);
		UnhookWindowsHookEx(g_hMouse);//移走鼠标钩子过程
		UnhookWindowsHookEx(g_hKeyboard);//移走键盘钩子过程
	}
	return 1;
}
  1. 在CinnerHookDlg::OnInitDialog()函数return之前添加安装键盘钩子过程代码
g_hWnd = m_hWnd;//获取目标窗口的句柄
g_hMouse = SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());//GetCurrentThreadId():得到当前线程ID
g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());

这里的键盘钩子过程是一个全局函数,因此该函数内部只能用一个全局的SendMessage函数。SendMessage的第一个参数是目标窗口的句柄,该句柄由CinnerHookDlg类的成员变量m_hWnd获得。
UnhookWindowsHookEx(HHOOK hhk);从钩子链中移走一个已经安装的钩子

21.1.3 全局钩子

(1)动态链接库实现全局钩子过程

要屏蔽当前正在运行的所有进程的鼠标和键盘消息,安装钩子过程的代码放到动态链接库中实现。
新建-项目-win32项目-应用程序类型选择Dll,空项目。项目名称为Hook
添加Hook.cpp,添加代码如下


#include <Windows.h>
HHOOK g_hMouse = NULL;
HHOOK g_hKeyboard = NULL;
HWND g_hwnd;
HINSTANCE g_hInst;
//鼠标钩子过程,屏蔽所有鼠标消息
LRESULT CALLBACK MouseProc(int nCode,
	WPARAM wParam,
	LPARAM lParam
	)
{
	return 1;
}
LRESULT CALLBACK KeyboardProc(int code,
	WPARAM wParam,
	LPARAM lParam
	)
{
	if (VK_F2 == wParam)
	{
		SendMessage(g_hwnd, WM_CLOSE,0,0);//窗口句柄、关闭窗口消息
		UnhookWindowsHookEx(g_hMouse);
		UnhookWindowsHookEx(g_hKeyboard);
	}
	return 1;
}
_declspec(dllexport) void SetHook(HWND hwnd);//如果接口函数没有导出,不会生成lib文件,只有dll文件生成
//安装鼠标钩子过程的函数
void SetHook(HWND hwnd)
{
	g_hwnd = hwnd;
	//g_hMouse = (WH_MOUSE, MouseProc,GetModuleHandle("Hook"),0);//第四个参数dwThreadId=0,说明安装的钩子过程与运行在同一桌面上的所有线程相关了。
	//第三个参数是安装钩子过程所在的DLL模块句柄
	//两种方式得到该句柄,1.DllMain函数,加载DLL时,系统调用DllMain并传递当前的DLL模块的句柄。2.GetModuleHandle
	//动态链接库不可单独运行,必须被调用。编写一个客户端程序,加载这个DLL。
	如果接口函数没有导出,不会生成lib文件,只有dll文件生成.所以给Hook.dll代码中添加_declspec(dllexport) void SetHook();
	//要调试dll,将dll复制到运行程序同目录下。
	//留后门,传递HookTest的窗口句柄给键盘钩子过程
	g_hMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, g_hInst, 0);//用g_hInst或者GetModuleHandle获取安装钩子过程所在的dll句柄都可以
	g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle(("Hook")), 0);
}

1.SetWindowsHookEx第三个参数是安装钩子过程所在的DLL模块句柄
有两种方式得到该句柄:方法(1).DllMain函数,加载DLL时,系统调用DllMain并传递当前的DLL模块的句柄。如代码中设置鼠标钩子。方法(2)用模块定义文件及GetModuleHandle,如代码中设置键盘钩子。
模块定义文件Hook.def内容:

LIBRARY Hook
EXPORTS
SetHook  @2

记得一定要配置模块定义文件到dll的属性中:
工程属性–>linker—>input---->Module Definition File
在这里插入图片描述

2.如果接口函数没有导出,不会生成lib文件,只有dll文件生成.所以给Hook.dll代码中添加_declspec(dllexport) void SetHook();
3.键盘钩子过程留后门F2,防止将所有进程的鼠标和键盘消息都屏蔽后,卡死的情况。当键盘按下F2,时,发送WM_CLOSE消息给调用进程的主窗口,SendMessage需要主窗口的句柄,由SetHook函数传递m_hWnd给g_hwnd 。
上述货物安装钩子过程所在的DLL模块句柄要用的.DllMain函数代码如下:
dllmain.cpp

#include<Windows.h>

extern HINSTANCE g_hInst;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	g_hInst = hModule;
	return TRUE;

}

动态链接库不可单独运行,必须被调用。编写一个客户端程序,加载这个DLL。
新建-项目-基于对话框的MFC项目-项目名称为HookTest
配置上面动态链接库项目Hook生成的Hook.lib文件:HookTest属性-链接器-输入-值设置为:…\Debug\Hook.lib
HookTestDlg.cpp文件中添加安装钩子代码:

_declspec(dllimport) void SetHook(HWND hwnd);
// CHookTestDlg 消息处理程序

BOOL CHookTestDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	...
	// TODO:  在此添加额外的初始化代码
	int cxScreen, cyScreen;
	cxScreen = GetSystemMetrics(SM_CXSCREEN);
	cyScreen = GetSystemMetrics(SM_CYSCREEN);
	SetWindowPos(&wndTopMost, 0, 0, cxScreen, cyScreen, SWP_SHOWWINDOW);//置顶HookTest的对话框窗口
	SetHook(m_hWnd);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

其中,窗口置顶并占据整个屏幕用SetWindowPos实现。

(2)共享节实现全局变量在多个进程间共享

上面的全局钩子程序,在不切换进程时按下F2键HookTest程序退出,在切换进程之后,按下F2键HookTest程序没有退出。原因是WINDOWS采用了**写入时复制时机制。**即就是DLL有个数据被两个进程共享,当第2个进程想修改DLL数据页面2的数据,操作系统会将数据2页面的数据复制一份,新建一个页面,断开数据2页面与第2个进程之间的映射关系,将新页面与第2个进程映射,这样修改第2个进程的页面数据相当于修改新页面的额数据。而第1个进程仍然用DLL的原始数据,第一个进程和第二个进程在修改之前共享同一份数据。修改后不是同一个页面上的数据。
所以上面程序HookTest,在调用SetHook函数时,全局窗口变量g_hWnd发生变化,操作系统采用了写入时复制时机制,为HoolTest进程 重新分配一个页面,随后对g_hWnd变量的设置值就是在新页面中进行的,在切换到其他进程之后,g_hWnd仍然是原始数据,所以按下F2后HookTest不能退出。
想要切换进程后,按下F2,HookTest仍能退出,需要采用共享节的方法。给Hook.dll中新建并共享一个节,将全局窗口句柄变量g_hWnd放入该节中,该全局变量在多个进程之间共享。
定义共享结点有两种方式:
1.节设置为R(读)W(写)S(共享)类型,例如

#pragma data_seg(".MySec")
HWND g_hwnd = NULL;
#pragma data_seg()
#pragma comment(linker,"/section:.MySec,RWS");//将.MySec这个节设置为共享节

2.用SECTIONS关键字实现,例如
Hook.def文件中添加

LIBRARY Hook
EXPORTS
SetHook  @2
SECTIONS
MySec READ WRITE SHARED  

显示动态链接库导出函数的命令:dumpbin -exports Hook.dll
显示动态链接库的节,命令:dumpbin -headers Hook.dll

在这里插入图片描述
通过共享节方式,所有进程都共享g_hwnd的同一份数据。当运行HookTest程序后,g_hwnd为HookTest程序的主窗口句柄,切换到其他窗口,g_hwnd值未变,仍然为HookTest程序的主窗口句柄。按下F2,发送WM_CLOSE消息,HookTest退出。

总结

1.Hook编程实现进程内钩子和全局钩子安装

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
孙鑫vc是一种特殊的混合编程语言,它结合了C语言和Verilog语言的特点。在深入详解孙鑫vc代码之前,我们先了解一下它的一些特性。 首先,孙鑫vc具有高度的可定制性。用户可以根据自己的需求选择C语言和Verilog语言中的特性来编写代码。这种灵活性使得孙鑫vc可以适用于不同的应用领域。 其次,孙鑫vc支持并行计算。它提供了一种简单而有效的方式来利用硬件资源进行并行计算,提高程序的执行效率。 另外,孙鑫vc还具有强大的调试功能。它能够在运行时对代码进行监控和调试,帮助开发者快速定位问题并进行修复。 深入详解孙鑫vc代码包括以下几个方面: 首先,我们可以从代码的结构和组织方式入手。孙鑫vc代码一般由多个模块组成,每个模块包含了各自的功能和接口。 其次,我们需要了解代码中使用的变量和数据类型。在孙鑫vc中,可以使用C语言和Verilog语言中的数据类型,如整型、浮点型等。了解这些数据类型的使用方法和限制对理解代码非常重要。 然后,我们需要分析代码中的控制流和算法。这包括了代码中的条件语句、循环语句等,以及算法的实现细节。通过对控制流和算法的分析,我们可以更好地理解代码的逻辑和实现原理。 最后,我们还需要关注代码中的接口和数据传输方式。在孙鑫vc中,模块之间通过接口进行数据的传递和交互。了解接口的定义和使用方式对于理解代码的功能和模块之间的关系非常重要。 综上所述,深入详解孙鑫vc代码需要从代码的结构和组织方式、变量和数据类型、控制流和算法、接口和数据传输方式等多个方面进行分析和理解。通过对这些方面的研究,我们可以更好地理解孙鑫vc代码,并且能够对代码进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值