13- 钩子

Posted 黑桃鱼

tags:

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

钩子-windows hook

**本质:**Windows消系统的消息过滤器。
**全局钩子原理:**将自己的dll注入到别的进程。并不是一开始就注入所有程序。
钩子种类:

  - **局部钩子:**仅仅在当前进程下有效
  - **全局钩子:**又称为系统钩子,在WIndows所有的进程都有效。
     - 全局要在DLL中实现。

不用种类的钩子有自己的不同的回调函数。
钩子原理:回调函数,操作系统先发给我们,然后在发送给窗口。

1、绘图解释钩子的过程


图示说明
系统给w2的消息;正常情况下我们w1是获取不到的,但是可以使用hook技术,将原来的消息先勾到我们(w1)这里,然后再回给系统,系统再重新转发给w2;这样w1就可以看到w2的消息;(偷窥者,不是中介)

2、hook用途:

作为注入的技术手段之一,可以利用hook技术,往对方的进程里面注入代码,获取进程相关的信息;
作监控的东西,eg:spy++

二、 局部钩子API介绍


1、SetWindowsHookExA 函数(winuser.h)

将应用程序定义的钩子子程安装到钩子链中。您将安装一个钩子子程来监视系统的某些类型的事件。这些事件或者与特定的线程相关联,或者与调用线程在同一桌面中的所有线程相关联。
句法

HHOOK WINAPI SetWindowsHookEx(
__in int idHook, \\\\钩子类型
__in HOOKPROC lpfn, \\\\回调函数地址
__in HINSTANCE hMod, \\\\实例句柄
__in DWORD dwThreadId); \\\\线程ID)
参数1 int idHook:钩子类型 idHook选项:14
参数2 HOOKPROC lpfn:回调函数,每个类型的钩子需匹配各自对应的回调函数
参数3 HINSTANCE hMod:实例句柄,局部钩子填NULL;全局钩子填dll的模块句柄(钩子的回调函数会填入dll)。使用 GetModuleHandle("Dll1")  获取
参数4 DWORD dwThreadId:线程ID,局部钩子填自己窗口的线程ID(UI主线程);全局钩子填0会钩系统范围内所有窗口的消息。


参数
[in] idHook
类型:要安装的钩子子程的类型。该参数可以是下列值之一。

价值意义
WH_CALLWNDPROC安装一个钩子子程,在系统将消息发送到目标窗口过程之前监视消息。
WH_CALLWNDPROCRET安装一个钩子子程,在消息被目标窗口过程处理后监视消息。
WH CBT安装一个钩子子程来接收对CBT应用程序有用的通知。
WH_DEBUG安装一个钩子子程,用于调试其他钩子子程。
WH _FOREGROUNDIDLEz安装一个钩子子程,当应用程序的前台线程即将空闲时,将调用这个钩子子程。这个挂钩对于在空闲时间执行低优先级任务很有用。
WH_GETMESSAGE安装一个钩子子程来监视发送到消息队列的消息。
WH_JOURNALPLAYBACK安装一个钩子子程,该钩子子程发送WH日志
钩子子程。
WH_JOURNALRECORD安装一个钩子子程,记录发送到系统消息队列的输入消息。这个钩子对于记录宏很有用。
WH_KEYBOARD安装一个钩子子程来监视击键消息。
WH_KEYBOARD_LL安装一个钩子子程来监视低级键盘输入事件。
WH_MOUSE安装一个钩子子程来监视鼠标消息。
WH_MOUSE_LL安装一个钩子子程来监视低级别的鼠标输入事件。
WH_MSGFILTER安装一个钩子子程,监视对话框、消息框、菜单或滚动条中的输入事件所生成的消息。
WH_SHELL安装一个钩子子程来接收对shell应用程序有用的通知。
WH_SYSMSGFILTER安装一个钩子子程,监视对话框、消息框、菜单或滚动条中的输入事件所生成的消息。钩子子程在与调用线程相同的桌面中为所有应用程序监视这些消息。

