再看HOOK!

来源:互联网 发布:2016网络伤感情歌 编辑:程序博客网 时间:2024/05/17 23:58

想做一个记录键盘输入的程序,首先想到HOOK,但是在SetWindowsHookEx();和UnHookWindowsHookEX();时遇到很多麻烦

首先是在XP SP3中编译,编译器是VC6.0 SP6,系统32位。直接在程序中调用SetWindowsHookEx和UnHookWindowsHookEX。开始时以SetWindowsHookEx(...,...,NULL,0);方式调用;即后两个参数置0,结果调用不成功。这里没有记录错误值(根据后面的经验推断,估计是1428..),接着改为SetWindowsHookEx(...,...,hin,0);其中hin是当前窗口的句柄,以AfxGetIncetanceHandle();方式初始化得到。这次安装HOOK成功了,但是在SetWindowsHookEx();的调用之后,接着直接showWindow(sw_minimize);将窗口最小化,结果导致UnHookWindowsHookEx();不成功,以getLastError();查看,得到错误码是1404;(用户清除失败),查看网上的帮助,据称是由于数据共享区未设置,在焦点移出窗口之后,windows删除了之前安装的HOOK;为了验证这一想法,将最小化那句注释掉,果然成功了。

 

接着将工程移植到WIN2008 SERVER R2 (X64),编译环境是VS2010,怎么搞都不能成功,安装HOOK时一直不成功,通过GetLastError();方式获取错误码是1428,查阅资料后,错误原因是:缺少当前窗口的handle..但是明明在调用时给定了当前窗口的handle啊...在这里停了很久,后来查了网上的帖子,普遍的说法是由于调用时最后一个参数为0(SetWindowsHookEX(..,..,hin,0);),即创建的是全局Hook,windows2008在此处的处理应当与XP采用了不同的机制,在WINDOWS2008中创建全局HOOK必须要求HOOK的安装、处理、卸载函数都在DLL中实现,采用动态加载的方式在执行时装载。于是将相应的函数写入DLL,在工程中以LOOKUP方式获取并调用,这次成功了...并且在窗口最小后之后(即鼠标移出窗口后仍然不会出错)。

 

一种解释是:

 

在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成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

 

 

但是实际上,我并没有设置data_seg段,只是将有关全局Hook的操作放在DLL中,便成功实现了预期的功能。总结一下,在XP里,全局HOOK可以放在本程序中操作,但是程序不能失去焦点,否则HOOK会被系统自动释放。

在WIN2008中,对全局HOOK的调用必须置入DLL中,至于之前提到的数据段共享的问题,目前还没有遇到,还请高手指点。

原创粉丝点击