# 用户态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。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2024-03/scaled-1680-/UoTPUjQ2mJxHVnRA-image.png)](https://raven-medicine.com/uploads/images/gallery/2024-03/UoTPUjQ2mJxHVnRA-image.png)

在 WinDBG 中，我们印证了，并且该条目的值被更新成了函数的地址。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2024-03/scaled-1680-/28BChHTZO9f9NxoP-image.png)](https://raven-medicine.com/uploads/images/gallery/2024-03/28BChHTZO9f9NxoP-image.png)

但如果，IAT 条目中的值被修改成安全产品模块的导出函数，那么是不是就意味着调用该函数的时候，安全产品都在检视了？IAT Hooking，就是这么一个原理。不过，如今安全产品，尤其是 EDR，主要使用下面要讲的内联 Hooking 进行函数调用检视。

### **内联 Hooking**

内联 Hooking 是如今更主流的 Hooking 方案，EDR 通常会给 NTAPI 设置内联 Hook，因为 NTAPI 作为用户态与内核态的桥梁。内联 Hook 的特征为在 NTAPI 代码的 **syscall 指令前**，加入**无条件的跳转**，即 **jmp** 命令。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2024-03/scaled-1680-/bEJWcFdj5BMCy1wb-image.png)](https://raven-medicine.com/uploads/images/gallery/2024-03/bEJWcFdj5BMCy1wb-image.png)

下图是常被恶意软件所利用的 **NtWriteVirtualMemory**，其 Win32 API 是 **WriteProcessMemory**。我们可以看到，第 2 条指令跳转到了别处，这是被 hook 的特征。当然了，不同的 EDR hook 的函数可能所有不同(但肯定有一些 NTAPI 是都被 Hook 的)，hook 的指令位置也可能有所不同，例如有的 EDR 会覆盖 **mov r10, rcx** 这条指令。但是，跳转一定是发生在 syscall 指令之前，因为 syscall 指令的执行即意味着向内核态的过渡。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2024-03/scaled-1680-/0HYWeD5yHhBgzDCv-image.png)](https://raven-medicine.com/uploads/images/gallery/2024-03/0HYWeD5yHhBgzDCv-image.png)

而对于不怎么在恶意软件中被利用的良性 NTAPI，则没有被 hook 的迹象。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2024-03/scaled-1680-/4KPFnhX4kGObSxVj-image.png)](https://raven-medicine.com/uploads/images/gallery/2024-03/4KPFnhX4kGObSxVj-image.png)

实际上，对于大多数 NTAPI，代码都形如下图，这是 syscall 的格式。至于和上图相差的几条指令，至少在 x64 中，并非必须的，也就是有着下述这几条指令，就可以完成 syscall。

```c
mov r10, rcx
mov rax, [SSN]
syscall
ret
```

在稍后的章节，我们会讲解如何绕过内联 Hook。