[in] lpfn
类型:函数指针
回调函数,钩子拿到这些信息怎么给你,通过回调函数把信息交给你;需要注意,不同钩子的回调函数不一样,可以通过msdn查看;
钩子子程的指针。如果_dwThreadId_参数为零或指定由不同进程创建的线程的标识符,则_lpfn_参数必须指向DLL中的钩子子程。否则,_lpfn_可以指向与当前进程关联的代码中的钩子子程。
[in] hmod
类型:实例句柄
局部钩子填写NULL;全局钩子填窗口所在模块的句柄,(全局程序会卡)
DLL的句柄
[in] dwThreadId
类型:双字节值
钩子子程要关联的线程的id,要钩的窗口所在的线程所在的id,每个进程的主线程id;
类型:钩子的句柄
如果函数成功,返回值是钩子子程的句柄。
如果函数失败,返回值为NULL。若要获取扩展的错误信息,请调用错误码.

2、UnhookWindowsHookEx 函数(winuser.h)

移除安装在钩子链中的钩子子程钩子函数功能。
句法

BOOL UnhookWindowsHookEx(
  [in] HHOOK hhk
);

参数
[in] hhk
类型:钩子的句柄
要移除的钩子的句柄,SetWindowsHookExA的返回值。此参数是通过以前调用钩子函数.
返回值
类型:布尔
如果函数成功,返回值是非零的。
如果函数失败,返回值为零。若要获取扩展的错误信息,请调用错误码.

3、NextHookEx 函数(winuser.h)

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

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

参数
[in, optional] hhk
类型:钩子的句柄
该参数被忽略。
[in] nCode
类型:(同Internationalorganizations)国际组织
传递给当前钩子子程的钩子代码。下一个钩子子程使用这个代码来决定如何处理钩子信息。
[in] wParam
类型:WPARAM
这_参数_传递给当前钩子子程的值。此参数的含义取决于与当前挂钩链相关联的挂钩类型。
[in] lParam
类型:参数
这_参数_传递给当前钩子子程的值。此参数的含义取决于与当前挂钩链相关联的挂钩类型。
返回值
类型:LRESULT
该值由链中的下一个钩子子程返回。当前钩子子程也必须返回这个值。返回值的含义取决于钩子的类型。

注意是结构

钩子种类

局部钩子:只能钩自己的窗口

// MFC基于对话框项目
HHOOK g_hHook;
// 键盘回调函数
LRESULT CALLBACK KeyboardProc(
	_In_ int    code,
	_In_ WPARAM wParam,
	_In_ LPARAM lParam
)

	// 特殊检查
	if (code < 0)
	
		return	CallNextHookEx(g_hHook, code, wParam, lParam);
	

	CString strFmt;
	strFmt.Format("HT:%c", wParam);
	OutputDebugString(strFmt);
	return	CallNextHookEx(g_hHook, code, wParam, lParam);


// 局部Hook 安装
void CHookTestDlg::OnBnClickedButton1()

	// 设置钩子
	g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
	if (g_hHook == NULL)
	
		AfxMessageBox("局部钩子安装失败");
	


// 局部Hook 卸载
void CHookTestDlg::OnBnClickedButton3()

	UnhookWindowsHookEx(g_hHook);	// 卸载钩子

执行结果

全局钩子:钩子的回调函数会填入dll,以便按需注入。

// 动态链接库项目 HookDll.cpp代码
HHOOK g_hHook;
LRESULT CALLBACK MyKeyboardProc(int code,       // hook code
    WPARAM wParam,  // virtual-key code
    LPARAM lParam   // keystroke-message information
)

    if (code < 0)
    
        return CallNextHookEx(g_hHook, code, wParam, lParam);
    

    char szBufff[MAXBYTE];
    wsprintf(szBufff, "HT:%c pid:%d", wParam, GetCurrentProcessId());
    OutputDebugString(szBufff);

    return CallNextHookEx(g_hHook, code, wParam, lParam);


__declspec(dllexport) BOOL InstallHook()

    g_hHook = SetWindowsHookEx(
        WH_KEYBOARD,
        MyKeyboardProc,
        GetModuleHandle("HookDll"),
        0);

    if (g_hHook != NULL)
    
        return TRUE;
    
    return FALSE;


__declspec(dllexport) VOID UnInstallHook()

    UnhookWindowsHookEx(g_hHook);

