Hook函数三步走(SetWindowsHookExUnhookWindowsHookExCallNextHookEx)

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hook函数三步走(SetWindowsHookExUnhookWindowsHookExCallNextHookEx)相关的知识,希望对你有一定的参考价值。

Hook(Windows系统机制)

Hook定义

Hook是Windows中提供的一种用以替换DOS下“中断”的系统机制,中文译为“挂钩”或 “钩子” 。

在对特定的系统事件进行hook后,一旦发生已hook事件,对该事件进行hook的程 序就会收到系统的通知,这时程序就能在第一时间对该事件做出响应。

钩子(Hook) ,是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定 窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处 理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。

钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出, 在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函 数即可以加工处理(改变) 该消息,也可以不作处理而继续传递该消息,还可以强制结束消息 的传递。

Hook原理

• 每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定 的,应用程序定义的,被Hook子程调用的回调函数,也就是该钩子的各个处理子程序。当与指定的Hook类 型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息, 或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始, 而最早安装的钩子放在最后,也就是后加入的先获得控制权。

• Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放 其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系 统会自动为它做卸载钩子的操作。

• 钩子子程是一个应用程序定义的回调函数(CALLBACK Function) ,不能定义成某个类的成员函数,只能定 义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可 以是系统中所有线程的事件。

系统钩子与线程钩子

SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。

线程钩子用于监视指定线程的事件消息。线程钩子一般在当前线程或者当前线 程派生的线程内。

系统钩子 监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有 的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。系统自动将 包含“钩子回调函数”的DLL映射到受钩子函数影响的所有进程的地址空间中, 即将这个DLL注入了那些进程。

钩子函数

我师父说:就研究明白这三个函数,钩子基本就会了。

设置钩子: SetWindowsHookEx

函数功能:该函数将一个应用程序定义的挂钩处理过程安装到挂钩链中去,您可以通过安装挂钩处理过程来对系统的某些类型事件进行监控,这些事件与某个特定的线程或系统中的所有事件相关.

函数原形:

HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId );

参数:

  • idHook:指示欲被安装的挂钩处理过程之类型,此参数可以是以下值之一:
参数功能说明
WH_CALLWNDPROC(4)安装一个挂钩处理过程,在系统将消息发送至目标窗口处理过程之前,对该消息进行监视,详情参见CallWndProc挂钩处理过程.
WH_CALLWNDPROCRET(12)安装一个挂钩处理过程,它对已被目标窗口处理过程处理过了的消息进行监视,详情参见 CallWndRetProc 挂钩处理过程.
WH_CBT(5)安装一个挂钩处理过程,接受对CBT应用程序有用的消息 ,详情参见 CBTProc 挂钩处理过程.
WH_DEBUG(9)安装一个挂钩处理过程以便对其他挂钩处理过程进行调试, 详情参见DebugProc挂钩处理过程.
WH_FOREGROUNDIDLE(11)安装一个挂钩处理过程,该挂钩处理过程当应用程序的前台线程即将进入空闲状态时被调用,它有助于在空闲时间内执行低优先级的任务.
WH_GETMESSAGE(3)安装一个挂钩处理过程对寄送至消息队列的消息进行监视,详情参见 GetMsgProc 挂钩处理过程.
WH_JOURNALPLAYBACK(1)安装一个挂钩处理过程,对此前由WH_JOURNALRECORD 挂钩处理过程纪录的消息进行寄送.详情参见 JournalPlaybackProc挂钩处理过程.
WH_JOURNALRECORD(0)安装一个挂钩处理过程,对寄送至系统消息队列的输入消息进行纪录.详情参见JournalRecordProc挂钩处理过程.
WH_KEYBOARD(2)安装一个挂钩处理过程对击键消息进行监视. 详情参见KeyboardProc挂钩处理过程.
WH_KEYBOARD_LL(13)此挂钩只能在Windows NT中被安装,用来对底层的键盘输入事件进行监视.详情参见LowLevelKeyboardProc挂钩处理过程.
WH_MOUSE(7)安装一个挂钩处理过程,对鼠标消息进行监视. 详情参见 MouseProc挂钩处理过程.
WH_MOUSE_LL(14)此挂钩只能在Windows NT中被安装,用来对底层的鼠标输入事件进行监视.详情参见LowLevelMouseProc挂钩处理过程.
WH_MSGFILTER(-1)安装一个挂钩处理过程, 以监视由对话框、消息框、菜单条、或滚动条中的输入事件引发的消息.详情参见MessageProc挂钩处理过程.
WH_SHELL(10)安装一个挂钩处理过程以接受对外壳应用程序有用的通知, 详情参见 ShellProc挂钩处理过程.
WH_SYSMSGFILTER(6)安装一个挂钩处理过程,以监视由对话框、消息框、菜单条、或滚动条中的输入事件引发的消息.这个挂钩处理过程对系统中所有应用程序的这类消息都进行监视.详情参见 SysMsgProc挂钩处理过程.
  • lpfn:指向相应的挂钩处理过程.若参数dwThreadId为0或者指示了一个其他进程创建的线程之标识符,则参数lpfn必须指向一个动态链接中的挂钩处理过程.否则,参数lpfn可以指向一个与当前进程相关的代码中定义的挂钩处理过程.
  • hMod:指示了一个动态链接的句柄,该动态连接库包含了参数lpfn 所指向的挂钩处理过程.若参数dwThreadId指示的线程由当前进程创建,并且相应的挂钩处理过程定义于当前进程相关的代码中,则参数hMod必须被设置为NULL(0).
  • dwThreadId:指示了一个线程标识符,挂钩处理过程与线程相关.若此参数值为0,则该挂钩处理过程与所有现存的线程相关.

