系统调用简介
什么是系统调用
Windows系统调用使它们能够请求特定服务,例如读取或者写入文件,创建新的进程以及分配内存等等,我们之前使用的一些API都是R3层也就是用户层的API,比如VirtualAlloc,VirtualAllocEx等等,当我们去调用这些API函数的时候会触发NtAllocateVirtualMemory的系统调用,然后系统调用会将用户在上一个函数调用中提供的参数给到Windows内核中,执行请求的操作也会返回给程序。
所有的系统调用都会返回一个NTSTATUS值。
大多数的系统调用都是从NTDLL.DLL中进行导出的。
为什么要使用系统调用
使用系统调用可以对操作系统的底层进行访问,这对于执行R3层的Windows API来说一些无法执行的操作和复杂的操作都是很有利的,比如说NtCreateUserProcess系统调用在创建CreateProcess Win API无法提供的进程时提供了其他的选项。
还有就是系统调用可以用于规避EDR相关的设备,比如说如果EDR在用户层挂的钩子,那么我们直接使用系统调用就可以绕过。
Zw与Nt系统调用
系统调用有两种类型需要了解,一种是Nt开头的,一种是zW开头的,其实无论是Nt开头还是zW开头功能都是一样的,不同的是Nt开头的系统调用是用户模式程序的主要接口,就比如说VirtualAlloc申请内存等操作,而zW开头的系统调用是操作系统的低级内核模式接口,一般的话需要直接访问操作系统的设备驱动或其他内核模式代码使用。
简单来说zW系统调用用于设备驱动开发中的内核模块,而Nt系统调用用于用户模式程序执行,如下图可以看到同一系统调用的zW和Nt版本共享相同的函数地址。
系统调用号
这里系统调用号我们可以在x32dbg中看到。
跟进这个call,这里将18给了eax寄存器,这里的18就是系统调用号,后续我们会用到这个。
不同的系统调用号
需要注意的是系统调用号会根据操作系统的不同而有所不同的,比如NtAllocateVirtualMemory在win10上调用号是18,到了win11就变成了19了。
Syscall
在系统中,调用号不是随意的,而是每次都加1的表示方式。
就比如说NtAllocateVirtualMemory的系统调用号是18,ZwQueryInformationProcess就是19。
Syscall结构
上面的图我们会发现它们的结构其实是一样的,只是系统调用号不同罢了。
mov r10,rcx
mov eax,系统调用号
syscall
比如NtAllocateVirtualMemory的调用号就是18。
汇编指令解释
首先将保存在RCE中的第一个参数移动到R10寄存器中,然后将系统调用号放到eax寄存器中,最后执行syscall指令。
64位系统上的系统调用指令或32位系统上的系统调指令是启动系统调用的指令。执行系统共享指令将导致程序将控制从用户模式转移到内核模式。然后,内核将执行所请求的操作,并将控制返回到用户模式程序
并不是所有的NT API都是系统调用
这里需要注意的是虽然某些NtAPI返回NTSTATUS,但是它们不一定都是系统调用,这些NtAPI可能是Win API或系统调用使用的较低级别的函数,某些NtAPI不被归类为系统调用的原因是他们不符号系统调用的结构,比如说没有调用调用号或开头缺少mov r10,rcx等指令。
如下API非系统调用的NtAPI示例:
LdrLoadDll - LoadLibrary WinAPI使用它来将DLL加载到调用进程
SystemFunction032和SystemFunction033这些NTAPI是用于RC4加密和解密的操作。
LdrLoadDll
可以看到它并没有遵循我们上面说到的系统调用的结构,比如mov r10,rcx。
系统调用-用户态挂钩
简介
目前大部分安全厂商都会在系统调用上执行API挂钩,以便运行时分析和监控程序的行为,比如可以挂钩NtProtectVirtualMemory系统调用,这样的话就可以检测到更高级别的Win API调用,比如说VirtualProtect,即使他在导入地址表中隐藏了名称,也是可以检测到的,一般的话用户态钩子通常安装在syscall指令之前,这是用户模式下系统调用的最后一步。
绕过用户态挂钩
直接使用系统调用是绕过用户态挂钩的一种方法,比如说在分配内存的时候使用NtAllocateVirtualMemory而不是直接使用VirtualAlloc/Ex 这些Win API函数,还有几种可以绕过用户态挂钩的方法。
使用直接系统调用
使用间接系统调用
脱钩
直接系统调用
直接系统调用可以绕过对用户态系统调用挂钩EDR等设备,现在的问题是我们如何去确定系统调用编号,因为操作系统的不同所以编号也就不同,调用编号可以在硬编码在二进制文件中,也可以在运行时动态计算。
如下汇编代码可以用于相同的结果,这样的话就不需要从安装了挂钩的NTDLL地址空间内调用,从而避免了挂钩。
NtAllocateVirtualMemory PROC
mov r10,rcx
mov eax,系统调用号
syscall
ret
NtAllocateVirtualMemory ENDP
NtProtectVirtualMemory PROC
mov r10,rcx
mov eax,系统调用号
syscall
ret
NtProtectVirtualMemory ENDP
间接系统调用
间接系统调用的实现方式和直接系统调用是相差不多的,唯一的区别就是汇编代码中缺少了syscall指令,因为一些EDR会检测我们二进制文件中是否有syscall,这样的话间接系统调用就避免了这个问题。
如下图描述了间接系统调用:
汇编如下:
NtAllocateVirtualMemory PROC
mov r10,rcx
mov eax,系统调用号
jmp (Address) syscall的地址
ret
NtAllocateVirtualMemory ENDP
NtProtectVirtualMemory PROC
mov r10,rcx
mov eax,系统调用号
jmp (Address) syscall的地址
ret
NtProtectVirtualMemory ENDP
间接系统调用的好处
间接系统调用与直接系统调用的好处在于AV/EDR查找如果NTDLL地址空间外部调用的系统调用,它们会认为是可疑的,对于间接系统调用来说,系统调用指令从NTDLL的地址空间执行,就像正常的系统调用一样,所以间接系统调用比直接系统调用更容易绕过EDR。
脱钩
取消挂钩是另一种逃避挂钩的方法,也就是说加载到内存的挂钩NTDLL库被未挂钩的版本进行了替换,比如说NTDLL这个DLL模块的内存,被我们通过硬盘上的NTDLL.DLL进行了替换,这样的话就可以删除NTDLL库中放置的所有挂钩。
系统调用-SysWhispers
简介
SysWhispers是一种方便于我们通过直接系统调用来逃避系统调用挂钩的一个工具,他有多个版本。
SysWhispers下载地址:
https://github.com/jthuraisamy/SysWhispers
这里生成一个NtAllocateVirtualMemory函数的syscall。
python .syswhispers.py -f NtAllocateVirtualMemory -o syscalls
这里他会生成两个文件一个是头文件一个是asm文件。
我们打开asm文件如下:
这边它首先会去判断操作系统的版本来决定使用内置的函数调用号,最后直接系统调用。
.code
NtAllocateVirtualMemory PROC
mov rax, gs:[60h] ; Load PEB into RAX.
NtAllocateVirtualMemory_Check_X_X_XXXX: ; Check major version.
cmp dword ptr [rax+118h], 5
je NtAllocateVirtualMemory_SystemCall_5_X_XXXX
cmp dword ptr [rax+118h], 6
je NtAllocateVirtualMemory_Check_6_X_XXXX
cmp dword ptr [rax+118h], 10
je NtAllocateVirtualMemory_Check_10_0_XXXX
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_Check_6_X_XXXX: ; Check minor version for Windows Vista/7/8.
cmp dword ptr [rax+11ch], 0
je NtAllocateVirtualMemory_Check_6_0_XXXX
cmp dword ptr [rax+11ch], 1
je NtAllocateVirtualMemory_Check_6_1_XXXX
cmp dword ptr [rax+11ch], 2
je NtAllocateVirtualMemory_SystemCall_6_2_XXXX
cmp dword ptr [rax+11ch], 3
je NtAllocateVirtualMemory_SystemCall_6_3_XXXX
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_Check_6_0_XXXX: ; Check build number for Windows Vista.
cmp word ptr [rax+120h], 6000
je NtAllocateVirtualMemory_SystemCall_6_0_6000
cmp word ptr [rax+120h], 6001
je NtAllocateVirtualMemory_SystemCall_6_0_6001
cmp word ptr [rax+120h], 6002
je NtAllocateVirtualMemory_SystemCall_6_0_6002
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_Check_6_1_XXXX: ; Check build number for Windows 7.
cmp word ptr [rax+120h], 7600
je NtAllocateVirtualMemory_SystemCall_6_1_7600
cmp word ptr [rax+120h], 7601
je NtAllocateVirtualMemory_SystemCall_6_1_7601
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_Check_10_0_XXXX: ; Check build number for Windows 10.
cmp word ptr [rax+120h], 10240
je NtAllocateVirtualMemory_SystemCall_10_0_10240
cmp word ptr [rax+120h], 10586
je NtAllocateVirtualMemory_SystemCall_10_0_10586
cmp word ptr [rax+120h], 14393
je NtAllocateVirtualMemory_SystemCall_10_0_14393
cmp word ptr [rax+120h], 15063
je NtAllocateVirtualMemory_SystemCall_10_0_15063
cmp word ptr [rax+120h], 16299
je NtAllocateVirtualMemory_SystemCall_10_0_16299
cmp word ptr [rax+120h], 17134
je NtAllocateVirtualMemory_SystemCall_10_0_17134
cmp word ptr [rax+120h], 17763
je NtAllocateVirtualMemory_SystemCall_10_0_17763
cmp word ptr [rax+120h], 18362
je NtAllocateVirtualMemory_SystemCall_10_0_18362
cmp word ptr [rax+120h], 18363
je NtAllocateVirtualMemory_SystemCall_10_0_18363
cmp word ptr [rax+120h], 19041
je NtAllocateVirtualMemory_SystemCall_10_0_19041
cmp word ptr [rax+120h], 19042
je NtAllocateVirtualMemory_SystemCall_10_0_19042
cmp word ptr [rax+120h], 19043
je NtAllocateVirtualMemory_SystemCall_10_0_19043
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_SystemCall_5_X_XXXX: ; Windows XP and Server 2003
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_0_6000: ; Windows Vista SP0
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_0_6001: ; Windows Vista SP1 and Server 2008 SP0
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_0_6002: ; Windows Vista SP2 and Server 2008 SP2
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_1_7600: ; Windows 7 SP0
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_1_7601: ; Windows 7 SP1 and Server 2008 R2 SP0
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_2_XXXX: ; Windows 8 and Server 2012
mov eax, 0016h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_3_XXXX: ; Windows 8.1 and Server 2012 R2
mov eax, 0017h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_10240: ; Windows 10.0.10240 (1507)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_10586: ; Windows 10.0.10586 (1511)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_14393: ; Windows 10.0.14393 (1607)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_15063: ; Windows 10.0.15063 (1703)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_16299: ; Windows 10.0.16299 (1709)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_17134: ; Windows 10.0.17134 (1803)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_17763: ; Windows 10.0.17763 (1809)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_18362: ; Windows 10.0.18362 (1903)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_18363: ; Windows 10.0.18363 (1909)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_19041: ; Windows 10.0.19041 (2004)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_19042: ; Windows 10.0.19042 (20H2)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_19043: ; Windows 10.0.19043 (21H1)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_Unknown: ; Unknown/unsupported version.
ret
NtAllocateVirtualMemory_Epilogue:
mov r10, rcx
syscall
ret
NtAllocateVirtualMemory ENDP
end
.h文件中声明了函数原型。
#pragma once
#include <Windows.h>
EXTERN_C NTSTATUS NtAllocateVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID * BaseAddress,
IN ULONG ZeroBits,
IN OUT PSIZE_T RegionSize,
IN ULONG AllocationType,
IN ULONG Protect);
SysWhispers2
SysWhispers2是比较好用的,下载地址:
https://github.com/jthuraisamy/SysWhispers2
下载之后执行:
python .syswhispers.py --functions NtAllocateVirtualMemory,NtWriteVirtualMemory -o syscall_enm
生成之后将syscall_enm_stubs.std.x64.asm复制到项目中。
然后将头文件syscall_enm.h复制到项目中以及syscall_enm.c文件。
这三个复制到项目中之后,我们就可以正常使用函数进行调用了。
可以看到我上面没有定义任何导出。
正常上线:
SysWhispers3
SysWhispers3和SysWhispers2是差不多的。
python syswhispers.py --functions NtWriteVirtualMemory,NtAllocateVirtualMemory -o syscalls_mem -m jumper_randomized
还是一样会生成3个文件一个是头文件,一个是asm文件,还有一个就是.c文件了。
原文始发于微信公众号(Relay学安全):系统调用总结(1)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论