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



你可能感兴趣的:(编程语言)