返回值:

若此函数执行成功,则返回值就是该挂钩处理过程的句柄;若此函数执行失败,则返回值为NULL(0)。若想获得更多错误信息,请调用GetLasError函数。

参数说明:

备注:若参数hMod为NULL,而参数dwThreadld为0或者指示了一个其他进程创建的线程标识符,则会产生错误.

对函数CallNextHookEx进行调用以下链接下一个挂钩处理过程是可选的,但也是被推荐的否则,其他安装了此挂钩的应用程序将无法获得此挂钩通知,从而可能导致错误的行为.除非您确实希望防止其他应用程序看到此挂钩通知,您应当调用函数CallNextHookEx。

在终止一个应用程序之前,必须调用函数UnhookWindowsHookEx以释放与此挂钩相关的系统资源.

挂钩的作用域依赖与挂钩的类型.一些挂钩只能被设置成系统作用域,其他挂钩(如下所示)还可以被设置为某一特定线程的作用域:

Hook作用域
WH_CALLWNDPROC线程或系统
WH_CALLWNDPROCRET线程或系统
WH_CBT线程或系统
WH_DEBUG线程或系统
WH_FOREGROUNDIDLE线程或系统
WH_GETMESSAGE线程或系统
WH_JOURNALPLAYBACK系统
WH_JOURNALRECORD系统
WH_KEYBOARD线程或系统
WH_KEYBOARD_LL线程或系统
WH_MOUSE线程或系统
WH_MOUSE_LL线程或系统
WH_MSGFILTER线程或系统
WH_SHELL线程或系统
WH_SYSMSGFILTER系统

对于一个特定的挂钩类型,现成的挂钩先被调用,然后才是系统挂钩被调用。

系统挂钩作为共享资源,安装一次就对所用应用程序产生影响。所有的系统挂钩函数必须在库中。系统挂钩应当被限制用于一些特殊用途的应用程序或者用来作为应用程序调试的辅助工具。不再需要挂钩的库应当将相应的挂钩处理过程删除掉。

释放钩子: UnhookWindowsHookEx

通过SetWindowsHookEx函数删除挂钩链中安装的钩子过程。

函数原型:

BOOL WINAPI UnhookWindowsHookEx(
  _In_ HHOOK hhk
);

参数

  • hhk [in]
    类型:HHOOK
    要取下钩子的把手。此参数是通过先前调用SetWindowsHookEx获得的钩子句柄

返回值
类型:BOOL
如果函数成功,返回值不为零。
如果函数失败,返回值为零。要获取扩展错误信息,请调用GetLastError。

