跳至主要內容

63-系统调用 - 简介

Maldevacademy大约 5 分钟安全开发

什么是系统调用

Windows 系统调用或 syscall 是程序与系统交互的接口,它允许程序请求特定服务,例如读写文件、创建新进程或分配内存。可以回想一下导论模块中提到的内容,syscall 是在调用 WinAPI 函数时执行操作的 API。例如,当调用 WinAPI 函数 VirtualAllocVirtualAllocEx 时,会触发 NtAllocateVirtualMemory syscall。然后,此 syscall 将用户在前次函数调用中提供的参数移动到 Windows 内核,执行请求的操作并将结果返回给程序。

所有 syscall 都返回一个 NTSTATUS 值open in new window,表示错误代码。如果 syscall 成功执行操作,则返回 STATUS_SUCCESS (零)。

大多数 syscall 没有由 Microsoft 记录下来,因此 syscall 模块将引用以下所示的文档。

NTDLL 和系统调用

大多数系统调用由 ntdll.dll DLL 导出。

为什么使用系统调用

使用系统调用可以低级访问操作系统,这对于执行通过标准 WinAPI 无法执行或更复杂的操作很有利。例如,NtCreateUserProcess 系统调用在创建进程时提供了 CreateProcess WinAPI 所不能提供的更多选项。

此外,系统调用还可用于规避后续模块中将讨论的主机安全解决方案。

Zw 与 Nt 系统调用

系统调用分为两种,一种以 Nt 开头,另一种以 Zw 开头。

NT 系统调用是用户模式程序的主要接口。这些是大多数 Windows 程序通常使用的系统调用。

Zw 系统调用则是操作系统的一个底层、内核模式接口。通常用于设备驱动程序和其他需要直接访问操作系统功能的内核模式代码。

总结一下,Zw 系统调用在内核模式中用于设备驱动程序开发,而 Nt 系统调用则在用户模式程序中执行。虽然也可以在用户模式程序中同时使用这两种调用,并仍然获得相同的结果。在下图中可以看出,ZwNt 版本的相同系统调用共享相同的函数地址。

3113
3113
3223
3223

为了本课程的简单性,将只使用 Nt 系统调用。

系统调用服务号

每个系统调用都有一个特殊的系统调用号,称为系统服务号 (SSN)。内核正是使用这些系统调用号来区分不同系统调用。例如,NtAllocateVirtualMemory 系统调用的 SSN 为 24,而 NtProtectVirtualMemory 的 SSN 为 80,内核就是使用这些数字来区分 NtAllocateVirtualMemoryNtProtectVirtualMemory

SSN 在不同操作系统中的差异

需要注意的是,SSN 会因操作系统 (如 Windows 10 与 11) 的差异而不同,甚至在同一版本的系统中也会不同 (如 Windows 11 21h2 与 Windows 11 22h2)。以上文提到的示例,NtAllocateVirtualMemory 在一个 Windows 版本中的 SSN 可能为 24,而在另一个版本中则为 34。这种情况也适用于 NtProtectVirtualMemory 及其他系统调用。

术语解释:

  • SSN:系统调用编号 (System call number)

内存中的系统调用

在一台计算机中,SSN 并非完全随意指定,并且彼此之间存在关联关系。内存中的每个系统调用号等于前面的 SSN 加 1。例如,系统调用 B 的 SSN 等于系统调用 A 的 SSN 加 1。这种情况在从另一端处理系统调用时也成立,其中系统调用 C 的 SSN 将等于系统调用 D 的 SSN 减 1。

下图展示了此关系,其中 ZwAccessCheck 的 SSN 为 0,下一个系统调用 NtWorkerFactoryWorkerReady 的 SSN 为 1,以此类推。

image
image

了解系统调用彼此之间存在关联关系在即将到来的系统调用模块的规避中将派上用场。

系统调用结构

系统调用结构通常相同,如下面的代码段所示:

mov r10, rcx
mov eax, SSN
syscall

例如,下面显示了 64 位系统上的 NtAllocateVirtualMemory

image
image

下面显示了 NtProtectVirtualMemory

image
image

系统调用指令说明

syscall的第一条指令将RCX中保存的第一个参数值移至R10寄存器。随后,syscall的SSN被移至EAX寄存器。最后,执行特殊的syscall指令。

64 位系统上的syscall指令或 32 位系统上的sysenter是启动系统调用的指令。执行syscall指令将使程序将控制权从用户模式转移到内核模式。然后,内核将执行请求的操作并在完成时将控制权返回给用户模式程序。

Test和Jne指令

testjne指令用于WoW64open in new window(WoW64是允许32位进程在64位机器上运行的模拟技术),当进程是64位进程时,这些指令不会影响执行流程。

并非所有 NtAPI 都是系统调用

重要提示:虽然有些 NtAPI 会返回 NTSTATUS,但这些 API 并非一定是系统调用。这些 NtAPI 可能是用于 WinAPI 或系统调用的更低级别的函数。某些 NtAPI 未归类为系统调用的原因是它们与系统调用的结构不符,例如没有系统调用号或开始时缺少常见的 mov r10, rcx 指令。下面显示了非系统调用的 NtAPI 示例。

  • LdrLoadDll - WinAPI 中的 LoadLibrary 函数使用它将映像加载到调用进程中。

  • SystemFunction032SystemFunction033 - 这些 NtAPI 早期引入,执行 RC4 加密/解密操作。

  • RtlCreateProcessParametersEx - WinAPI 中的 CreateProcess 函数使用它创建进程的参数。

LdrLoadDll

LdrLoadDll 的指令如下所示。请注意它不遵循典型的系统调用结构。

image
image