了解HOOK
- 一:HOOK是什么?
- 二:HOOK的分类
- 三:HOOK的原理?
- 四:为什么全局钩子HOOK必须写到DLL中?
- 五:HOOK的类型
一:HOOK是什么?
hook就是我们平时听到的钩子,它其实就是windows操作系统中的一种消息处理机制。
windows的消息在到达窗口处理函数之前,钩子(hook)可以提前获取这些消息(鼠标、键键盘,窗口移动等等消息),所以当我们获取到这些消息后,我们就可以把我们想做的事情,或者说要处理的某些逻辑放在钩子中。
所以:钩子实际上就是一段消息处理的程序段,通过系统调用,将程序段(钩子函数)挂入系统。每当特定的消息发出,在没有到达目的窗口之前,钩子程序就先捕获改消息,就是钩子函数先一步得到控制权。
二:HOOK的分类
- 进程内钩子:最对当前进程起作用
- 全局钩子:对系统所有的进程起作用。也是最强大,也最常用的。但是全局钩子函数必须放在独立的动态链接库(DLL)中。至于为什么,后面我们会解释一下。
三:HOOK的原理?
任何一个钩子都有系统维护一个指针链表(钩子链);其中每个指针指向每个钩子的的处理函数。这个钩子链类似链表的头插:最后安装的钩子放在链表的开始,当监视的消息出现时,系统从链表的第一个钩子开始调用处理函数。
下面简单说下钩子安装,卸载用的API:
- 安装钩子:SetWindowsHookEx()
- 卸载钩子:UnhookWindowsHookEx()
- 钩子传递(调用下一个钩子):CallNextHookEx()
这些函数具体的返回值,参数类型可以参考微软文档,后期我实现简单的例子的时候,会具体说说每个参数的作用。
四:为什么全局钩子HOOK必须写到DLL中?
先说一下DLL:
动态链接库文件(DLL)里面存放的各类程序的函数的实现过程,当程序需要调用函数时先载入DLL,取得函数的地址,然后进行调用。使用DLL的好处就是不需要再程序运行的开始前就要加载所有代码,只需在程序要用某个函数时,从DLL中取出得到这个函数地址,进行调用就行。
当多个进程共享一个DLL时,其实内存中只保留了一个分dll代码,而每个进程调用dll的带出函数是是在单独在自己进程的堆栈上分配空间的,也就是说:每个调用DLL的进程单独保留自己用到的数据。所谓的多进程共享其实就在内存中保留dll代码的空间内“做文章”,如多个进程共享一个dll时,使用LoadLibrary和GetProcAddress得到的地址是一样的,也说明内存中保留一份dll,每个进程共享dll
如果希望更加详细的了解动态库的原理,参考这篇大神的博客:多进程共享动态链接库的原理
【总结一下:】
全局钩子是对所有进程起作用,所有的GUI线程要调用钩子函数的时候。那如果全局钩子不做成一个共享的DLL。所有的进程都要加载一次钩子函数的代码到自己进程中,那属实浪费空间。所以钩子函数做成一个dll,在内存中只保留一份代码。
举个例子:
比如某流氓软件PP.exe 写了钩子函数HOOK_PassWord(),当我们运行微信.exe 输入密码时,HOOK_PassWord()要被成功执行,系统就得把函数实现加载到内存中去,如果钩子函数写在PP.exe 难不成系统要再把PP.exe 再加载一遍到内存中么? 既费内存,又引入了风险。DLL的优势就是模块化,随用随加载。
五:HOOK的类型
常见Hook技术介绍:
-
Inline Hook:
API函数都保存在操作系统提供的DLL文件中,当在程序中调用某个API函数并运行程序后,程序会隐式地将API函数所在的DLL文件加载入内存中,这样,程序就会像调用自己的函数一样调用API。Inline Hook这种方法是在程序流程中直接进行嵌入jmp指令来改变流程的。
Inline Hook流程
1:构造跳转指令。
2:在内存中找到欲Hook函数地址,并保存欲Hook位置处的前5字节。
3:将构造的跳转指令写入需Hook的位置处。
4:当被Hook位置被执行时会转到自己的流程执行。
5:如果要执行原来的流程,那么取消Hook,也就是还原被修改的字节。
6:执行原来的流程。
7:继续Hook住原来的位置 -
导入地址表钩子-IAT HOOK
导入地址表是PE文件结构中的一个表结构。在可执行文件中使用其他DLL可执行文件的代码或数据,成为导入或者输入。当PE文件需要运行时,将被系统加载至内存中,此时windows加载器会定位所有的导入的函数或者数据将定位到的内容填写至可执行文件的某个位置供其使用。这个地位是需要借助于可执行文件的导入表来完成的。导入表中存放了所使用的DLL的模块名称及导入的函数名称或函数序号。