备注:
即使在UnhookWindowsHookEx返回后,挂钩过程也可能处于被另一个线程调用的状态。如果钩子过程没有被并发调用,则挂钩过程将在UnhookWindowsHookEx返回之前立即被删除。

继续钩子: CallNextHookEx

在线程级的钩子中经常用到 GetCurrentThreadID 函数来获取当前线程 的 ID

将钩子信息传递给当前钩子链中的下一个钩子过程。钩子过程可以在处理钩子信息之前或之后调用这个函数。

函数原型:

LRESULT CallNextHookEx(
  [in, optional] HHOOK  hhk,
  [in]           int    nCode,
  [in]           WPARAM wParam,
  [in]           LPARAM lParam
);

参数

  • [in, optional] hhk
    类型:HHOOK
    该参数可以忽略,是可选的。
  • [in] nCode
    类型:int
    传递给当前钩子过程的钩子代码。下一个挂钩过程使用此代码来确定如何处理挂钩信息。
  • [in] wParam
    类型:WPARAM
    传递给当前钩子过程的wParam值。此参数的含义取决于与当前钩子链关联的钩子类型。
  • [in] lParam
    类型:LPARAM
    传递给当前钩子过程的lParam值。此参数的含义取决于与当前钩子链关联的钩子类型。

返回值

类型:LRESULT
该值由链中的下一个钩子过程返回。当前的钩子过程也必须返回这个值。返回值的含义取决于钩子类型。有关详细信息,请参阅各个挂钩过程的说明。

函数备注
挂钩程序安装在链条中,适用于特定挂钩类型。CallNextHookEx调用链中的下一个钩子。

调用CallNextHookEx是可选的,但强烈推荐;否则,安装了钩子的其他应用程序将不会收到钩子通知,因此可能会出现不正确的行为。除非您绝对需要防止其他应用程序看到通知,否则您应该调用CallNextHookEx。

Hook小案例

提示:运行这个程序你会丧失鼠标控制权,别乱动别的鼠标按键切换什么的,运行后摁下enter键,关掉弹窗就恢复了

提示:运行这个程序你会丧失鼠标控制权,别乱动别的鼠标按键切换什么的,运行后摁下enter键,关掉弹窗就恢复了

提示:运行这个程序你会丧失鼠标控制权,别乱动别的鼠标按键切换什么的,运行后摁下enter键,关掉弹窗就恢复了

#include <Windows.h>
#include <cmath>

LRESULT CALLBACK HookMFunc(int iCode, WPARAM wParam, LPARAM lParam)
{
	PMSLLHOOKSTRUCT pmsh = (PMSLLHOOKSTRUCT)lParam;
	
	bool bControl = false;

	static float scr_x = GetSystemMetrics(SM_CXSCREEN);
	static float scr_y = GetSystemMetrics(SM_CYSCREEN);

	static float scr_c_x = scr_x / 2.0f;
	static float scr_c_y = scr_y / 2.0f;

	float dx = pmsh->pt.x - scr_c_x;
	float dy = pmsh->pt.y - scr_c_y;
	float fds = sqrt(dx*dx + dy*dy);

	if (fds > 10)
	{
		float at = atan2(dy, dx);

		// 陷阱光标
		SetCursorPos(int(scr_c_x + cos(at) *100), int(scr_c_y + sin(at) * 100));

		bControl = true;
	}

	return (bControl ? 1 : CallNextHookEx(NULL, iCode, wParam, lParam)); // 继续钩子
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

	HHOOK hHm = SetWindowsHookEx(WH_MOUSE_LL, HookMFunc, hInstance, NULL);
	
	MessageBox(NULL, L"当你点击之后系统会自动释放控制权", L"提示", MB_OK);

	return 0;
}

以上是关于Hook函数三步走(SetWindowsHookExUnhookWindowsHookExCallNextHookEx)的主要内容,如果未能解决你的问题,请参考以下文章

inline hook之fishhook objc_msgSend

Android插件化开发之Hook StartActivity方法

ucos里的hook函数是啥?为啥要有这些函数?干啥用的?

pytestpytest的Hook函数详解

hook机制

利用C++编程,将hook(钩子)加到程序每一个函数,其中hook后跳转到自己自定义函数代码,实现函数打印功能