直接系统调用学习记录
字数 1325 2025-08-20 18:17:47
直接系统调用技术详解与实现
1. 直接系统调用概述
直接系统调用(Direct System Call)是Windows系统上的一种技术,允许用户模式程序直接进入内核模式执行特权操作,而无需通过常规的API调用链(kernel32.dll → ntdll.dll → 系统调用)。
核心优势:
- 绕过EDR(终端检测与响应)在用户层的钩子(Hook)
- 减少API调用痕迹,提高隐蔽性
- 直接与内核交互,执行效率更高
2. EDR检测机制分析
现代EDR解决方案通常在Ring 3层(用户模式)实现钩子技术:
- 通过修改API函数的前几个字节(操作码和操作数)
- 将执行流重定向到EDR的hook.dll
- 在hook.dll中分析API调用行为是否恶意
常规调用流程(被EDR监控):
应用程序 → kernel32.dll → ntdll.dll → EDR钩子 → 系统调用
直接系统调用流程(绕过EDR):
应用程序 → 直接系统调用指令 → 内核模式
3. 技术实现原理
系统调用号获取
每个Windows系统函数都有一个唯一的系统调用号(SSN, System Service Number),存储在ntdll.dll中对应函数的第4个字节。
汇编核心代码
mov r10, rcx ; 将第一个参数保存到r10寄存器
mov eax, SSN ; 将系统调用号存入eax
syscall ; 执行系统调用指令
ret ; 返回
4. 实现步骤详解
准备工作
- 获取SysWhispers2项目(https://github.com/jthuraisamy/SysWhispers2)
- 主要使用其中的syscalls.h和汇编文件
C++实现流程
- 获取ntdll.dll的模块句柄
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
- 获取目标函数地址并读取系统调用号
FARPROC pFunc = GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
DWORD ssn = *((PDWORD)((PBYTE)pFunc + 4)); // 第4字节为SSN
-
为所有需要的函数重复上述操作
-
使用汇编代码执行直接系统调用
MASM汇编实现
EXTERN用于全局访问变量 包含了对应的SSN
5. 关键注意事项
- 系统兼容性:不同Windows版本的系统调用号可能不同
- 参数传递:必须严格按照x64调用约定准备参数
- 错误处理:直接系统调用不会设置LastError,需要自行处理
- 检测规避:现代EDR可能监控syscall指令的使用,需要进一步混淆
6. 防御对抗演进
随着直接系统调用技术的普及,EDR厂商已发展出新的检测手段:
- 监控异常的系统调用来源(非ntdll.dll发起的syscall)
- 分析调用栈的完整性
- 检测syscall指令的上下文
应对方法包括:
- 动态生成syscall存根
- 混合使用直接和间接调用
- 调用栈混淆
7. 实际应用示例
以下是一个简化的直接系统调用实现框架:
#include <windows.h>
// 定义函数类型
typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
PSIZE_T RegionSize,
ULONG AllocationType,
ULONG Protect);
// 获取SSN的函数
DWORD GetSSN(LPCSTR funcName) {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
FARPROC pFunc = GetProcAddress(hNtdll, funcName);
return *((PDWORD)((PBYTE)pFunc + 4));
}
// 直接系统调用执行函数
extern "C" NTSTATUS DirectNtAllocateVirtualMemory(
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
PSIZE_T RegionSize,
ULONG AllocationType,
ULONG Protect) {
DWORD ssn = GetSSN("NtAllocateVirtualMemory");
__asm {
mov r10, rcx
mov eax, ssn
syscall
ret
}
}
int main() {
// 使用直接系统调用分配内存
PVOID baseAddr = nullptr;
SIZE_T size = 0x1000;
DirectNtAllocateVirtualMemory(
GetCurrentProcess(),
&baseAddr,
0,
&size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
// 使用分配的内存...
return 0;
}
8. 进阶技术方向
- 动态SSN解析:运行时解析ntdll.dll获取最新SSN,提高兼容性
- 间接系统调用:通过合法模块中的syscall指令间接执行
- 硬件断点绕过:使用硬件断点检测EDR的监控并规避
- VDSO调用:利用Linux风格的vDSO机制进行系统调用
9. 资源推荐
- SysWhispers2:https://github.com/jthuraisamy/SysWhispers2
- HellsGate:动态SSN解析技术
- HalosGate:针对HellsGate的改进版
- FreshyCalls:另一种直接系统调用实现方式
10. 法律与道德声明
直接系统调用技术本身是中性的,但可能被用于恶意目的。本技术文档仅用于安全研究和防御技术研究,任何使用此技术进行的未经授权系统访问均属违法。安全研究人员应在合法授权范围内进行相关测试和研究。