SetWindowsHookEx 失败的一个隐晦的错误原因(思路为主,解决为辅)

我在上一篇博客《SetWindowsHookEx 的资料整理与内部机理的深入分析》中详细介绍了SetWindowsHookEx的失败原因,今天又发现了一个隐晦的错误原因,这也是我这么多天来一直在寻找的东西。起因是我想为一个进程的所有线程都调用SetWindowsHookEx,现象是有些线程可以调用成功,有些线程不能调用成功。前些天一直在看windows,ReactOS相关的源代码,反汇编单步跟踪SetWindowsHookEx的执行流程,但是卡在了sysenter上,ring3级别的代码都一路跟踪过去了,到ring0的门口,一直试了好久都没能进去。今天转变了一下思路,为什么不在宏观上观察这些进程,成功的与不成功的都有什么特点呢?这也给我提了个醒,要有追究到底的坚持,也要有思路转变的灵活。

好了,现在开始说这个隐晦的错误原因到底是怎么找到的。这里主要提供的是思路,如果不想看这些,就直接看后面绿色字体部分。

1. 寻找答案

线程分为成功Hook的和不成功Hook的,那么我的目标就是要找到这两类线程有什么不同之处。所以,问题转化为了得到尽可能多的有关线程的信息,比较他们的不同之处。这样,问题就定义好了。那么,如何找到问题的答案呢?

1)Google

Google的好处在于,在你知道的东西很少的时候,可以给你提供思路,这对知识量少的同学来说是非常重要的,因为新的线索可以提供新的思路。并且我们遇到的问题,很大程度上已经被别人遇到并解决了。但是Google不好的地方就是信息分散,太杂太乱。对于菜鸟来说,最重要的一个问题就是:不知道什么是可以做的,什么是不可以做的,什么是已经实现的,什么是一定要自己实现的。有时候连我们自己都不知道问题的定义是什么,又怎么能让Google帮我们精确的找到呢?

2) MSDN

说来惭愧,今天解决这个问题的一个瞬间,我才有一点明白知识要怎么学,MSDN要怎么用。前面说了,我们菜鸟不知道什么是已经实现的,什么是要自己实现的,那么,这应该怎么办?有一个字叫Reference(参考),这就是这里的关键,也是今天对我最大的启示。我们要找的是与线程有关的函数,首先Google:CreateProcess,来到这里。然后,就看到我要找Reference。看下面:

页面的这一行就是关键,点开里面的Process and Thread Reference 或者Process and Thread functions。只需要把functions里面的东西仔细看一看,就可以大概知道什么是已经实现的,这样做比Google的效率要高很多。因为这样更加精准,所有与Process还有Thread相关的函数都在这里了。在这里,我发现了GetThreadTimes函数,这是一个突破,因为已经发现了与线程有关的时间信息。来试试吧!调用这个函数需要线程句柄,可是我只有线程Id,这时候,又在这个页面里发现了OpenThread函数,用这个函数就可以由线程ID得到线程句柄。然后,测试,发现了问题所在。

当然,这种方法也有它不靠谱的地方,比如说,如果我刚才没有参考《WINDOWS 核心编程》就不会发现原来还有一个叫GetThreadContext的函数,这个函数并不在Process and Thread functions里,而在Debugging Function里面。

这里的结论就是:多读书,多看参考,知道有什么,等用的时候,再去仔细看怎么用。


2.问题在哪里

如果要给一个进程的所有线程Hook,首先要得到这些线程,关于这一点,可以利用一组TOOL HELP FUNCTION来解决,用上面给出的思路,点这里。相信应该可以解决。

1) 得到所有线程id:TOOL HELP FUNCTION

2)得到每一个线程的时间信息:GetThreadTimes,并观察

3)给每一个线程调用SetWindowsHookEx

4)再给刚调用过SetWindowsHookEx函数的线程调用GetThreadTimes函数并观察

4)观察调用SetWindowsHookEx成功的线程与失败的线程的时间有什么不同。



3. 得出结论

1)凡是SetWindowsHookEx调用成功的线程,UserTime或者KernelTime至少有一个不为0,并且此线程此时还要存活着。

2)调用失败的线程,或者已经死亡,或者仍然存活,但UserTime和KernelTime在SetWindowsHookEx前后全部都为0!

