首页 >> 资讯

环球微资讯!逆向原理 | SetWindowsHookEx 原理探究与实验

2023-01-17 19:57:08来源:哔哩哔哩

大家好我是Mz1,很久没有做视频,但是最近在学习和寻找素材了,新视频应该不远了。

这是我给大家带来的一个windows平台的hook实验,也是我自己的一个学习整理,关于SetWindowsHookEx这个win32api。


(资料图片仅供参考)

同步了我自己的cnblogs。

SetWindowsHook其实是在windows逆向中非常重要的一个api与之对应的是UnhookWindowsHookEx,用于卸载钩子。但是这个api的机制其实是比较复杂的,之前应该没有记录过,这次准备好好记录一下。同时之后还准备写几篇关于windows上异常处理机制的文章记录一下。从原理和实验两个部分出发。author: Mz1

文章目录

原理部分:

SetWindowsHookEx的基本信息和参数

调用SetWindowsHookEx的时候,操作系统在做什么

实验部分:

hook自己进程中的线程

hook别的进程中的线程

全局hook的使用

SetWindowsHookEx的基本信息和参数

在msdn上的描述还是比较清晰的:

参数1:idHook

idHook表示安装hook的种类,一般来说是宏定义,包括但不限于下方的宏定义,都在msdn可以查到:

比较常用的如下:

参数2:lpfn

lpfn指向hook使用的过程处理函数。在这个函数的结尾应该调用CallNextHookEx来传递消息,不然进程大概率挂掉。

注意!如果dwThreadId这个参数(也就是你要hook的线程)是0(0表示全局hook),或者这个线程不属于当前进程,这个lpfn必须指向一个位于dll中的过程处理函数!否则该hook只能应用于当前进程下的线程。

这是什么原因呢?其实跟操作系统做的事情有关,我们放在下面慢慢说。

参数3:hmod

上面不是说了,在hook其他进程的线程的时候,我们要把过程处理函数放在dll中吗?这个hmod就是指向了那个dll。如果是hook当前进程下的线程且过程处理函数位于当前进程中,则直接NULL就行了。

参数4:dwThreadId (最关键)

这应该是这个api中最重要的一个参数,决定了函数执行以后不同的行为。dwThreadId也就是想要hook的线程id。如果这个参数为0,就是我们常说的全局hook,将hook应用于当前桌面的所有应用程序。

返回值

返回这个hook的句柄。如果失败,返回NULL,可以调用GetLastError查看原因。

说到这里,基本的信息就已经解释完毕了,下面是原理和操作系统的行为。

调用SetWindowsHookEx的时候,操作系统在做什么

在msdn的Remark里面有这样一段:

SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names.

上面这段就是说,安装hook的程序和dll,要和目标程序的位数对应上。

Because hooks run in the context of an application, they must match the "bitness" of the application. If a 32-bit application installs a global hook on 64-bit Windows, the 32-bit hook is injected into each 32-bit process (the usual security boundaries apply). In a 64-bit process, the threads are still marked as "hooked." However, because a 32-bit application must run the hook code, the system executes the hook in the hooking app's context; specifically, on the thread that called SetWindowsHookEx. This means that the hooking application must continue to pump messages or it might block the normal functioning of the 64-bit processes.If a 64-bit application installs a global hook on 64-bit Windows, the 64-bit hook is injected into each 64-bit process, while all 32-bit processes use a callback to the hooking application.

上面这段就是说,你用32位的代码在64位的操作系统上装全局hook的时候,所有32位的应用程序都会被注入这个dll。但是,上面也说了,注入程序、注入的dll和被注入的程序位数要对应,因此,对于上面32位代码在64位系统上安装全局hook的时候,操作系统仍然会把64位的程序标记为已经被hook,但是!hook的过程处理函数的代码,会在注入程序(32位)的上下文中执行。64位的全局hook类似。在有一篇文章中的解释比较清楚:32位dll是不能注入到64位进程中,同理64位dll不能注入到32位进程中。如果32位进程调用SetWindowsHookEx 注入32位dll,其只能注入到32位进程中,虽然不能注入到64位进程,但是64位进程的线程依然被标注为hooked。当64位进程产生需要被hook处理的事件时,系统会在调用SetWindowsHookEx函数的进程(严格的说是线程)中执行hook例程。这要求调用SetWindowsHookEx的线程拥有一个消息泵,否则会阻止64位进程的执行。据我的推测,要求调用SetWindowsHookEx的线程拥有一个消息泵,是因为64位进程通过windows消息向调用SetWindowsHookEx的线程发送windows消息,通知钩子事件发生。如果这个线程没有处理消息,通信阻塞,64位进程挂起。如果此时安装hook的进程结束掉,64位进程继续执行。

光看肯定是一头雾水的,下面是实验部分。

hook自己进程中的线程

这里我使用的是32位的vc6进行试验创建一个mfc程序,比较方便。

左边的edit用来显示信息(输出)右边的edit用来打字测试。

这里以键盘消息hook为例子。先写好代码的框架:

测试完毕以后可以成功安装hook,修改回调函数的内容,输出按键消息:

至此,我们对自身进程的hook就完成了。

hook别的进程中的线程

我们分别写一个被hook的程序,和一个dll。

被hook的进程,同样用mfc创建,啥都不用做,拖个输入框出,显示一下自己的pid和线程id就行:

现在我们需要把hook的回调函数放在dll中了,对dll进行编写:注意,dll中sethook和unsethook是安装hook的程序使用的,用来安装和卸载hook。

然后编写注入的程序,这里就不用mfc了,直接用控制台方便一点啊:

至此,完成对指定进程中的线程进行hook:

全局hook的使用

最后,我们要尝试的,就是全局hook,只要在上面的基础上将dwThreadId的值设置为0就可以了。但是!特别重要的一点,因为64位的程序也会被挂上钩子,要使用消息代理处理(见上面原理部分)所以将上面的启动hook的程序改成mfc程序,为了兼容之前的dll,直接把调用SetHook时候的dwThreadId改成0就可以了。

做完以后发现自己没加输出,憨憨了,不过不影响。

至此,SetWindowsHookEx原理到实践就基本ok啦!!!!!终于把这个整理清晰了23333

如果觉得还不错的话关注/点赞/收藏支持一下吧~~~

关键词: hook WINDOWS EDIT SPECIFICALLY 操作系统 A_PI NULL 应用程序 一般来说

相关新闻