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相同

HOOK技术之SSDT hook(x86/x64)

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

浅谈EPT无痕HOOK的方法 | QI4L的沉思录

对抗

R3层可以通过临时注册函数挤掉之前注册的异常处理函数

VT层可以通过优先VT(自己先成为host),先设置不可读不可写来保护程序。但是一般也没用应该。。。