3)如果前两种情况都不符合,例如线程没有死亡,UserTime或者KernelTime至少有一个不为0,但是SetWindowsHookEx仍然调用失败了,或者UserTime或KernelTime都为0,进程存活,但是调用成功了。那么可以参考我前一篇博文,可能会有所启发,我这里也难以解释这种现象。


特别注意1:千万要注意调用 OpenThread和GetThreadTimes的时候,一定要检查是否调用成功,如果调用不成功,一定要用GetLastError去看看到底因为什么调用不成功。否则你会以为这些进程的UserTime和KernelTime都为0,而实际上他们并不为0,只是你函数调用失败了,没有得到这些时间。这就会让我们的分析误入歧途!

特别注意2:测试的过程中会发现,有些线程OpenThread和GetThreadTimes都成功了,并且UserTime和KernelTime也不为0,但是SetWindowsHookEx仍然调用失败了,这是什么原因呢?我用Process Explorer查看了这些线程,发现这些线程已经死亡了,不再出现在进程的线程列表中。

特别注意3:测试的过程会发现,在调试模式下,权限会比较大,调用OpenProcess的时候可以用THREAD_ALL_ACCESS,OpenProcess和GetThreadTime不会因为没有权限而调用失败,如果调用失败了,一般是因为这些线程已经死亡了。这时OpenProcess返回错误代码87,参数不正确,GetThreadTime使用OpenProcess的返回值会返回NULL,错误代码6,无效句柄。而在正常模式下双击程序运行,OpenProcess如果使用THREAD_ALL_ACESS,那么会调用失败,同时GetThreadTime也会调用失败,错误代码为5,没有权限。关于如何提升进程的权限,参考下面:

http://hi.baidu.com/invisiable/blog/item/41e4c3a13fa4a68f461064fb.html

http://topic.csdn.net/u/20071023/18/78534432-2138-4444-9967-198e14e82468.html


4.解决之道

我查资料的过程中,很多人都说使用ToolHelp函数得到进程的所有线程,但是问题在于,ToolHelp函数只得到了那一时刻进程的快照,而进程时刻都处于生老病死中。要想真正为进程的每个线程Hook,是不是应该每隔一段时间就查询一下这个进程的所有线程,然后给每一个UserTime和KernelTime不全为0并且没有Hook过的线程进行Hook呢?这个我还没有试过。


或许有更好的方法,或许我们要解决的问题根本就不需要用HOOK技术。

转载请注明出处:http://blog.csdn.net/on_1y/article/details/7582347



转载于:https://www.cnblogs.com/Iambda/archive/2012/05/19/3933518.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`SetWindowsHookEx`是一个Windows API函数,用于安装一个全局的或局部的钩子函数。该函数可以用于截获和处理系统和应用程序的消息和事件,比如键盘、鼠标、消息等。使用钩子函数可以实现一些高级的功能,如键盘记录、鼠标跟踪、窗口监控等。 `SetWindowsHookEx`的函数原型如下: ```c++ HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId ); ``` 其中,参数含义如下: - `idHook`:指定需要安装的钩子类型,可以使用以下常量中的一个或多个来指定钩子类型: - `WH_KEYBOARD`:键盘钩子。 - `WH_MOUSE`:鼠标钩子。 - `WH_CALLWNDPROC`:消息钩子。 - `WH_CBT`:窗口钩子。 - `WH_GETMESSAGE`:获取消息钩子。 - `WH_JOURNALRECORD`:记录钩子。 - `WH_KEYBOARD_LL`:低级键盘钩子。 - `WH_MOUSE_LL`:低级鼠标钩子。 - `lpfn`:指定钩子函数的地址,该函数的原型为`LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)`,其中`nCode`表示钩子类型,`wParam`和`lParam`表示传递给钩子函数的消息和事件参数。钩子函数必须返回一个值,具体取决于钩子类型。 - `hMod`:指定包含钩子函数的模块的句柄,通常可以设置为NULL。 - `dwThreadId`:指定需要安装钩子的线程ID,如果为0,则表示钩子函数将被安装到系统的所有线程中,为全局钩子。 例如,以下代码安装一个键盘钩子: ```c++ HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD, HookProc, NULL, 0); ``` 需要注意的是,安装钩子函数需要有足够的权限,并且需要谨慎使用,不当的使用可能会对系统和应用程序的稳定性产生不良影响。同时,安装钩子函数后,需要在使用完毕后及时卸载,否则可能会导致钩子函数一直运行,耗费系统资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值