跳到主要内容

间接系统调用技术分析与评估:针对 EDR 调用栈验证机制的绕过方法

· 阅读需 12 分钟
ICE Lab
Institute of Cyber Environment

摘要

端点检测与响应(EDR)系统在过去三年中逐步建立了基于调用栈验证(Call Stack Validation)的检测体系,用于识别直接系统调用(Direct Syscalls)等恶意行为。间接系统调用(Indirect Syscalls)技术通过跳转到 ntdll.dll 中已有的 syscall 指令来执行系统调用,使 EDR 的调用栈验证机制完全失效——从 EDR 的检测视角来看,syscall 指令确实是从合法的 ntdll.dll 内存区域发起的。本文对间接系统调用的技术原理、与直接系统调用的差异、动态 SSN 解析方法及完整执行流程进行了系统分析,并从内核态验证、模块完整性检查及遥测完整性监控三个维度提出了相应的防御建议。

关键词:间接系统调用;EDR 绕过;调用栈验证;SSN 解析;Windows 安全

1. 引言

系统调用(System Call)是用户态程序请求内核服务的标准接口。在 Windows 平台上,系统调用通过 syscall 指令从用户态切换到内核态,由内核执行具体的系统服务。EDR 系统的用户态钩子通过在 ntdll.dll 的导出函数入口处插入跳转指令(jmpmov r10, rcx + jmp),实现对敏感系统调用的监控。

传统的直接系统调用(Direct Syscalls)技术通过在自己的二进制文件中直接执行 syscall 指令来绕过用户态钩子。然而,EDR 厂商通过在异常检测规则中加入调用栈验证——检查 syscall 指令执行时的返回地址是否在 ntdll.dll 范围内——使该技术在 2024 年后逐渐失效。

间接系统调用(Indirect Syscalls)是这一攻防博弈的最新演进。其核心思想是不再在攻击者代码中执行 syscall 指令,而是跳转到 ntdll.dll 中已有的 syscall 指令来执行系统调用。这使得调用栈验证完全失效,因为从 EDR 的视角来看,syscall 指令确实是从合法的 ntdll.dll 内存区域发起的。

2. 技术背景

2.1 系统调用的标准路径

在 Windows x64 平台上,一个正常的系统调用路径如下:

应用程序 → kernel32.dll → ntdll.dll → syscall 指令 → 内核

NtAllocateVirtualMemory 为例,ntdll.dll 中对应的存根(stub)函数结构如下:

; ntdll.dll 中的 NtAllocateVirtualMemory
mov r10, rcx
mov eax, 18h ; 系统服务编号(SSN)
syscall ; 切换到内核态
ret

EDR 的用户态钩子通常将函数入口的前几个字节替换为跳转指令,将执行流重定向到 EDR 的检测模块。

2.2 EDR 调用栈验证机制

调用栈验证是 EDR 用于检测直接系统调用的关键技术。其原理如下:当检测模块被调用时,回溯调用栈(Stack Walk),检查返回地址所属的内存模块。如果 syscall 指令的返回地址不在 ntdll.dll 范围内,则判定为可疑的直接系统调用行为。

直接系统调用之所以被检测,根本原因在于 syscall 指令从攻击者二进制文件的内存区域执行,调用栈中会留下异常的返回地址记录。

3. 间接系统调用技术原理

3.1 核心机制

间接系统调用的核心思想是:不再在攻击者代码中执行 syscall 指令,而是跳转到 ntdll.dll 中已有的 syscall 指令来执行系统调用。两种方式的对比:

; 直接系统调用 — EDR 可检测到异常的返回地址
mov r10, rcx
mov eax, 18h ; SSN
syscall ; 从攻击者二进制中执行 → EDR 告警
ret

; 间接系统调用 — EDR 看到返回地址在 ntdll.dll 范围内
mov r10, rcx
mov eax, 18h ; SSN
jmp qword ptr [syscall_addr] ; 跳转到 ntdll 内的 syscall 指令

从 EDR 的视角来看,间接系统调用的执行路径完全合法——syscall 指令从 ntdll.dll 的代码段执行,调用栈验证无法区分该调用是由正常程序逻辑还是由攻击者代码触发。

3.2 syscall Gadget 定位

间接系统调用的关键在于在 ntdll.dll 中定位未被钩子篡改的 syscall 指令。定位方法如下:

  1. 通过 GetModuleHandleA 获取 ntdll.dll 的基址
  2. 遍历导出表定位目标系统调用函数的地址
  3. 在函数字节码中扫描 syscall 指令的特征码 0x0F 0x05
  4. 返回该指令的地址作为跳转目标
