Hooking
Hook,即钩子,在网络安全用语中,指的是拦截并且修改特定的 API 执行流程,通常用于 Debugging,逆向工程,游戏作弊,检测恶意软件行为。API Hook 将原有的 API 替换为自定义的以进行额外的检视,如果非恶意,则继而调用原有的 API,否则会被拦截。
安全产品,例如 EDR,可能会实施 SSDT Hooking,IAT Hooking,内联 Hooking。不过对于 SSDT Hooking,因为是内核层的,虽然安全产品可以实施 SSDT Hooking 从而实现更彻底的检视,但也会被恶意软件用来实现文件、网络连接、注册表键等的隐藏。基于内核层的更改,无论目的是好是坏,都可能影响系统安全性、完整性、以及稳定性,因此微软后来引入了 PatchGuard 来阻止对内核的补丁。作为"补偿",微软引入了我们之前简述过的内核回调,供安全产品进行内核层面的检视。
IAT Hooking
我们在 PE 小节介绍过 IAT 表了,IAT 表记录了映像文件所引用的模块以及其中的导出函数。我们在编写恶意软件时,调用 Win32 API 或者 NTAPI 的话,则会使 IAT 表中增加该 API 以及其所在的模块。
以 calc 为例,我们使用 PE Bear 可以查看其在磁盘时候的 IAT 表,这时候 IAT 与 INT 是一致的,没有函数地址。但当 calc 被载入到内存中时,IAT 中会更新函数的地址。例如,我们在下图可以看到 KERNEL32 模块中第一个导入函数是 GetCurrentThreadId,当前 IAT 条目中的值是 HintName 表的 RVA。
在 WinDBG 中,我们印证了,并且该条目的值被更新成了函数的地址。
但如果,IAT 条目中的值被修改成安全产品模块的导出函数,那么是不是就意味着调用该函数的时候,安全产品都在检视了?IAT Hooking,就是这么一个原理。不过,如今安全产品,尤其是 EDR,主要使用下面要讲的内联 Hooking 进行函数调用检视。
内联 Hooking
内联 Hooking 是如今更主流的 Hooking 方案,EDR 通常会给 NTAPI 设置内联 Hook,因为 NTAPI 作为用户态与内核态的桥梁。
下图是常被恶意软件所利用的 NtWriteVirtualMemory,其 Win32 API 是 WriteProcessMemory。我们可以看到,第 2 条指令跳转到了别处,这是被 hook 的特征。当然了,不同的 EDR hook 的函数可能所有不同,hook 的指令位置也可能有所不同,例如有的 EDR 会覆盖 mov r10, rcx 这条指令。
而对于不怎么在恶意软件中被利用的良性 NTAPI,则没有被 hook 的迹象。
实际上,对于大多数 NTAPI,代码都形如下图,这是 syscall 的格式。至于和上图相差的几条指令,至少在 x64 中,并非必须的,也就是有着下述这几条指令,就可以完成 syscall。
mov r10, rcx
mov rax, [SSN]
syscall
ret
例如,NtAllocateVirtualMemory 是 VirtualAlloc 的 NTAPI 版本。