windows上的hook对抗
HOOK
hook是逆向中一种基本操作,它的作用对象是函数(代码块),作用效果是在执行某个函数之前/之时/之后立即执行用户定义的过程。我们将加载这个hook到这个函数的过程称为挂钩,将从函数取消这个hook的过程称为脱钩。
本文是hook原理的总结,没有涉及实践
基于修改代码的Hook
最简单的inlineHook
- 先获取目标函数的地址
- 将目标函数地址前x字节改为jmp跳转,跳转到自己函数的地址
- 最后执行刚刚被覆盖的指令,然后跳转回去
简单,但是无法确定被覆盖的指令的长度,同时如果被覆盖的指令含有ip索引或短跳转,则会报错,需要手动脱钩
trampolineHook
基于上面的缺点,通过构造一个新函数,在新函数中调用原函数,调用之前,调用之后都可以执行自己需要的操作。
- 获取目标函数地址
- 将目标函数地址前x字节改为jmp跳转,跳转到下一步这个函数的地址,并保存前x字节
- 构造一个新函数,使得传入传出类型(调用约定)相同
- 将前x字节还原
- hook代码
- 执行原函数
- hook代码
- 将前x字节重新设置为该函数开头
- 返回
这样,就不需要担心被覆盖的指令的执行问题,同时,这个hook可以自动恢复挂钩,如果是多线程,也许会出现挂钩不到的情况?
CRC32检测Hook
针对以上两种基于修改代码实现的hook,只需要通过实现一个CRC32校验函数去检查对应函数是否被修改即可。
需要已知目标的CRC32值,或者在很早的时候先执行,保存原始CRC32值,在某个时刻或开启新线程循环检查计算的CRC32值是否相同即可,这种方法也可以实现对断点的检测。
更改页属性正面对抗CRC32检测
如果CRC32是先执行,然后保存,可以在进程启动时立即hook实现绕过,但是这样只能用基本inlinehook,否则trampolineHook还原代码时也会被检测到。
CRC32检测代码时,需要读取代码段数据,通过将代码段的页设为不可读或使用硬件断点为函数加上读断点,可以找到CRC32检测代码,然后hook这个代码即可令检测失效。
与此同时,如果还有n个CRC32检测代码,第i个检测第i+1个,第n个检测真实代码,那么就需要对所有的代码进行hook。在实际场景中,防不胜防,在游戏逆向时,往往可能导致封号造成损失,所以需要注意。这就是在量上的对抗升级?
基于表的Hook
IAT hook
在hook外部导入函数时,可以对IAT进行hook,基本方法是:
- IAT初始化后,读取IAT对应的位置,找到jmp后面的offset,计算出正确的函数地址
- 实现一个hook函数,最后jmp到正确的函数地址
- 将offset设置为到hook函数的偏移
对于kernel32函数和ntdll函数这样设置都比较方便
API Set Map Hook(DLL注入)
打开进程时,系统根据API Set Map设置导入的不同dll 函数的正确映射dll,因此,通过挂起创建的进程,先修改对应的map,实现对某个dll中全部函数的hook。
- 用注入器进程创建目标进程
- 将目标peb的ApiSetMap拷贝出来,添加自己的dll到结构末尾
- 将上面的内容拷贝到目标的新地址空间
- 让目标ApiSetMap指针指向对应位置
- 执行进程
CPP虚函数表Hook
CPP中实现子类调用父类虚函数(自己实现的函数)时,使用了虚函数表,通过修改虚函数表,实现调用hook的函数。类实例开头存放着虚函数表,其中有所有可调用的函数地址。它们有固定的顺序。通过修改这个来实现Hook一个类的成员方法
具体过程省略,和签名的IAThook相同
SSDT Hook
SSDT是系统服务描述符表,在内核层。执行系统调用的时候,会在这个表中寻找对应的函数地址,从而执行。
- 绕过写保护和Patchguard
- 写入SSDT表
- 后续过程与IAThook相同
IDT Hook
IDT是中断描述符表,描述了CPU遇到中断/异常时该转向哪里去处理它们。IDT对应单个CPU核心,hook时,需要hook所有表。内核层。
[原创]rootkit hook之[四]– IDT Hook
对抗
对于表,依旧是数据,因此对应层的CRC32检测依然有效,这就回到了上面的正面对抗
基于消息分发的Hook
IRP hook
依旧是内核层,不会。之后学。
[原创]rootkit hook 之[五] – IRP Hook全家福-软件逆向-看雪-安全社区|安全招聘|kanxue.com
对抗
hook都不会,还对抗?QAQ
基于异常分发的Hook
hook本质上就是在程序运行的特定时机修改控制流执行一些代码,因此异常分发,中断,调用等等都可以实现hook。
在CPU遇到异常时会进入异常分发,在异常分发的流程中进行一些修改即可实现hook。
R3层
AddVectoredExceptionHandler可以为一个函数增加异常处理函数,如果遇到异常,会直接触发增加的函数。因此通过设置软/硬件断点,故意触发设置的函数即可实现hook。
过程略。
VT层
由于host(宿主机)可以接管guest(虚拟机)的所有错误触发,可以通过开启EPT扩展页表设置guest的某些页表属性为不可读不可写可执行,当guest试图读写时,触发EPT异常,将
- 生成一个新页,内容是自己的hook函数
- 将原来的页前几字节设为jmp
- 将原来的页设为不可读不可写可执行
- 如果读写,则会触发EPT异常
- 异常处理函数将页设为可读可写不可执行并恢复页的前几个字节
- 如果执行,且当前是可读可写不可执行,触发EPT异常
- 异常处理函数将页设为不可读不可写可执行,并设置jmp
对抗
R3层可以通过临时注册函数挤掉之前注册的异常处理函数
VT层可以通过优先VT(自己先成为host),先设置不可读不可写来保护程序。但是一般也没用应该。。。