系统调用之SysWhispers
一、介绍
SysWhispers 是一个通过直接系统调用绕过系统调用钩子的工具。SysWhispers 有多个版本,具有不同的特性。本文将分析各个版本之间的改进。
SysWhispers是64 位系统生成了支持直接系统调用的头文件/ASM 文件植入。它支持从 Windows XP 到 Windows 10 19042(20H2)的系统调用。受支持的 Windows 版本受到限制,因为系统调用号 (SSN) 会随着每次 Windows 更新而更改。因此,在 Windows 10 1903 上针对特定系统调用的直接系统调用实现可能与 Windows 10 1909 上的相同系统调用不兼容,由于在不同版本的 Windows 中相同的系统调用可能具有不同的 SSN,因此 SysWhispers 在运行时检查目标系统的 Windows 版本,并将 SSN 手动设置到正确版本。
二、SysWhispers分析
SysWhispers项目的当中提供一个示例:https://github.com/jthuraisamy/SysWhispers/blob/master/example-output/syscalls.asm
SysWhispers - NtMapViewOfSection 示例
NtMapViewOfSection PROC mov rax, gs:[60h] ; 将 PEB 加载到 RAX 中。 NtMapViewOfSection_Check_X_X_XXXX: ; 检查主版本号。 cmp dword ptr [rax+118h], 5 je NtMapViewOfSection_SystemCall_5_X_XXXX cmp dword ptr [rax+118h], 6 je NtMapViewOfSection_Check_6_X_XXXX cmp dword ptr [rax+118h], 10 je NtMapViewOfSection_Check_10_0_XXXX jmp NtMapViewOfSection_SystemCall_Unknown NtMapViewOfSection_Check_6_X_XXXX: ; 检查 Windows Vista/7/8 的次版本号。 cmp dword ptr [rax+11ch], 0 je NtMapViewOfSection_Check_6_0_XXXX cmp dword ptr [rax+11ch], 1 je NtMapViewOfSection_Check_6_1_XXXX cmp dword ptr [rax+11ch], 2 je NtMapViewOfSection_SystemCall_6_2_XXXX cmp dword ptr [rax+11ch], 2 je NtMapViewOfSection_SystemCall_6_3_XXXX jmp NtMapViewOfSection_SystemCall_Unknown NtMapViewOfSection_Check_6_0_XXXX: ; 检查 Windows Vista 的内部版本号。 cmp dword ptr [rax+120h], 6000 je NtMapViewOfSection_SystemCall_6_0_6000 cmp dword ptr [rax+120h], 6001 je NtMapViewOfSection_SystemCall_6_0_6001 cmp dword ptr [rax+120h], 6002 je NtMapViewOfSection_SystemCall_6_0_6002 jmp NtMapViewOfSection_SystemCall_Unknown NtMapViewOfSection_Check_6_1_XXXX: ; 检查 Windows 7 的内部版本号。 cmp dword ptr [rax+120h], 7600 je NtMapViewOfSection_SystemCall_6_1_7600 cmp dword ptr [rax+120h], 7601 je NtMapViewOfSection_SystemCall_6_1_7601 jmp NtMapViewOfSection_SystemCall_Unknown NtMapViewOfSection_Check_10_0_XXXX: ; 检查 Windows 10 的内部版本号。 cmp dword ptr [rax+120h], 10240 je NtMapViewOfSection_SystemCall_10_0_10240 cmp dword ptr [rax+120h], 10586 je NtMapViewOfSection_SystemCall_10_0_10586 cmp dword ptr [rax+120h], 14393 je NtMapViewOfSection_SystemCall_10_0_14393 cmp dword ptr [rax+120h], 15063 je NtMapViewOfSection_SystemCall_10_0_15063 cmp dword ptr [rax+120h], 16299 je NtMapViewOfSection_SystemCall_10_0_16299 cmp dword ptr [rax+120h], 17134 je NtMapViewOfSection_SystemCall_10_0_17134 cmp dword ptr [rax+120h], 17763 je NtMapViewOfSection_SystemCall_10_0_17763 cmp dword ptr [rax+120h], 18362 je NtMapViewOfSection_SystemCall_10_0_18362 cmp dword ptr [rax+120h], 18363 je NtMapViewOfSection_SystemCall_10_0_18363 jmp NtMapViewOfSection_SystemCall_Unknown NtMapViewOfSection_SystemCall_5_X_XXXX: ; Windows XP 和 Server 2003 mov eax, 0025h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_6_0_6000: ; Windows Vista SP0 mov eax, 0025h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_6_0_6001: ; Windows Vista SP1 和 Server 2008 SP0 mov eax, 0025h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_6_0_6002: ; Windows Vista SP2 和 Server 2008 SP2 mov eax, 0025h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_6_1_7600: ; Windows 7 SP0 mov eax, 0025h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_6_1_7601: ; Windows 7 SP1 和 Server 2008 R2 SP0 mov eax, 0025h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_6_2_XXXX: ; Windows 8 和 Server 2012 mov eax, 0026h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_6_3_XXXX: ; Windows 8.1 和 Server 2012 R2 mov eax, 0027h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_10240: ; Windows 10.0.10240 (1507) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_10586: ; Windows 10.0.10586 (1511) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_14393: ; Windows 10.0.14393 (1607) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_15063: ; Windows 10.0.15063 (1703) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_16299: ; Windows 10.0.16299 (1709) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_17134: ; Windows 10.0.17134 (1803) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_17763: ; Windows 10.0.17763 (1809) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_18362: ; Windows 10.0.18362 (1903) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_10_0_18363: ; Windows 10.0.18363 (1909) mov eax, 0028h jmp NtMapViewOfSection_Epilogue NtMapViewOfSection_SystemCall_Unknown: ; 未知/不支持的版本。 ret NtMapViewOfSection_Epilogue: mov r10, rcx syscall ret NtMapViewOfSection ENDP
PEB 结构包含三个可用于确定 Windows 操作系统版本信息成员:
1. OSMajorVersion
- 类型:
DWORD - 作用: 该字段存储 Windows 操作系统的主版本号。例如:
- Windows 7 对应的主版本号是
6。 - Windows 10 对应的主版本号是
10。
- Windows 7 对应的主版本号是
2. OSMinorVersion
- 类型:
DWORD - 作用: 该字段存储 Windows 操作系统的次版本号。例如:
- Windows 7 SP1 对应的次版本号是
1。 - Windows 8 对应的次版本号是
2。 - Windows 10 对应的次版本号可能为
0或更高的版本号(取决于 Windows 10 的不同版本)。
- Windows 7 SP1 对应的次版本号是
3. OSBuildNumber
- 类型:
DWORD - 作用: 该字段存储操作系统的构建号,也就是操作系统的具体版本。例如:
- Windows 7 SP1 的构建号是
7601。 - Windows 10 版本 15063(1703)对应的构建号是
15063。 - Windows 10 版本 1909 对应的构建号是
18363。
- Windows 7 SP1 的构建号是
SysWhispers 生成的 64 位汇编函数使用这些成员跳转到硬编码值 SSN 所在的位置。使用的逻辑本质上是若干 if 和 else if 语句。例如,如果目标计算机是 Windows 10 1809,则出现以下逻辑:
-
由于 PEB 的 主版本 成员等于 10,执行
NtMapViewOfSection_Check_10_0_XXXX标签。 -
此标签接着检查系统的 生成版本号。在这个示例中,这个数字是 1809,这会使其跳转到
NtMapViewOfSection_SystemCall_10_0_17763标签。 -
然后,SSN 设置为
0028h -
最终跳转到
NtMapViewOfSection_Epilogue标签,在那里执行剩余的系统调用指令。回忆一下,系统调用函数的格式如下:
mov r10, rcx mov eax, SSN syscall ret
三、SysWhispers2分析
SysWhispers2采用了名为“按系统调用地址排序”的方法。此方法消除了汇编指令在运行时手动选择 SSN 的需要,从而缩小了系统调用存根。 按系统调用地址排序
按系统调用地址排序是一种在运行时获取系统调用 SSN 的方法。此方法通过查找所有以 “Zw” 开头的系统调用并将其地址存储在一个数组中,然后按升序(最小地址到最大地址)对这些地址进行排序来实现。SSN 将成为存储在数组中系统调用的索引。
举例说明:
将系统调用号(SSN)作为数组的索引: 由于这些系统调用的地址已经按升序排列,现在可以将它们的“索引”作为它们的 SSN。 例如,假设我们为这些地址分配一个索引: 索引 0 对应 ZwCreateFile 地址 0x7FFD12345678 索引 1 对应 ZwQuerySystemInformation 地址 0x7FFD12345690 索引 2 对应 ZwReadFile 地址 0x7FFD12345700 索引 3 对应 ZwWriteFile 地址 0x7FFD12345750 在这种情况下,SSN 就是这个数组的索引。因此: SSN 为 0 的系统调用是 ZwCreateFile SSN 为 1 的系统调用是 ZwQuerySystemInformation SSN 为 2 的系统调用是 ZwReadFile SSN 为 3 的系统调用是 ZwWriteFile
https://github.com/jthuraisamy/SysWhispers2/blob/main/example-output/Syscalls.c
SW2_PopulateSyscallList 函数对系统调用地址进行排序,该函数 获取 NTDLL 的基地址和其导出目录。利用该信息,它 计算导出函数的 VA(地址、名称、序号) ,接下来,SysWhispers2 检查导出的函数名称,寻找以 Zw 为前缀的函数名称。这些函数名称会被哈希,并与它们的地址一起保存在 数组中。之后,SW2_PopulateSyscallList 会按 升序对收集到的地址进行排序
为了找到系统调用的 SSN,SW2_GetSyscallNumber函数会获取目标系统调用名称的哈希,并返回索引,表示在数组中找到此系统调用哈希的位置。此索引值是系统调用的 SSN

