直接系统调用之从上层API到下层API的旅程
字数 2416 2025-10-01 14:05:44

直接系统调用技术:从原理到实战实现

1. 系统调用基础概念

1.1 什么是系统调用

系统调用(System Call/Syscall)是用户模式程序与内核模式交互的接口机制。当用户程序需要访问受保护的内核资源或服务时,必须通过系统调用实现从用户态(Ring 3)到内核态(Ring 0)的切换。

1.2 Windows API调用链示例

以Notepad创建文件为例的调用流程:

  1. 调用Kernel32.dll中的CreateFileW
  2. Kernel32.dll调用Kernelbase.dll中的CreateFileW
  3. Kernelbase.dll调用Ntdll.dll中的NtCreateFile
  4. Ntdll.dll中的NtCreateFile包含Syscall指令,触发模式切换

1.3 Syscall指令架构差异

  • x64处理器:使用syscall指令
  • x86处理器:使用sysenter指令
  • 早期x86系统:使用int 2eh指令

1.4 系统调用结构

系统调用由两部分组成:

  • Syscall Stub(系统调用存根):准备工作
    • 将系统调用号放入指定寄存器
    • 设置参数到寄存器或堆栈
  • Syscall指令:实际执行模式切换

1.5 系统调用号特性

系统调用号(System Call Number/System Service Number/System Call ID)是内核函数的唯一标识。关键特性:

  • 同一内核函数在不同Windows版本中具有不同的调用号
  • 调用号在执行Syscall指令时必须正确传递

2. 需要系统调用的场景

2.1 必须使用系统调用的操作

  • 硬件设备访问(扫描仪、打印机等)
  • 网络连接和数据传输
  • 文件读写操作
  • 任何需要内核处理的任务

2.2 无需系统调用的操作

  • 数学计算(如1+1=2)
  • 用户态内存管理操作:
    • malloc的"小内存分配"(使用进程堆缓存时)
    • memcpy等内存拷贝操作

3. 直接系统调用技术详解

3.1 技术定义

直接系统调用是攻击者在恶意软件中使用的技术,通过自行实现Syscall指令来绕过AV/EDR在用户层对ntdll.dll的hook。

3.2 技术原理

用户模式下的恶意软件自行实现Syscall指令,直接进入内核模式,避免了通过ntdll.dll的正常路径。

3.3 为什么使用直接系统调用

EDR常用的Hook技术:

  1. Import Address Table (IAT) hooking:易于绕过
  2. SSDT hooking:Windows引入Patch Guard后无法使用
  3. Inline API hooking:当前主流检测技术

Inline Hooking通过在内存中添加无条件跳转指令,跳转到安全厂商的Hooking.dll进行分析:

  • 研判为非恶意:跳转回原执行流程
  • 研判为恶意:终止执行

3.4 EDR Hook检测方法

无EDR Hook环境(仅Windows Defender):

  • Windbg和x64dbg显示正常的NtAllocateVirtualMemory调用
  • 无额外跳转指令

有EDR Hook环境

  • 存在jmp ntdll.7FFCC600A827等跳转指令
  • Step into跟踪会进入EDR厂商的Hooking.dll

4. 实战实现:直接系统调用Loader