// 使用dll的MFC对话框项目代码
__declspec(dllimport) BOOL InstallHook();
__declspec(dllimport) VOID UnInstallHook();
#pragma comment(lib, "./Debug/HookDll.lib")
// 安装全局钩子
void CHookTestDlg::OnBnClickedButton2()

	if (!InstallHook())
	
		AfxMessageBox("安装全局钩子失败");
	

// 卸载全局钩子
void CHookTestDlg::OnBnClickedButton4()

	UnInstallHook();


或者填写def文件

执行结果

为什么全局钩子需要用dll作为过程函数?

在别的程序运行过程函数监听消息。
全局钩子在安装钩子后,如果安装钩子的程序关闭,目标进程的dll也会自动卸载。

PS:

  1. 安装了同一时间的全局钩子和局部钩子,系统优先调用局部钩子,然后调用全局钩子。
  2. 安装多个钩子处理过程,形成钩子链。当钩子结束后赢吧钩子信息传递给下一个钩子函数。钩子链式栈结构,最后安装的,最先获得控制权。
  3. 全局钩子会消耗消息处理的时间,降低系统性能,所以全局钩子并不是全部注入到所有程序中,而是在发生消息事件的时候,才将dll注入到目标程序中安装钩子。
  4. 安装的钩子需要释放,安装钩子的主程序关闭,其他程序的钩子也会自动释放。
  5. 在键盘钩子回调函数中,修改wparam的值不会影响目标进程的值。

什么是钩子函数?钩子函数的使用。

使用方法及说明.

WINDOWS的钩子函数可以认为是WINDOWS的主要特性之一。利用它们,您可以捕捉您自己进程或其它进程发生的事件。通过“钩挂”,您可以给WINDOWS一个处理或过滤事件的回调函数,该函数也叫做“钩子函数”,当每次发生您感兴趣的事件时,WINDOWS都将调用该函数。一共有两种类型的钩子:局部的和远程的。
局部钩子仅钩挂您自己进程的事件。
远程的钩子还可以将钩挂其它进程发生的事件。远程的钩子又有两种:
基于线程的 它将捕获其它进程中某一特定线程的事件。简言之,就是可以用来观察其它进程中的某一特定线程将发生的事件。
系统范围的 将捕捉系统中所有进程将发生的事件消息。 当您创建一个钩子时,WINDOWS会先在内存中创建一个数据结构,该数据结构包含了钩子的相关信息,然后把该结构体加到已经存在的钩子链表中去。新的钩子将加到老的前面。当一个事件发生时,如果您安装的是一个局部钩子,您进程中的钩子函数将被调用。如果是一个远程钩子,系统就必须把钩子函数插入到其它进程的地址空间,要做到这一点要求钩子函数必须在一个动态链接库中,所以如果您想要使用远程钩子,就必须把该钩子函数放到动态链接库中去。当然有两个例外:工作日志钩子和工作日志回放钩子。这两个钩子的钩子函数必须在安装钩子的线程中。原因是:这两个钩子是用来监控比较底层的硬件事件的,既然是记录和回放,所有的事件就当然都是有先后次序的。所以如果把回调函数放在DLL中,输入的事件被放在几个线程中记录,所以我们无法保证得到正确的次序。故解决的办法是:把钩子函数放到单个的线程中,譬如安装钩子的线程。
钩子一共有14种,以下是它们被调用的时机:
WH_CALLWNDPROC 当调用SendMessage时
WH_CALLWNDPROCRET 当SendMessage的调用返回时
WH_GETMESSAGE 当调用GetMessage 或 PeekMessage时
WH_KEYBOARD 当调用GetMessage 或 PeekMessage 来从消息队列中查询WM_KEYUP 或 WM_KEYDOWN 消息时
WH_MOUSE 当调用GetMessage 或 PeekMessage 来从消息队列中查询鼠标事件消息时
WH_HARDWARE 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时
WH_MSGFILTER 当对话框、菜单或滚动条要处理一个消息时。该钩子是局部的。它时为那些有自己的消息处理过程的控件对象设计的。
WH_SYSMSGFILTER 和WH_MSGFILTER一样,只不过是系统范围的
WH_JOURNALRECORD 当WINDOWS从硬件队列中获得消息时
WH_JOURNALPLAYBACK 当一个事件从系统的硬件输入队列中被请求时
WH_SHELL 当关于WINDOWS外壳事件发生时,譬如任务条需要重画它的按钮.
WH_CBT 当基于计算机的训练(CBT)事件发生时
WH_FOREGROUNDIDLE 由WINDOWS自己使用,一般的应用程序很少使用
WH_DEBUG 用来给钩子函数除错
附:如何使用钩子函数(接收到字母A按下时,窗体由最小化弹出的完整的代码)
Public Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, _
ByVal nCode As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long

