0%
毅种循环

模块 63 - Syscalls - 简介

恶意软件开发课程 - Syscalls - 简介


模块 63 - Syscalls - 简介#

Syscalls - 简介#

什么是 Syscalls (系统调用)#

Windows 系统调用 (System Calls),简称 Syscalls,是程序与操作系统内核交互的桥梁位。它允许程序请求特定的操作系统服务,如读取或写入文件、创建新进程或分配内存。

回顾之前的入门模块,WinAPI 函数底层实际上都是通过调用这些 Syscalls 来完成实际操作的。例如,当你调用 VirtualAllocVirtualAllocEx 时,系统底层会触发 NtAllocateVirtualMemory 系统调用。该系统调用会将用户提供的参数传递给 Windows 内核,执行请求的操作,并将结果返回给程序。

所有 Syscalls 都会返回一个 NTSTATUS 值 来表示错误代码。如果操作成功,则返回 STATUS_SUCCESS(即 0)。

由于微软官方并未记录大部分 Syscalls,我们的 Syscalls 模块将主要参考以下文档:

NTDLL 与 Syscalls#

绝大多数系统调用都是从 ntdll.dll 中导出的。

为什么要使用 Syscalls?#

  1. 低级访问:使用系统调用可以获得操作系统的低级访问权限,执行一些标准 WinAPI 无法完成或实现起来非常复杂的任务。例如,NtCreateUserProcess 相比 CreateProcess 提供了更多的底层配置选项。
  2. 逃避防御:syscalls 常被用于绕过基于主机的安全解决方案(如 EDR/AV),我们将在后续模块中深入探讨这一点。

Zw 与 Nt 系统调用的区别#

你会发现系统调用通常有两种开头:NtZw

  • Nt 系统调用:是用户模式 (User-mode) 程序的主要接口。这是大多数普通 Windows 程序使用的版本。
  • Zw 系统调用:则是更底层的系统接口,通常用于内核模式 (Kernel-mode),比如驱动程序开发。

💡 初学者提示:在用户态下它们是一样的

ntdll.dll 中,如果你查看 NtAllocateVirtualMemoryZwAllocateVirtualMemory 的反汇编代码,你会发现它们的地址和代码逻辑是 完全相同 的。它们都指向同一个入口点。在用户层开发中,你可以互换使用它们,但按照惯例,我们通常使用 Nt 版本。

系统服务号 (SSN)#

每一个系统调用都有一个专属的编号,称为 系统服务号 (System Service Number, SSN)。内核正是通过这个编号来区分不同的系统调用。

  • 例如:NtAllocateVirtualMemory 的 SSN 可能是 24。
  • NtProtectVirtualMemory 的 SSN 可能是 80。

内核接收到编号后,查表就知道该执行哪个函数了。

不同 OS 版本的 SSN 差异#

非常重要的一点:SSN 并不是固定不变的。它会随着 Windows 版本(如 Win10 与 Win11)甚至细分版本(如 Windows 11 21h2 与 22h2)的变化而改变。 同一个 NtAllocateVirtualMemory,在版本 A 上是 24,在版本 B 上可能就变成了 34。

内存中的 Syscalls 排列#

虽然 SSN 在不同版本间会变,但在同一个系统中,它们的排列是有规律的。通常在内存中,系统调用是按顺序排列的,每个 SSN 都等于前一个 SSN + 1。理解这种关联性对于我们后续绕过某些安全监控(Hooking)非常关键。

Syscall 的汇编结构 (X64)#

一个标准的 X64 Syscall 汇编结构通常如下:

mov r10, rcx    ; 将第一个参数从 rcx 移动到 r10 (内核要求)
mov eax, SSN    ; 将系统服务号存入 eax
syscall         ; 执行 syscall 指令,切入内核态
ret             ; 返回
asm

图片
图片

指令详解#

  1. mov r10, rcx:在 x64 调用约定中,第一个参数在 rcx 中。但 syscall 指令会修改 rcx 寄存器,因此内核规定必须先把第一个参数挪到 r10
  2. mov eax, SSN:内核根据 eax 里的数字来判断程序到底想调用哪个服务。
  3. syscall:这是关键指令。它会让 CPU 立即从用户态切换到内核态,并将控制权交给操作系统的系统调用处理程序。

💡 初学者提示:WoW64 指令

在某些反汇编中,你可能看到 testjne 指令。那是为了在 64 位机器上兼容运行 32 位程序(WoW64)设计的。对于纯 64 位进程,这些指令不会影响执行逻辑,可以暂时忽略。

并非所有 NtAPI 都是 Syscalls#

虽然很多 ntdll.dll 导出的函数以 Nt 开头并返回 NTSTATUS,但它们不一定都是系统调用。

  • Syscall:具有 SSN,且最终会通过 syscall 指令进入内核。
  • 常规 NtAPI:可能只是 ntdll 内部的一些辅助工具函数(Low-level wrapper)。

例如:

  • LdrLoadDll:这是 LoadLibrary 底层使用的函数,但它本身不是 Syscall,它内部会去调用其他的 Syscall。
  • SystemFunction032 (RC4):这只是个算法函数,不涉及内核服务请求。

🎯 总结#

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

  1. 内核大门:理解了 Syscall 是程序通往内核的终极关卡。
  2. SSN 原理:明白了内核是如何通过索引号(SSN)来分发任务的。
  3. 态转换:理解了 syscall 指令是如何实现用户态到内核态的瞬间切换。

📚 下一步学习

下一个模块将介绍 Syscalls - Userland Hooking。我们将学习为什么安全软件要盯着这些 Syscall 看,以及它们是如何在这些“大门”前安插“保镖”的!