0%
毅种循环

模块 64 - Syscalls - 用户层挂钩

恶意软件开发课程 - Syscalls - 用户层挂钩


模块 64 - Syscalls - 用户层挂钩#

Syscalls - 用户层挂钩 (Userland Hooking)#

简介#

基于主机的安全解决方案(如 EDR/AV)经常对系统调用(Syscalls)执行 API Hooking,目的是在程序运行时对其进行分析和监控。例如,通过挂钩 NtProtectVirtualMemory 系统调用,安全软件可以检测到像 VirtualProtect 这样的高级 WinAPI 调用,即使该 API 已从程序的导入地址表(IAT)中隐藏。

此外,当检测到敏感操作时,安全软件可以扫描设为“可执行”状态的任何内存区域,寻找已知的病毒特征指纹。

💡 初学者提示:Hook 的位置

在用户态下,Hook 通常被安装在 syscall 指令执行之前的几条指令处。这是用户模式下系统调用的最后一步。一旦执行了 syscall 指令,执行流就进入了内核态,此时用户态的安全软件就很难再直接拦截它了。

为什么不挂钩内核?#

虽然在执行流进入内核后进行挂钩也是一种方法,但由于 Windows 的 Patch Guard 等缓解措施的存在,第三方软件很难(甚至几乎不可能)修补内核内存。此外,在内核中放置钩子会带来严重的稳定性隐患,可能导致系统崩溃(蓝屏),因此这种做法非常少见。这也是为什么大多数安全软件选择在“用户态”安装钩子的原因。

用户层挂钩演示 (EDR 模拟)#

本节将展示一个模拟 EDR 的 DLL(MalDevEdr.dll)。该 DLL 使用 Minhook 库挂钩了 NtProtectVirtualMemory。它具备以下能力:

  1. 监控内存属性变更。
  2. 如果检测到内存被设为可执行(RXRWX),则将其内存内容 Dump(导出)到屏幕。
  3. 如果检测到申请 RWX(可读写执行)权限,则立即结束目标进程。

演示流程:#

  1. 正常运行:运行一个普通的 APC Injection 程序,没有任何拦截。
  2. 注入 EDR:使用 Process Hacker 将 MalDevEdr.dll 注入到运行中的注入程序中。
  3. 触发拦截:当注入程序尝试调用 VirtualProtect 将 Payload 所在内存设为 RWX 时,拦截生效。
  4. 结果:DLL 识别出了内存中的 Msfvenom 载荷,并在 Payload 执行前强行关闭了程序。这展示了为何即使绕过了静态扫描,运行时的 Hook 依然能起到关键防御作用。

绕过用户层系统调用挂钩#

由于 Hook 存在于用户态的 ntdll.dll 模块中,我们有几种方法可以隐蔽地绕过它们:

  1. 直接系统调用 (Direct Syscalls)
  2. 间接系统调用 (Indirect Syscalls)
  3. 取消挂钩 (Unhooking)

1. 直接系统调用 (Direct Syscalls)#

这种方法不调用 ntdll.dll 中的函数,而是直接在自己的汇编文件(.asm)中编写完整的系统调用逻辑。因为我们是在自己的代码中执行 syscall 指令,所以完全避开了 ntdll.dll 中被安全软件篡写的那些指令。

; 在汇编文件中手动定义的 NtAllocateVirtualMemory
NtAllocateVirtualMemory PROC
    mov r10, rcx
    mov eax, [NtAllocateVirtualMemory 的 SSN]
    syscall
    ret
NtAllocateVirtualMemory ENDP
asm

这种方法的挑战在于如何获取正确的 SSN (系统服务号)。诸如 SysWhispersHellsGate 都是基于此原理的工具。

2. 间接系统调用 (Indirect Syscalls)#

间接系统调用是直接系统调用的改进版。在直接系统中,我们在自己的代码里执行 syscall 指令,这在某些高级 EDR 看来是“可疑”的(因为正常的调用应该来自 ntdll.dll 内部)。

间接系统调用的做法是:在我们自己的代码中准备好寄存器参数(r10eax),然后 跳转ntdll.dll 中某个合法的 syscall 指令处执行。

NtAllocateVirtualMemory PROC
    mov r10, rcx
    mov eax, [SSN]
    jmp [ntdll.dll 中某个 syscall 指令的地址]
    ret
NtAllocateVirtualMemory ENDP
asm

💡 关键优势

这样一来,从系统的角度看,系统调用的指令依然是在 ntdll.dll 的地址空间内执行的,看起来更加合规,更容易瞒过高级检测。

3. 取消挂钩 (Unhooking)#

这种方法更为暴力。其核心思想是:既然内存中的 ntdll.dll 被修改了,我们就从磁盘(C:\Windows\System32\ntdll.dll)上读取一份干净、原始的文件,用它来覆盖掉内存中那些被安全软件“污染”过的指令。一旦覆盖完成,所有的用户层钩子都会瞬间失效。


🎯 总结#

在本模块中,我们学习了:

  1. 攻防博弈:理解了安全软件(EDR)是如何利用 Hook 监控我们的低级操作的。
  2. 局限性:明确了用户层钩子虽然强大,但也非常容易被特定的技术绕过。
  3. 绕过思路:初步接触了 Direct/Indirect Syscalls 以及 Unhooking 三大利器。

📚 下一步学习

下一个模块将介绍 Syscalls - SysWhispers。我们将学习如何利用这套强大的开源工具实现全自动的绕过!