随笔- 88  评论- 433  文章- 13 

学习笔记-HOOK钩子(1)l

基本概念

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

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

Hook (钩子)增加了系统处理的数量,系统需要处理每一个消息,所以尽量在必要的情况下安装钩子,并且尽快移除系统钩子。

 


摘要: Windows 系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。而钩子是 Windows 系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。钩子可以监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理。这样,我们就可以在系统中安装自定义的钩子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、鼠标的输入,屏幕取词,日志监视等等。可见,利用钩子可以实现许多特殊而有用的功能。因此,对于高级编程人员来说,掌握钩子的编程方法是很有必要的。

 

 

<title> How to set a Windows hook in Visual C# .NET </title>

link : http://support.microsoft.com/kb/318804/en-us

Global hooks are not supported in the .NET Framework

Except for the WH_KEYBOARD_LL low-level hook and the WH_MOUSE_LL low-level hook, you cannot implement global hooks in the Microsoft .NET Framework. To install a global hook, a hook must have a native DLL export to inject itself in another process that requires a valid, consistent function to call into. This behavior requires a DLL export. The .NET Framework does not support DLL exports. Managed code has no concept of a consistent value for a function pointer because these function pointers are proxies that are built dynamically.

Low-level hook procedures are called on the thread that installed the hook. Low-level hooks do not require that the hook procedure be implemented in a DLL.

(下面是微软网站上的中文版文章 )

<title>HOW TO :在 Visual C# .NET 中设置窗口挂钩 </title>

您无法在 Microsoft .NET 框架中实现全局挂钩。

若要安装全局挂钩,挂钩必须有一个本机动态链接库 (DLL) 导出以便将其本身插入到另一个需要调入一个有效而且一致的函数的进程中。这需要一个 DLL 导出,而 .NET 框架不支持这一点。托管代码没有让函数指针具有统一的值这一概念,因为这些函数是动态构建的代理。

 

可以看出,这里的翻译存在问题确切的说是少了我们需要的信息,原文中分明提到了 low-lever        WH_KEYBOARD_LL low-level hook and the WH_MOUSE_LL low-level hook 是除外的,也就是说 .NET Framework 下支持这两个 low-lever hook ,原因也说得很清楚了。我认为我们可以简单点理解,钩子需要把自身的 DLL 注入到其它的进程空间中,(牵涉到在 Win32 环境运行机制的问题:不同的进程在内存中的拥有各自的内存空间),但是需要 .NET Framework 支持的 dll 导入到不需要 .NET Framework 支持的进程中,那我们的就 dll 不能正常工作了,所以说不支持全局的 Hook ,而 low-lever hook 不需要 dll 注入这一过程,所以能完成全局钩子的任务。

 

让我们看看 MSDN 对这两个 low-lever hook 是怎么描述的:

LowLevelKeyboardProc Function

The LowLevelKeyboardProc hook procedure is an application-defined or library-defined callback function used with the SetWindowsHookEx function. The system calls this function every time a new keyboard input event is about to be posted into a thread input queue. The keyboard input can come from the local keyboard driver or from calls to the keybd_event function. If the input comes from a call to keybd_event, the input was " injected ". However, the WH_KEYBOARD_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event.

The HOOKPROC type defines a pointer to this callback function. LowLevelKeyboardProc is a placeholder for the application-defined or library-defined function name.

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

LowLevelMouseProc Function

The LowLevelMouseProc hook procedure is an application-defined or library-defined callback function used with the SetWindowsHookEx function. The system call this function every time a new mouse input event is about to be posted into a thread input queue. The mouse input can come from the local mouse driver or from calls to the mouse_event function. If the input comes from a call to mouse_event, the input was "injected". However, the WH_MOUSE_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event.

The HOOKPROC type defines a pointer to this callback function. LowLevelMouseProc is a placeholder for the application-defined or library-defined function name.

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

至此真相大白了!!!

 

一些运行机制:

Win16 环境中, DLL 的全局数据对每个载入它的进程来说都是相同的;而在 Win32 环境中,情况却发生了变化, DLL 函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入 DLL 时,操作系统自动把 DLL 地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该 DLL 的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的 DLL 的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。

因此,在 Win32 环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个 Dll 各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

#pragmadata_seg 预处理指令用于设置共享数据段。例如:

#pragmadata_seg ( "SharedDataName")

HHOOK hHook=NULL;

#pragmadata_seg()

 

#pragmadata_seg("SharedDataName") #pragmadata_seg() 之间的所有变量将被访问该 Dll 的所有进程看到和共享。再加上一条指令 #pragma comment(linker,"/section:.SharedDataName,rws"), 那么这个数据节中的数据可以在所有 DLL 的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。

当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里 ( 以下简称 " 地址空间 ") 。这使得 DLL 成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

 


 

posted on 2006-03-27 22:00 秋雨飘飞 阅读(...) 评论(...) 编辑 收藏