4.1 工具和资源准备

  • 主要工具:Visual Studio C++开发环境
  • 关键项目:SysWhispers2(https://github.com/jthuraisamy/SysWhispers2)
  • 分析工具:API Monitor、Dumpbin、x64dbg

4.2 实现步骤

步骤1:Win32 API版本Loader

首先使用标准Win32 API编写基础Loader:

  • shellcode位于.text节区
  • 超过255字节的shellcode使用.rdata节保存
  • 编译后使用x64dbg查看,可见syscall指令位于ntdll.dll内存中

步骤2:生成直接系统调用代码

使用SysWhispers2生成替代代码:

  • syscalls.h:头文件
  • syscalls.c:源文件
  • syscallsstubs.std.x64.asm:资源文件(Microsoft Macro Assembler)

步骤3:替换实现

用SysWhispers2生成的文件替代ntdll依赖,实现直接系统调用:

  • 主要修改main.cpp中的调用方式
  • 生成的syscall指令将位于程序自身内存中,而非ntdll.dll

4.3 技术分析要点

  • PE结构分析:使用Dumpbin检查导入表
  • 内存执行分析:使用x64dbg验证syscall指令位置
  • 节区特征:确认syscall指令在.text节执行
  • API监控:使用API Monitor验证是否绕过了常规API调用链

5. 检测与对抗考虑

5.1 检测直接系统调用的方法

  • 监控非标准位置的syscall指令执行
  • 分析PE导入表异常(缺少预期的ntdll导入)
  • 检测用户态系统调用存根的实现

5.2 进阶对抗技术

  • Unhooking:恢复被Hook的原始函数
  • 间接系统调用:通过合法模块间接执行系统调用
  • 系统调用号混淆:动态获取和计算系统调用号

6. 总结

直接系统调用技术通过绕过用户态的API监控机制,为防御方提供了新的挑战。理解其原理和实现方式对攻防双方都至关重要:攻击者可以更有效地规避检测,防御者则可以开发相应的检测策略。这种技术的核心在于理解Windows系统调用机制和EDR的检测原理,并通过工具实现自定义的系统调用路径。

随着安全厂商不断改进检测能力,直接系统调用技术也在持续演进,包括系统调用号的动态解析、调用路径的随机化等高级技术,这些都是二进制安全领域持续对抗的焦点。

直接系统调用技术:从原理到实战实现 1. 系统调用基础概念 1.1 什么是系统调用 系统调用(System Call/Syscall)是用户模式程序与内核模式交互的接口机制。当用户程序需要访问受保护的内核资源或服务时,必须通过系统调用实现从用户态(Ring 3)到内核态(Ring 0)的切换。 1.2 Windows API调用链示例 以Notepad创建文件为例的调用流程: 调用Kernel32.dll中的CreateFileW Kernel32.dll调用Kernelbase.dll中的CreateFileW Kernelbase.dll调用Ntdll.dll中的NtCreateFile Ntdll.dll中的NtCreateFile包含Syscall指令,触发模式切换 1.3 Syscall指令架构差异 x64处理器:使用 syscall 指令 x86处理器:使用 sysenter 指令 早期x86系统:使用 int 2eh 指令 1.4 系统调用结构 系统调用由两部分组成: Syscall Stub(系统调用存根) :准备工作 将系统调用号放入指定寄存器 设置参数到寄存器或堆栈 Syscall指令 :实际执行模式切换 1.5 系统调用号特性 系统调用号(System Call Number/System Service Number/System Call ID)是内核函数的唯一标识。关键特性: 同一内核函数在不同Windows版本中具有不同的调用号 调用号在执行Syscall指令时必须正确传递 2. 需要系统调用的场景 2.1 必须使用系统调用的操作 硬件设备访问(扫描仪、打印机等) 网络连接和数据传输 文件读写操作 任何需要内核处理的任务 2.2 无需系统调用的操作 数学计算(如1+1=2) 用户态内存管理操作: malloc的"小内存分配"(使用进程堆缓存时) memcpy等内存拷贝操作 3. 直接系统调用技术详解 3.1 技术定义 直接系统调用是攻击者在恶意软件中使用的技术,通过自行实现Syscall指令来绕过AV/EDR在用户层对ntdll.dll的hook。 3.2 技术原理 用户模式下的恶意软件自行实现Syscall指令,直接进入内核模式,避免了通过ntdll.dll的正常路径。 3.3 为什么使用直接系统调用 EDR常用的Hook技术: Import Address Table (IAT) hooking :易于绕过 SSDT hooking :Windows引入Patch Guard后无法使用 Inline API hooking :当前主流检测技术 Inline Hooking通过在内存中添加无条件跳转指令,跳转到安全厂商的Hooking.dll进行分析: 研判为非恶意:跳转回原执行流程 研判为恶意:终止执行 3.4 EDR Hook检测方法 无EDR Hook环境 (仅Windows Defender): Windbg和x64dbg显示正常的NtAllocateVirtualMemory调用 无额外跳转指令 有EDR Hook环境 : 存在 jmp ntdll.7FFCC600A827 等跳转指令 Step into跟踪会进入EDR厂商的Hooking.dll 4. 实战实现:直接系统调用Loader 4.1 工具和资源准备 主要工具 :Visual Studio C++开发环境 关键项目 :SysWhispers2(https://github.com/jthuraisamy/SysWhispers2) 分析工具 :API Monitor、Dumpbin、x64dbg 4.2 实现步骤 步骤1:Win32 API版本Loader 首先使用标准Win32 API编写基础Loader: shellcode位于.text节区 超过255字节的shellcode使用.rdata节保存 编译后使用x64dbg查看,可见syscall指令位于ntdll.dll内存中 步骤2:生成直接系统调用代码 使用SysWhispers2生成替代代码: syscalls.h:头文件 syscalls.c:源文件 syscallsstubs.std.x64.asm:资源文件(Microsoft Macro Assembler) 步骤3:替换实现 用SysWhispers2生成的文件替代ntdll依赖,实现直接系统调用: 主要修改main.cpp中的调用方式 生成的syscall指令将位于程序自身内存中,而非ntdll.dll 4.3 技术分析要点 PE结构分析 :使用Dumpbin检查导入表 内存执行分析 :使用x64dbg验证syscall指令位置 节区特征 :确认syscall指令在.text节执行 API监控 :使用API Monitor验证是否绕过了常规API调用链 5. 检测与对抗考虑 5.1 检测直接系统调用的方法 监控非标准位置的syscall指令执行 分析PE导入表异常(缺少预期的ntdll导入) 检测用户态系统调用存根的实现 5.2 进阶对抗技术 Unhooking :恢复被Hook的原始函数 间接系统调用 :通过合法模块间接执行系统调用 系统调用号混淆 :动态获取和计算系统调用号 6. 总结 直接系统调用技术通过绕过用户态的API监控机制,为防御方提供了新的挑战。理解其原理和实现方式对攻防双方都至关重要:攻击者可以更有效地规避检测,防御者则可以开发相应的检测策略。这种技术的核心在于理解Windows系统调用机制和EDR的检测原理,并通过工具实现自定义的系统调用路径。 随着安全厂商不断改进检测能力,直接系统调用技术也在持续演进,包括系统调用号的动态解析、调用路径的随机化等高级技术,这些都是二进制安全领域持续对抗的焦点。