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 作为用户态与内核态的桥梁。内联 Hook 的特征为在 NTAPI 代码的 syscall 指令前,加入无条件的跳转,即 jmp 命令。
下图是常被恶意软件所利用的 NtWriteVirtualMemory,NtWriteVirtualMemory,其 Win32 API 是 WriteProcessMemory。WriteProcessMemory。我们可以看到,第 2 条指令跳转到了别处,这是被 hook 的特征。当然了,不同的 EDR hook 的函数可能所有不同(但肯定有一些 NTAPI 是都被 Hook 的),hook 的指令位置也可能有所不同,例如有的 EDR 会覆盖 mov r10, rcx 这条指令。但是,跳转一定是发生在 syscall 指令之前,因为 syscall 指令的执行即意味着向内核态的过渡。
而对于不怎么在恶意软件中被利用的良性 NTAPI,则没有被 hook 的迹象。
实际上,对于大多数 NTAPI,代码都形如下图,这是 syscall 的格式。至于和上图相差的几条指令,至少在 x64 中,并非必须的,也就是有着下述这几条指令,就可以完成 syscall。
mov r10, rcx
mov rax, [SSN]
syscall
ret
例在稍后的章节,我们会讲解如,NtAllocateVirtualMemory何绕过内联 是 VirtualAlloc 的 NTAPI 版本。Hook。