Public Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long

Public Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" _
(ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long

Public Const WH_KEYBOARD = 2

Public Const KEY_WINSTART = 91
Public Const KEY_WINMENU = 93

Global hHook As Long

Public Function KeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If nCode >= 0 Then
If wParam = KEY_WINMENU Or wParam = KEY_WINSTART Then
If (lParam And &HC0000000) = 0 Then
MsgBox "", , ""
KeyboardProc = 1
Exit Function
End If
End If
End If
KeyboardProc = CallNextHookEx(hHook, nCode, wParam, lParam)
End Function

Option Explicit

Private Sub Command1_Click()
form2.Show 1

End Sub

Private Sub form_Load()
hHook = SetWindowsHookEx(WH_KEYBOARD, AddressOf KeyboardProc, 0&, App.ThreadID)
Me.Show
End Sub

Private Sub form_Unload(Cancel As Integer)
Call UnhookWindowsHookEx(hHook)
End Sub
参考技术A Windows的钩子函数分两种,一种是全局的,一种是线程的。全局的钩子函数可以捕获任何应用程序的消息,但必须是标准的DLL才能实现,VB做不了。VB可以实现线程的,就是当前应用程序的消息,这对鼠标消息的捕捉有影响。
SetWindowsHookEx定义如下:
Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
idHook是钩子类型,如WH_KEYBOARD捕捉键盘消息,而WH_MOUSE捕捉鼠标消息。hmod用于全局钩子,VB要实现钩子,必须设为0。dwThreadId用于线程钩子VB中可以设置为App.ThreadID。lpfn为钩子函数,在VB中可以使用AddressOf获得钩子函数的地址。这个函数因为钩子类型不同而有所不同。如键盘钩子为:
Public Function KeyboardProc(ByVal nCode As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
如果Code不为0,钩子函数必须调用CallNextHookEx,将消息传递给下面的钩子。wParam和lParam不是按键。
参考技术B Hook简介
Hook
一、Hook简介

Hook这个东西有时令人又爱又怕,Hook是用来拦截系统某些讯息之用,例如说,我们想
让系统不管在什麽地方只要按个Ctl-B便执行NotePad,或许您会使用Form的KeyPreview
,设定为True,但在其他Process中按Ctl-B呢?那就没有用,这是就得设一个Keyboard
Hook来拦截所有Key in的键;再如:MouseMove的Event只在该Form或Control上有效,如镇
果希??在Form的外面也能得知Mouse Move的讯息,那只好使用Mouse Hook来栏截Mouse
的讯息。再如:您想记录方才使用者的所有键盘动作或Mosue动作,以便录巨集,那就
使用JournalRecordHook,如果想停止所有Mosue键盘的动作,而放(执行)巨集,那就
使用JournalPlayBack Hook;Hook呢,可以是整个系统为范围(Remote Hook),即其他
Process的动作您也可以拦截,也可以是LocalHook,它的拦截范围只有Process本身。
Remote Hook的Hook Function要在.Dll之中,Local Hook则在.Bas中。
在VB如何设定Hook呢?使用SetWindowsHookEx()

Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" _
(ByVal idHook As Long, _
ByVal lpfn As Long, _
ByVal hmod As Long, _
ByVal dwThreadId As Long) As Long

idHook代表是何种Hook,有以下几种
Public Const WH_CALLWNDPROC = 4
Public Const WH_CALLWNDPROCRET = 12
Public Const WH_CBT = 5
Public Const WH_DEBUG = 9
Public Const WH_FOREGROUNDIDLE = 11
Public Const WH_GETMESSAGE = 3
Public Const WH_HARDWARE = 8
Public Const WH_JOURNALPLAYBACK = 1
Public Const WH_JOURNALRECORD = 0
Public Const WH_KEYBOARD = 2
Public Const WH_MOUSE = 7
Public Const WH_MSGFILTER = (-1)
Public Const WH_SHELL = 10
Public Const WH_SYSMSGFILTER = 6

lpfn代表Hook Function所在的Address,这是一个CallBack Fucnction,当挂上某个
Hook时,我们便得定义一个Function来当作某个讯息产生时,来处理它的Function
,这个Hook Function有一定的叁数格式

Private Function HookFunc(ByVal ncode As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long

nCode 代表是什麽请况之下所产生的Hook,随Hook的不同而有不同组的可能值
wParam lParam 传回值则随Hook的种类和nCode的值之不同而不同。

因这个叁数是一个 Function的Address所以我们固定将Hook Function放在.Bas中,
并以AddressOf HookFunc传入。至於Hook Function的名称我们可以任意给定,不一
定叫 HookFunc

hmod 代表.DLL的hInstance,如果是Local Hook,该值可以是Null(VB中可传0进去),
而如果是Remote Hook,则可以使用GetModuleHandle(".dll名称")来传入。

dwThreadId 代表执行这个Hook的ThreadId,如果不设定是那个Thread来做,则传0(所以
一般来说,Remote Hook传0进去),而VB的Local Hook一般可传App.ThreadId进去
值回值 如果SetWindowsHookEx()成功,它会传回一个值,代表目前的Hook的Handle,
这个值要记录下来。

因为A程式可以有一个System Hook(Remote Hook),如KeyBoard Hook,而B程式也来设一
个Remote的KeyBoard Hook,那麽到底KeyBoard的讯息谁所拦截?答案是,最後的那一个
所拦截,也就是说A先做keyboard Hook,而後B才做,那讯息被B拦截,那A呢?就看B的
Hook Function如何做。如果B想让A的Hook Function也得这个讯息,那B就得呼叫
CallNextHookEx()将这讯息Pass给A,於是产生Hook的一个连线。如果B中不想Pass这讯息
给A,那就不要呼叫CallNextHookEx()。

Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, _
ByVal ncode As Long, _
ByVal wParam As Long, _
lParam As Any) As Long

hHook值是SetWindowsHookEx()的传回值,nCode, wParam, lParam则是Hook Procedure
中的三个叁数。
最後是将这Hook去除掉,请呼叫UnHookWindowHookEx()
Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long

hHook便是SetWindowsHookEx()的传回值。此时,以上例来说,B程式结束Hook,则换A可
以直接拦截讯息。

KeyBoard Hook的范例
Hook Function的三个叁数
nCode wParam lParam 传回值
HC_ACTION 表按键Virtual Key 与WM_KEYDOWN同 若讯息要被处理传0
或 反之传1
HC_NOREMOVE

Public hHook As Long
Public Sub UnHookKBD()
If hnexthookproc <> 0 Then
UnhookWindowsHookEx hHook
hHook = 0
End If
End Sub

Public Function EnableKBDHook()
If hHook <> 0 Then
Exit Function
End If
hHook = SetWindowsHookEx(WH_KEYBOARD, AddressOf MyKBHFunc, App.hInstance, App.ThreadID)
End Function

Public Function MyKBHFunc(ByVal iCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
MyKBHFunc = 0 '表示要处理这个讯息
If wParam = vbKeySnapshot Then '侦测 有没有按到PrintScreen键
MyKBHFunc = 1 '在这个Hook便吃掉这个讯息
End If
Call CallNextHookEx(hHook, iCode, wParam, lParam) '传给下一个Hook
End Function
参考技术C Windows系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。而钩子是windows系统中非常重要的系统接口,用它可以截获并处理送给其它应用程序的消息,来完成普通应用程序难以实现的功能。钩子种类很多,每种钩子可以截获并处理相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取,启动和关闭应用程序的消息等。

以上是关于13- 钩子的主要内容,如果未能解决你的问题,请参考以下文章

python 在windows下监听键盘按键

c#+windows api SetWindowsHookEx 全局钩子 demo 下载

c#+windows api SetWindowsHookEx 全局钩子 demo 下载

Ring3下绕过Windows写时复制机制实现全局EAT钩子

旧文章搬运Windows中全局钩子DLL的加载过程

windows钩子函数