add NtAllocateVirtualMemory and NtProtectVirtualMemory syscalls

I used SysWhispers2, so it should work for all versions of Windows.
This is of course to bypass userland hooks. VirtualProtect is likely to
get denied by an EDR
This commit is contained in:
physics-sp
2021-07-21 18:10:31 -03:00
parent 9f881738d0
commit a29eb09550
7 changed files with 265 additions and 16 deletions
+3
View File
@@ -152,12 +152,15 @@
<ClCompile Include="src\ldrutils.c" />
<ClCompile Include="src\main.c" />
<ClCompile Include="src\pebutils.c" />
<ClCompile Include="src\syscalls.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\darkloadlibrary.h" />
<ClInclude Include="include\ldrutils.h" />
<ClInclude Include="include\pebstructs.h" />
<ClInclude Include="include\pebutils.h" />
<ClInclude Include="include\syscalls.h" />
<ClInclude Include="include\syscallsstubs.asm" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
+72
View File
@@ -0,0 +1,72 @@
#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 SW2_HEADER_H_
#define SW2_HEADER_H_
#include <Windows.h>
#define SW2_SEED 0x63191199
#define SW2_ROL8(v) (v << 8 | v >> 24)
#define SW2_ROR8(v) (v >> 8 | v << 24)
#define SW2_ROX8(v) ((SW2_SEED % 2) ? SW2_ROL8(v) : SW2_ROR8(v))
#define SW2_MAX_ENTRIES 500
#define SW2_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)
// Typedefs are prefixed to avoid pollution.
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;
typedef struct _SW2_PEB_LDR_DATA {
BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} SW2_PEB_LDR_DATA, *PSW2_PEB_LDR_DATA;
typedef struct _SW2_LDR_DATA_TABLE_ENTRY {
PVOID Reserved1[2];
LIST_ENTRY InMemoryOrderLinks;
PVOID Reserved2[2];
PVOID DllBase;
} SW2_LDR_DATA_TABLE_ENTRY, *PSW2_LDR_DATA_TABLE_ENTRY;
typedef struct _SW2_PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PSW2_PEB_LDR_DATA Ldr;
} SW2_PEB, *PSW2_PEB;
DWORD SW2_HashSyscall(PCSTR FunctionName);
BOOL SW2_PopulateSyscallList();
EXTERN_C DWORD SW2_GetSyscallNumber(DWORD FunctionHash);
EXTERN_C NTSTATUS NtProtectVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID * BaseAddress,
IN OUT PSIZE_T RegionSize,
IN ULONG NewProtect,
OUT PULONG OldProtect);
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);
#endif
+41
View File
@@ -0,0 +1,41 @@
.code
EXTERN SW2_GetSyscallNumber: PROC
NtProtectVirtualMemory PROC
mov [rsp +8], rcx ; Save registers.
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
mov ecx, 0079D1B09h ; Load function hash into ECX.
call SW2_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
syscall ; Invoke system call.
ret
NtProtectVirtualMemory ENDP
NtAllocateVirtualMemory PROC
mov [rsp +8], rcx ; Save registers.
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
mov ecx, 00B9D010Fh ; Load function hash into ECX.
call SW2_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
syscall ; Invoke system call.
ret
NtAllocateVirtualMemory ENDP
end
+27 -15
View File
@@ -36,24 +36,30 @@ BOOL MapSections(
);
// try get prefered address
pdModule->ModuleBase = (ULONG_PTR)VirtualAlloc(
(LPVOID)(pNtHeaders->OptionalHeader.ImageBase),
(SIZE_T)pNtHeaders->OptionalHeader.SizeOfImage,
pdModule->ModuleBase = pNtHeaders->OptionalHeader.ImageBase;
SIZE_T RegionSize = pNtHeaders->OptionalHeader.SizeOfImage;
NTSTATUS status = NtAllocateVirtualMemory(
(HANDLE)-1,
&pdModule->ModuleBase,
0,
&RegionSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE
);
if (!pdModule->ModuleBase)
);
if (!NT_SUCCESS(status) || pdModule->ModuleBase != pNtHeaders->OptionalHeader.ImageBase)
{
pdModule->ModuleBase = (ULONG_PTR)VirtualAlloc(
pdModule->ModuleBase = NULL;
RegionSize = pNtHeaders->OptionalHeader.SizeOfImage;
status = NtAllocateVirtualMemory(
(HANDLE)-1,
&pdModule->ModuleBase,
0,
(SIZE_T)pNtHeaders->OptionalHeader.SizeOfImage,
&RegionSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE
);
}
if (!pdModule->ModuleBase)
if (!NT_SUCCESS(status))
{
return FALSE;
}
@@ -342,13 +348,19 @@ BOOL BeginExecution(
{
dwProtect |= PAGE_NOCACHE;
}
VirtualProtect(
(LPVOID)(pdModule->ModuleBase + pSectionHeader->VirtualAddress),
pSectionHeader->SizeOfRawData,
PVOID BaseAddress = pdModule->ModuleBase + pSectionHeader->VirtualAddress;
SIZE_T RegionSize = pSectionHeader->SizeOfRawData;
NTSTATUS status = NtProtectVirtualMemory(
(HANDLE)-1,
&BaseAddress,
&RegionSize,
dwProtect,
&dwProtect
);
);
if (!NT_SUCCESS(status))
{
return FALSE;
}
}
}
+1
View File
@@ -2,6 +2,7 @@
#include <windows.h>
#include "darkloadlibrary.h"
#include "syscalls.h"
typedef DWORD (WINAPI * _ThisIsAFunction) (LPCWSTR);
+120
View File
@@ -0,0 +1,120 @@
#include "syscalls.h"
// 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
SW2_SYSCALL_LIST SW2_SyscallList;
DWORD SW2_HashSyscall(PCSTR FunctionName)
{
DWORD i = 0;
DWORD Hash = SW2_SEED;
while (FunctionName[i])
{
WORD PartialName = *(WORD*)((ULONG64)FunctionName + i++);
Hash ^= PartialName + SW2_ROR8(Hash);
}
return Hash;
}
BOOL SW2_PopulateSyscallList()
{
// Return early if the list is already populated.
if (SW2_SyscallList.Count) return TRUE;
PSW2_PEB Peb = (PSW2_PEB)__readgsqword(0x60);
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;
}
if (!ExportDirectory) return FALSE;
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 (DWORD 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;
}
}
}
return TRUE;
}
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;
}