总结一下hook吧
常见的有虚表vitualtables hook,inline hook,iat hook,callbackhook等
其它那些 unhook这种衍生技术就分到别的章节学习笔记
虚表vitualtables hook
什么是虚表HOOK?
首先我们应该要先了解什么是虚表?
虚函数表->虚函数
这张表解决了继承、覆盖等问题,保证其能反应实际的函数。
如何实现
将虚表的虚函数地址更改成我们的即可
1 |
|
1.获得虚表指针
1 |
|
2.修改虚表的内存保护属性为可读可写
1 |
|
3.修改虚表中的虚函数地址为我们的函数地址.
1 |
|
第四步,还原HOOK,也就是将原函数地址,写回到虚表中
1 |
|
第五步:恢复原保护属性
1 |
|
优缺点
优点:相对其他hook来说比较简单
缺点:比较明显,只能用于虚函数,使用范围小。
c++中的RTTI
在对虚函数进行hook之前 需要先对C++的RRTI进行了解,如果不了解RTTI和虚表在内存上布局上的依赖关系,就无法hook虚表里面的所有函数
一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用 RTTI 机制。但是,在很多情况下,虚拟函数无法克服本身的局限。每每涉及到处理异类容器和根基类层次(如 MFC)时,不可避免要对对象类型进行动态判断,也就是动态类型的侦测。如何确定对象的动态类型呢?答案是使用内建的 RTTI 中的运算符:typeid 和 dynamic_cast。
代码示例
1 |
|
转载 https://blog.csdn.net/weixin_43742894/article/details/105998391
模糊假定
如果做一个假虚表且这个假虚表足够长,然后把假虚表的地址赋值给对象中的虚表指针,那么所有对虚函数的调用代码就会变成”call[虚表地址+虚表偏移]”。只要贾诩表的长度大于真虚表可能的最大偏移,同时使用贾诩表存放虚表监控函数的地址,这样对于虚函数中所有的调用都可以hook,这种就叫做模糊假定
步骤
1.先做一张很大的假虚表 可以是40000表项 因为虚表的最大容量为40000表项
1 |
|
2.建立跳转表并初始化跳转表和假虚表
3.重定向对象的虚表指针
inline hook
inline hook 本质:就是jmp到我们需要执行的代码,执行完后再jmp回来。
1、安装Inline Hook
参数校验
判断要替换的硬编码长度是否合适
将要Hook的内存修改为可写:VirtualProtectEx或VirtualProtect
创建内存空间,存储替换前的硬编码
得到要跳转的值
要跳转的地址 = E9的地址 + 5 +真正跳转的地址
–>E9后面的地址 = 要跳转的地址 - E9的地址 - 5
将要Hook的内存全部初始化成NOP (长度>5 )
修改要Hook的硬编码
修改hook状态
2、使用被Hook的函数
这时使用的就是钩子函数
钩子函数得是裸函数
保存寄存器
获取数据
恢复寄存器
执行被覆盖的硬编码
执行完毕 跳回Hook地址
3、卸载Inline Hook
判断是否需要卸载,没Hook当然就不需要卸载
修改内存为可写:VirtualProtectEx或VirtualProtect
恢复原来的硬编码
修改hook状态
==Inline Hook 最需要注意的就是自己更改的硬编码需要将其还原,不能多加也不能少添==
iat hook
IAT hook 还是需要有先行条件的,就是导入表里面得有所使用函数的地址。
导入表里面没有被调函数的地址情况:
被调函数与调用函数在同一模块
直接自己使用LoadLibrary,GetProcAddress来加载函数
对于这种无法用IAT hook 的情况,我们可以使用inline hook。
讲到iat又要讲到PE表的双桥就够 再来复习一遍https://blog.csdn.net/weixin_43742894/article/details/106150321
双桥结构
简略说一下就是
1 |
|
当PE被加载进虚拟地址空间以后,IAT的内容会被操作系统更改为函数的VA。这个修改最终导致通向“值-名称”描述的桥2发生断裂,如
当桥2发生断裂后,如果没有桥1作为参照(因为桥1和桥2维护了两个一一对应的函数RVA),我们就无法重新找到该地址到底是调用了那个函数。这就是为什么在导入表数据结构中存在两个桥的原因,也是为什么单桥导入表结构中无法实施绑定的原因。
==双桥断裂的过程:==
在PE加载的时候,双桥结构会断裂,IAT 会被PE加载器重写,PE加载器先搜索INT,PE加载器迭代搜索INT数组中的每个指针,找出 INT所指向的IMAGE_IMPORT_BY_NAME结构中的函数在内存中的真正的地址,并把它替代原来IAT中的值。
也就是当程序加载到内存以后,导入表部分发生变化的值正是IMAGE_IMPORT_DESCRIPTOR结构中的FirstThunk字段指向的函数指针表内容。
==这些内容已经不是指向函数名的指针了,而是指向了虚拟内存中该函数的可执行代码的地址!所以其含义也由原来的函数指针更改为函数的入口地址。==现在看来,所有的这些值最终都指向了同一片连续的区域,从而形成了我们常说的IAT。
==总结:==
1 |
|
==双桥结构断裂之后,IAT中存放的就是函数的真实地址,我们修改这个地址也就是实现了Hook。==
==实现原理:==
在进行Windows编程的时候,我们会经常使用Windows的API函数。而我们的API函数一般都是写在dll里,导入表里写着加载的dll和函数,代表该模块调用了哪些外部API,模块被加载到内存后, PE加载器会修改该表,地址改成外部API重定位后的真实地址, 我们只要直接把里面的地址改成我们新函数的地址, 就可以完成对相应API的Hook。
1 |
|
https://blog.csdn.net/weixin_43742894/article/details/106151669
callbackhook
回调函数callback:所调用函数执行完,之后调用的函数
钩子函数hook:消息到达目的地之前,进行拦截,处理消息