SysWhispers2 用于为 NtMapViewOfSection 生成直接系统调用。
.data
currentHash DWORD 0
.code
EXTERN SW2_GetSyscallNumber: PROC
WhisperMain PROC
pop rax
mov [rsp+ 8], rcx ; 保存寄存器。
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
mov ecx, currentHash
call SW2_GetSyscallNumber
add rsp, 28h
mov rcx, [rsp+ 8] ; 还原寄存器。
mov rdx, [rsp+16]
mov r8, [rsp+24]
mov r9, [rsp+32]
mov r10, rcx
syscall ; 发出系统调用
ret
WhisperMain ENDP
NtMapViewOfSection PROC
mov currentHash, 060C9AE95h ; 将函数哈希加载到全局变量中。
call WhisperMain ; 将函数哈希解析为系统调用号并进行调用
NtMapViewOfSection ENDP
end
060C9AE95h 是 ZwMapViewOfSection 字符串的十六进制哈希值。调用 NtMapViewOfSection 时,会先将该哈希值加载到全局变量 currentHash 中,然后调用 WhisperMain。WhisperMain 函数负责调用前面介绍的 C 函数 SW2_GetSyscallNumber,该函数将使用哈希值为 currentHash 的系统调用返回 SSN
mov [rsp+XX], XXX 指令用于在调用 SW2_GetSyscallNumber 之前将寄存器保存到堆栈中,而 mov XXX, [rsp+ XX] 指令用于将寄存器恢复到调用 SW2_GetSyscallNumber 之前的状态。之所以需要这样做,是因为调用 SW2_GetSyscallNumber 会更改这些寄存器的值。最后,在 WhisperMain 函数的末尾,出现了常见的系统调用指令:
mov r10, rcx syscall ret
请注意,这里缺少 mov eax, SSN 指令。这是因为当一个函数被调用时,其返回输出会存储在 eax 寄存器中。由于在这些指令之前已经调用了 SW2_GetSyscallNumber,这意味着 SSN 已经存储在 eax 寄存器中。
本文来自博客园,作者:aoaoaoao,转载请注明原文链接:https://www.cnblogs.com/websecyw/p/18704592

浙公网安备 33010602011771号