UINT_PTR find_syscall_gadget(const char* func_name) {
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
FARPROC func_addr = GetProcAddress(ntdll, func_name);

BYTE* ptr = (BYTE*)func_addr;
for (int i = 0; i < 32; i++) {
if (ptr[i] == 0x0F && ptr[i + 1] == 0x05) {
return (UINT_PTR)&ptr[i];
}
}
return 0;
}

3.3 动态 SSN 解析

系统服务编号(System Service Number, SSN)在不同 Windows 版本间存在差异,因此需要在运行时动态解析。SSN 通常存储在 ntdll.dll 导出函数入口偏移 +4 字节处。对于已被 EDR 钩子篡改的函数,需要使用以下方法推算 SSN:

  • Halo's Gate 算法:通过相邻的未被钩子篡改的函数来推算目标函数的 SSN。由于系统调用函数的 SSN 在导出表中是连续的,可通过目标函数前后的函数 SSN 插值计算。
  • Tartarus' Gate 算法:在钩子指令内部解析被覆盖的原始字节,从中提取 SSN 值。

4. 执行流程分析

间接系统调用的完整执行流程如下:

攻击者代码                     ntdll.dll 内存空间
| |
|--- mov r10, rcx -------------> |
|--- mov eax, SSN -------------> |
|--- jmp [syscall_addr] -------> |
| syscall | ← 从 ntdll.dll 执行
| ret |
|<--------- 返回 <----------------|
|
EDR 视角:调用栈显示 syscall 从 ntdll.dll 发起
返回地址在 ntdll.dll 范围内 → 判定为合法

EDR 调用栈验证的盲区在于:它只能检查 syscall 指令的返回地址归属,而无法区分该执行流是由正常代码跳转而来,还是由直接系统调用触发。

5. 组合攻击框架

在实际红队操作中,间接系统调用通常与其他绕过技术组合使用,形成完整的防御绕过链:

层级技术目标
1间接系统调用绕过用户态钩子与调用栈验证
2ETW Patch盲化事件遥测(EtwEventWriteRET
3AMSI Bypass盲化脚本扫描(AmsiScanBufferE_INVALIDARG
4睡眠加密休眠时加密内存中的 Beacon 载荷
5模块踩踏加载合法 DLL 后覆写其 .text 段写入载荷

其中,ETW Patch 的实现为将 ntdll.dll 中的 EtwEventWrite 函数入口替换为 RET 指令(0xC3),使 Windows 事件追踪系统无法记录后续的可疑行为。AMSI Bypass 则将 amsi.dll 中的 AmsiScanBuffer 函数入口修改为返回 E_INVALIDARG0x80070057),使脚本扫描引擎失效。

6. 防御建议

6.1 内核态调用栈验证

EDR 系统应部署内核传感器,在内核层验证调用链的完整性。与用户态调用栈验证不同,内核态验证可以捕获从异常上下文发起的系统调用,且不受用户态钩子的影响。

6.2 模块完整性监控

监控进程的模块加载行为,检测异常的模块加载模式。对于调用敏感系统调用但导入表中缺乏对应函数条目的进程,应产生告警。同时,周期性校验关键 DLL(如 ntdll.dllamsi.dll)的代码完整性,检测是否被篡改。

6.3 遥测完整性监控

检测 EtwEventWriteAmsiScanBuffer 等关键遥测函数的篡改行为。当发现这些函数的入口被修改为 RET 或其他异常指令时,应立即产生高优先级告警。

6.4 欺骗技术

在关键进程(如 lsass.exe)中植入金丝雀令牌(Canary Token),当攻击者尝试通过间接系统调用来枚举或访问受保护资源时触发告警。

7. 结论

间接系统调用技术代表了 EDR 绕过技术从"绕过钩子"到"绕过调用栈验证"的范式演进。它利用了调用栈验证机制的固有盲区——该机制只能验证 syscall 指令的执行来源,而无法区分该执行流是由正常代码跳转产生还是由恶意代码构造。这一盲区的存在表明,基于单一维度的检测机制在持续的攻防博弈中具有有限的有效期。构建多层、多维度、覆盖用户态与内核态的纵深检测体系,是对抗此类攻击的根本路径。

参考文献

[1] Safety Research Team. "Indirect Syscalls: Defeating EDR Call Stack Validation." https://blog.sektor7.net/

[2] Microsoft Corporation. "Windows System Call Interface." https://learn.microsoft.com/en-us/windows-hardware/drivers/

[3] Smithson, M. "SysWhispers: System Call Stub Generation." https://github.com/jthuraisamy/SysWhispers

[4] Microsoft Corporation. "Anti-Malware Scan Interface (AMSI)." https://learn.microsoft.com/en-us/windows/win32/amsi/

[5] MDSec Research. "Indirect System Calls in Practice." https://www.mdsec.co.uk/research/