Projects STRLCPY freeMetsrvLoader Commits c113779b
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    Makefile
     1 +BOFNAME := freeBokuLoader
     2 +CC_x64 := x86_64-w64-mingw32-gcc
     3 +CC_x86 := i686-w64-mingw32-gcc
     4 +STRIP_x64 := x86_64-w64-mingw32-strip
     5 +STRIP_x86 := i686-w64-mingw32-strip
     6 +OPTIONS := -masm=intel -Wall -I include
     7 + 
     8 +freeBokuLoader: clean
     9 + $(CC_x64) -c source/entry.c -o dist/$(BOFNAME).x64.o $(OPTIONS) -DBOF
     10 + $(STRIP_x64) --strip-unneeded dist/$(BOFNAME).x64.o
     11 + 
     12 + $(CC_x86) -c source/entry.c -o dist/$(BOFNAME).x86.o $(OPTIONS) -DBOF
     13 + $(STRIP_x86) --strip-unneeded dist/$(BOFNAME).x86.o
     14 + 
     15 +debug: clean
     16 + $(CC_x64) -c source/entry.c -o dist/$(BOFNAME).x64.o $(OPTIONS) -DBOF -DDEBUG
     17 + $(CC_x86) -c source/entry.c -o dist/$(BOFNAME).x86.o $(OPTIONS) -DBOF -DDEBUG
     18 + 
     19 +clean:
     20 + rm -f dist/*
     21 + 
  • ■ ■ ■ ■ ■ ■
    README.md
     1 +# freeBokuLoader
     2 + 
     3 +A simple BOF that tries to free the memory region where the User Defined Reflective Loader is stored.
     4 + 
     5 +As the name implies, it was tested against [BokuLoader](https://github.com/boku7/BokuLoader), but should work with most if not all UDRLs.
     6 + 
     7 +![](/images/example.png)
     8 + 
     9 + 
     10 +## Usage
     11 +```
     12 +beacon> freeBokuLoader
     13 +```
     14 + 
  • dist/freeBokuLoader.x64.o
    Binary file.
  • dist/freeBokuLoader.x86.o
    Binary file.
  • ■ ■ ■ ■ ■ ■
    freeBokuLoader.cna
     1 + 
     2 +beacon_command_register(
     3 +"freeBokuLoader",
     4 +"Free/Remove the memory region where the User Defined Reflective Loader is.",
     5 +"Usage: freeBokuLoader");
     6 + 
     7 +alias freeBokuLoader {
     8 + local('$handle $data $barch $username $args');
     9 + $barch = barch($1);
     10 + 
     11 + # pack the arguments
     12 + $args = bof_pack($1, "");
     13 + 
     14 + # read in the BOF file
     15 + $handle = openf(script_resource("dist/freeBokuLoader $+ . $+ $barch $+ .o"));
     16 + $data = readb($handle, -1);
     17 + closef($handle);
     18 + 
     19 + if(strlen($data) == 0)
     20 + {
     21 + berror($1, "could not read bof file");
     22 + return;
     23 + }
     24 + 
     25 + # run
     26 + btask($1, "freeBokuLoader BOF");
     27 + beacon_inline_execute($1, $data, "go", $args);
     28 +}
     29 + 
  • images/example.png
  • ■ ■ ■ ■ ■ ■
    include/beacon.h
     1 +/*
     2 + * Beacon Object Files (BOF)
     3 + * -------------------------
     4 + * A Beacon Object File is a light-weight post exploitation tool that runs
     5 + * with Beacon's inline-execute command.
     6 + *
     7 + * Cobalt Strike 4.1.
     8 + */
     9 +
     10 + /* data API */
     11 +typedef struct {
     12 + char* original; /* the original buffer [so we can free it] */
     13 + char* buffer; /* current pointer into our buffer */
     14 + int length; /* remaining length of data */
     15 + int size; /* total size of this buffer */
     16 +} datap;
     17 +
     18 +DECLSPEC_IMPORT void BeaconDataParse(datap* parser, char* buffer, int size);
     19 +DECLSPEC_IMPORT int BeaconDataInt(datap* parser);
     20 +DECLSPEC_IMPORT short BeaconDataShort(datap* parser);
     21 +DECLSPEC_IMPORT int BeaconDataLength(datap* parser);
     22 +DECLSPEC_IMPORT char* BeaconDataExtract(datap* parser, int* size);
     23 +
     24 +/* format API */
     25 +typedef struct {
     26 + char* original; /* the original buffer [so we can free it] */
     27 + char* buffer; /* current pointer into our buffer */
     28 + int length; /* remaining length of data */
     29 + int size; /* total size of this buffer */
     30 +} formatp;
     31 +
     32 +DECLSPEC_IMPORT void BeaconFormatAlloc(formatp* format, int maxsz);
     33 +DECLSPEC_IMPORT void BeaconFormatReset(formatp* format);
     34 +DECLSPEC_IMPORT void BeaconFormatFree(formatp* format);
     35 +DECLSPEC_IMPORT void BeaconFormatAppend(formatp* format, char* text, int len);
     36 +DECLSPEC_IMPORT void BeaconFormatPrintf(formatp* format, char* fmt, ...);
     37 +DECLSPEC_IMPORT char* BeaconFormatToString(formatp* format, int* size);
     38 +DECLSPEC_IMPORT void BeaconFormatInt(formatp* format, int value);
     39 +
     40 +/* Output Functions */
     41 +#define CALLBACK_OUTPUT 0x0
     42 +#define CALLBACK_OUTPUT_OEM 0x1e
     43 +#define CALLBACK_ERROR 0x0d
     44 +#define CALLBACK_OUTPUT_UTF8 0x20
     45 +
     46 +DECLSPEC_IMPORT void BeaconPrintf(int type, char* fmt, ...);
     47 +DECLSPEC_IMPORT void BeaconOutput(int type, char* data, int len);
     48 +
     49 +/* Token Functions */
     50 +DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token);
     51 +DECLSPEC_IMPORT void BeaconRevertToken();
     52 +DECLSPEC_IMPORT BOOL BeaconIsAdmin();
     53 +
     54 +/* Spawn+Inject Functions */
     55 +DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char* buffer, int length);
     56 +DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len);
     57 +DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len);
     58 +DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo);
     59 +
     60 +/* Utility Functions */
     61 +DECLSPEC_IMPORT BOOL toWideChar(char* src, wchar_t* dst, int max);
     62 + 
  • ■ ■ ■ ■ ■ ■
    include/entry.h
     1 + 
     2 +#include <windows.h>
     3 +#include <winternl.h>
     4 + 
     5 +PVOID get_ip(VOID);
     6 + 
     7 +BOOL free_udrl(VOID);
     8 + 
     9 +BOOL is_valid_pe(
     10 + PVOID base_address,
     11 + SIZE_T region_size);
     12 + 
     13 +BOOL free_region(
     14 + PVOID base_address,
     15 + SIZE_T region_size,
     16 + BOOL is_mapped);
     17 + 
     18 +WINBASEAPI int __cdecl MSVCRT$memcmp(const void *_Buf1,const void *_Buf2,size_t _Size);
     19 +#define memcmp MSVCRT$memcmp
     20 + 
     21 +#define RVA(type, base_addr, rva) (type)((ULONG_PTR) base_addr + rva)
     22 + 
     23 +#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 )
     24 + 
     25 +#define MZ 0x5A4D
     26 + 
     27 +#ifndef NT_SUCCESS
     28 + #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
     29 +#endif
     30 + 
     31 +#ifdef _WIN64
     32 + #define PEB_OFFSET 0x60
     33 + #define READ_MEMLOC __readgsqword
     34 +#else
     35 + #define PEB_OFFSET 0x30
     36 + #define READ_MEMLOC __readfsdword
     37 +#endif
     38 + 
     39 +#define MEM_COMMIT 0x1000
     40 +//#define MEM_IMAGE 0x1000000
     41 +#define MEM_MAPPED 0x40000
     42 +#define PAGE_NOACCESS 0x01
     43 +#define PAGE_GUARD 0x100
     44 + 
     45 +#if defined(BOF)
     46 + #define PRINT(...) { \
     47 + BeaconPrintf(CALLBACK_OUTPUT, __VA_ARGS__); \
     48 + }
     49 +#else
     50 + #define PRINT(...) { \
     51 + fprintf(stdout, __VA_ARGS__); \
     52 + fprintf(stdout, "\n"); \
     53 + }
     54 +#endif
     55 + 
     56 +#if defined(BOF)
     57 + #define PRINT_ERR(...) { \
     58 + BeaconPrintf(CALLBACK_ERROR, __VA_ARGS__); \
     59 + }
     60 +#else
     61 + #define PRINT_ERR(...) { \
     62 + fprintf(stdout, __VA_ARGS__); \
     63 + fprintf(stdout, "\n"); \
     64 + }
     65 +#endif
     66 + 
     67 +#if defined(DEBUG) && defined(BOF)
     68 + #define DPRINT(...) { \
     69 + BeaconPrintf(CALLBACK_OUTPUT, "DEBUG: %s:%d:%s(): ", __FILE__, __LINE__, __FUNCTION__); \
     70 + BeaconPrintf(CALLBACK_OUTPUT, __VA_ARGS__); \
     71 + }
     72 +#elif defined(DEBUG) && !defined(BOF)
     73 + #define DPRINT(...) { \
     74 + fprintf(stderr, "DEBUG: %s:%d:%s(): ", __FILE__, __LINE__, __FUNCTION__); \
     75 + fprintf(stderr, __VA_ARGS__); \
     76 + fprintf(stderr, "\n"); \
     77 + }
     78 +#else
     79 + #define DPRINT(...)
     80 +#endif
     81 + 
     82 +#if defined(DEBUG) && defined(BOF)
     83 + #define DPRINT_ERR(...) { \
     84 + BeaconPrintf(CALLBACK_ERROR, "ERROR: %s:%d:%s(): ", __FILE__, __LINE__, __FUNCTION__); \
     85 + BeaconPrintf(CALLBACK_ERROR, __VA_ARGS__); \
     86 + }
     87 +#elif defined(DEBUG) && !defined(BOF)
     88 + #define DPRINT_ERR(...) { \
     89 + fprintf(stderr, "ERROR: %s:%d:%s(): ", __FILE__, __LINE__, __FUNCTION__); \
     90 + fprintf(stderr, __VA_ARGS__); \
     91 + fprintf(stderr, "\n"); \
     92 + }
     93 +#else
     94 + #define DPRINT_ERR(...)
     95 +#endif
     96 + 
     97 +#ifdef _M_IX86
     98 + // x86 has conflicting types with these functions
     99 + #define NtClose _NtClose
     100 +#endif
     101 + 
  • ■ ■ ■ ■ ■ ■
    include/syscalls.h
     1 +#pragma once
     2 + 
     3 +// Code below is adapted from @modexpblog. Read linked article for more details.
     4 +// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
     5 + 
     6 +#ifndef SW2_HEADER_H_
     7 +#define SW2_HEADER_H_
     8 + 
     9 +#include <windows.h>
     10 + 
     11 +#define SW2_SEED 0x1337C0DE
     12 +#define SW2_ROL8(v) (v << 8 | v >> 24)
     13 +#define SW2_ROR8(v) (v >> 8 | v << 24)
     14 +#define SW2_ROX8(v) ((SW2_SEED % 2) ? SW2_ROL8(v) : SW2_ROR8(v))
     15 +#define SW2_MAX_ENTRIES 500
     16 +#define SW2_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)
     17 + 
     18 +// Typedefs are prefixed to avoid pollution.
     19 + 
     20 +typedef struct _SW2_SYSCALL_ENTRY
     21 +{
     22 + DWORD Hash;
     23 + DWORD Address;
     24 + PVOID SyscallAddress;
     25 +} SW2_SYSCALL_ENTRY, *PSW2_SYSCALL_ENTRY;
     26 + 
     27 +typedef struct _SW2_SYSCALL_LIST
     28 +{
     29 + DWORD Count;
     30 + SW2_SYSCALL_ENTRY Entries[SW2_MAX_ENTRIES];
     31 +} SW2_SYSCALL_LIST, *PSW2_SYSCALL_LIST;
     32 + 
     33 +typedef struct _SW2_PEB_LDR_DATA {
     34 + BYTE Reserved1[8];
     35 + PVOID Reserved2[3];
     36 + LIST_ENTRY InMemoryOrderModuleList;
     37 +} SW2_PEB_LDR_DATA, *PSW2_PEB_LDR_DATA;
     38 + 
     39 +typedef struct _SW2_LDR_DATA_TABLE_ENTRY {
     40 + PVOID Reserved1[2];
     41 + LIST_ENTRY InMemoryOrderLinks;
     42 + PVOID Reserved2[2];
     43 + PVOID DllBase;
     44 +} SW2_LDR_DATA_TABLE_ENTRY, *PSW2_LDR_DATA_TABLE_ENTRY;
     45 + 
     46 +typedef struct _SW2_PEB {
     47 + BYTE Reserved1[2];
     48 + BYTE BeingDebugged;
     49 + BYTE Reserved2[1];
     50 + PVOID Reserved3[2];
     51 + PSW2_PEB_LDR_DATA Ldr;
     52 +} SW2_PEB, *PSW2_PEB;
     53 + 
     54 +DWORD SW2_HashSyscall(
     55 + IN PCSTR FunctionName);
     56 + 
     57 +PVOID GetSyscallAddress(
     58 + IN PVOID nt_api_address,
     59 + IN ULONG32 size_of_ntapi);
     60 + 
     61 +BOOL SW2_PopulateSyscallList(VOID);
     62 + 
     63 +BOOL local_is_wow64(VOID);
     64 + 
     65 +PVOID getIP(VOID);
     66 + 
     67 +void SyscallNotFound(VOID);
     68 + 
     69 +#if defined(__GNUC__)
     70 +DWORD SW2_GetSyscallNumber(IN DWORD FunctionHash) asm ("SW2_GetSyscallNumber");
     71 +PVOID SW3_GetSyscallAddress(IN DWORD FunctionHash) asm ("SW3_GetSyscallAddress");
     72 +#else
     73 +DWORD SW2_GetSyscallNumber(IN DWORD FunctionHash);
     74 +PVOID SW3_GetSyscallAddress(IN DWORD FunctionHash);
     75 +#endif
     76 + 
     77 +//typedef struct _IO_STATUS_BLOCK
     78 +//{
     79 +// union
     80 +// {
     81 +// NTSTATUS Status;
     82 +// VOID* Pointer;
     83 +// };
     84 +// ULONG_PTR Information;
     85 +//} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
     86 + 
     87 +//typedef struct _UNICODE_STRING
     88 +//{
     89 +// USHORT Length;
     90 +// USHORT MaximumLength;
     91 +// PWSTR Buffer;
     92 +//} UNICODE_STRING, *PUNICODE_STRING;
     93 + 
     94 +#ifndef InitializeObjectAttributes
     95 +#define InitializeObjectAttributes( p, n, a, r, s ) { \
     96 + (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
     97 + (p)->RootDirectory = r; \
     98 + (p)->Attributes = a; \
     99 + (p)->ObjectName = n; \
     100 + (p)->SecurityDescriptor = s; \
     101 + (p)->SecurityQualityOfService = NULL; \
     102 +}
     103 +#endif
     104 + 
     105 +//typedef struct _OBJECT_ATTRIBUTES
     106 +//{
     107 +// ULONG Length;
     108 +// HANDLE RootDirectory;
     109 +// PUNICODE_STRING ObjectName;
     110 +// ULONG Attributes;
     111 +// PVOID SecurityDescriptor;
     112 +// PVOID SecurityQualityOfService;
     113 +//} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
     114 + 
     115 +typedef enum _MEMORY_INFORMATION_CLASS
     116 +{
     117 + MemoryBasicInformation,
     118 + MemoryWorkingSetInformation,
     119 + MemoryMappedFilenameInformation,
     120 + MemoryRegionInformation,
     121 + MemoryWorkingSetExInformation,
     122 + MemorySharedCommitInformation,
     123 + MemoryImageInformation,
     124 + MemoryRegionInformationEx,
     125 + MemoryPrivilegedBasicInformation,
     126 + MemoryEnclaveImageInformation,
     127 + MemoryBasicInformationCapped
     128 +} MEMORY_INFORMATION_CLASS, *PMEMORY_INFORMATION_CLASS;
     129 + 
     130 +//typedef struct _CLIENT_ID
     131 +//{
     132 +// HANDLE UniqueProcess;
     133 +// HANDLE UniqueThread;
     134 +//} CLIENT_ID, *PCLIENT_ID;
     135 + 
     136 +//typedef enum _PROCESSINFOCLASS
     137 +//{
     138 +// ProcessBasicInformation = 0,
     139 +// ProcessDebugPort = 7,
     140 +// ProcessWow64Information = 26,
     141 +// ProcessImageFileName = 27,
     142 +// ProcessBreakOnTermination = 29
     143 +//} PROCESSINFOCLASS, *PPROCESSINFOCLASS;
     144 + 
     145 +typedef VOID(NTAPI* PIO_APC_ROUTINE) (
     146 + IN PVOID ApcContext,
     147 + IN PIO_STATUS_BLOCK IoStatusBlock,
     148 + IN ULONG Reserved);
     149 + 
     150 +NTSTATUS NtQueryVirtualMemory(
     151 + IN HANDLE ProcessHandle,
     152 + IN PVOID BaseAddress,
     153 + IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
     154 + OUT PVOID MemoryInformation,
     155 + IN SIZE_T MemoryInformationLength,
     156 + OUT PSIZE_T ReturnLength OPTIONAL);
     157 + 
     158 +NTSTATUS NtFreeVirtualMemory(
     159 + IN HANDLE ProcessHandle,
     160 + IN OUT PVOID * BaseAddress,
     161 + IN OUT PSIZE_T RegionSize,
     162 + IN ULONG FreeType);
     163 + 
     164 +NTSTATUS NtUnmapViewOfSection(
     165 + HANDLE ProcessHandle,
     166 + PVOID BaseAddress);
     167 + 
     168 +#endif
     169 + 
  • ■ ■ ■ ■ ■ ■
    source/entry.c
     1 + 
     2 +#include "entry.h"
     3 +#include "beacon.h"
     4 +#include "syscalls.c"
     5 + 
     6 +// helper function to get the instruction pointer
     7 +__declspec(naked) PVOID get_ip(VOID)
     8 +{
     9 +#ifdef _WIN64
     10 + __asm__(
     11 + "mov rax, [rsp] \n"
     12 + "ret \n"
     13 + );
     14 +#else
     15 + __asm__(
     16 + "mov eax, [esp] \n"
     17 + "ret \n"
     18 + );
     19 +#endif
     20 +}
     21 + 
     22 +BOOL free_udrl(VOID)
     23 +{
     24 + BOOL is_pe = FALSE;
     25 + BOOL success = FALSE;
     26 + BOOL is_mapped = FALSE;
     27 + PVOID base_address, current_address;
     28 + SIZE_T region_size;
     29 + current_address = 0;
     30 + MEMORY_INFORMATION_CLASS mic = 0;
     31 + MEMORY_BASIC_INFORMATION mbi;
     32 + 
     33 + PVOID IP = get_ip();
     34 + 
     35 + while (TRUE)
     36 + {
     37 + NTSTATUS status = NtQueryVirtualMemory(
     38 + NtCurrentProcess(),
     39 + (PVOID)current_address,
     40 + mic,
     41 + &mbi,
     42 + sizeof(mbi),
     43 + NULL);
     44 + if (!NT_SUCCESS(status))
     45 + break;
     46 + 
     47 + base_address = mbi.BaseAddress;
     48 + region_size = mbi.RegionSize;
     49 + // next memory range
     50 + current_address = base_address + region_size;
     51 + 
     52 + // ignore non-commited pages
     53 + if (mbi.State != MEM_COMMIT)
     54 + continue;
     55 + // ignore pages with PAGE_NOACCESS
     56 + if ((mbi.Protect & PAGE_NOACCESS) == PAGE_NOACCESS)
     57 + continue;
     58 + // ignore pages with PAGE_GUARD as they can't be read
     59 + if ((mbi.Protect & PAGE_GUARD) == PAGE_GUARD)
     60 + continue;
     61 + // ignore pages that are not executable
     62 + if (((mbi.Protect & PAGE_EXECUTE_READ) != PAGE_EXECUTE_READ) &&
     63 + ((mbi.Protect & PAGE_EXECUTE_READWRITE) != PAGE_EXECUTE_READWRITE) &&
     64 + ((mbi.Protect & PAGE_EXECUTE_READWRITE) != PAGE_EXECUTE_READWRITE) &&
     65 + ((mbi.Protect & PAGE_EXECUTE_WRITECOPY) != PAGE_EXECUTE_WRITECOPY))
     66 + continue;
     67 + // ignore image pages (this might change in the future!)
     68 + if (mbi.Type == MEM_IMAGE)
     69 + continue;
     70 + // make sure it is not beacon
     71 + if ((ULONG_PTR)IP >= (ULONG_PTR)base_address &&
     72 + (ULONG_PTR)IP < RVA(ULONG_PTR, base_address, region_size))
     73 + continue;
     74 + 
     75 + 
     76 + DPRINT(
     77 + "range: 0x%p, DataSize: 0x%llx, State: 0x%lx, Protect: 0x%lx, Type: 0x%lx",
     78 + base_address,
     79 + region_size,
     80 + mbi.State,
     81 + mbi.Protect,
     82 + mbi.Type);
     83 + 
     84 + is_pe = is_valid_pe(base_address, region_size);
     85 + if (is_pe)
     86 + {
     87 + DPRINT("The region seems to be a valid PE");
     88 + is_mapped = mbi.Type == MEM_MAPPED;
     89 + success = free_region(base_address, region_size, is_mapped);
     90 + if (success)
     91 + {
     92 + DPRINT("Removed the UDRL at: 0x%p", base_address);
     93 + return TRUE;
     94 + }
     95 + 
     96 + PRINT_ERR(
     97 + "Could not free range of 0x%llx bytes at 0x%p",
     98 + region_size,
     99 + base_address);
     100 + }
     101 + }
     102 + return FALSE;
     103 +}
     104 + 
     105 +BOOL is_valid_pe(
     106 + PVOID base_address,
     107 + SIZE_T region_size)
     108 +{
     109 + PIMAGE_NT_HEADERS pNtHeaders;
     110 + 
     111 + // make sure the MZ magic bytes are valid
     112 + if (*(PUSHORT)base_address == MZ)
     113 + {
     114 + pNtHeaders = RVA(
     115 + PIMAGE_NT_HEADERS,
     116 + base_address,
     117 + ((PIMAGE_DOS_HEADER)base_address)->e_lfanew);
     118 + 
     119 + // make sure that pNtHeaders is in within the memory range
     120 + if ((ULONG_PTR)pNtHeaders >= (ULONG_PTR)base_address &&
     121 + (ULONG_PTR)pNtHeaders < RVA(ULONG_PTR, base_address, region_size) - 3)
     122 + {
     123 + // check the NT_HEADER signature
     124 + if (pNtHeaders->Signature == IMAGE_NT_SIGNATURE)
     125 + {
     126 + // found valid PE image, hopefully the reflective loader
     127 + return TRUE;
     128 + }
     129 + }
     130 + }
     131 + return FALSE;
     132 +}
     133 + 
     134 +BOOL free_region(
     135 + PVOID base_address,
     136 + SIZE_T region_size,
     137 + BOOL is_mapped)
     138 +{
     139 + NTSTATUS status;
     140 + if (is_mapped)
     141 + {
     142 + status = NtUnmapViewOfSection(
     143 + NtCurrentProcess(),
     144 + base_address);
     145 + DPRINT("NtUnmapViewOfSection status: 0x%lx", status);
     146 + }
     147 + else
     148 + {
     149 + // region_size must be 0 for MEM_RELEASE
     150 + region_size = 0;
     151 + status = NtFreeVirtualMemory(
     152 + NtCurrentProcess(),
     153 + &base_address,
     154 + &region_size,
     155 + MEM_RELEASE);
     156 + DPRINT("NtFreeVirtualMemory status: 0x%lx", status);
     157 + }
     158 + return NT_SUCCESS(status);
     159 +}
     160 + 
     161 +void go(char* args, int length)
     162 +{
     163 + if (free_udrl())
     164 + {
     165 + PRINT("Removed the User Defined Reflective Loader :)");
     166 + }
     167 + else
     168 + {
     169 + PRINT_ERR("Could NOT remove the User Defined Reflective Loader :(");
     170 + }
     171 +}
     172 + 
  • ■ ■ ■ ■ ■ ■
    source/syscalls.c
     1 +#include "syscalls.h"
     2 + 
     3 +// Code below is adapted from @modexpblog. Read linked article for more details.
     4 +// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
     5 + 
     6 +SW2_SYSCALL_LIST SW2_SyscallList __attribute__ ((section(".data")));
     7 +PVOID SyscallAddress __attribute__ ((section(".data"))) = NULL;
     8 + 
     9 +/*
     10 + * If no 'syscall' instruction is found in NTDLL,
     11 + * this function will be called.
     12 + * By default just returns STATUS_NOT_FOUND.
     13 + * The idea is to avoid having a 'syscall' instruction
     14 + * on this program's .text section to evade static analysis
     15 + */
     16 + 
     17 +__declspec(naked) void SyscallNotFound(void)
     18 +{
     19 + asm(
     20 + "mov eax, 0xC0DEDEAD \n"
     21 + "ret \n"
     22 + );
     23 +}
     24 + 
     25 +/*
     26 + * the idea here is to find a 'syscall' instruction in 'ntdll.dll'
     27 + * so that we can call it from our code and try to hide the fact
     28 + * that we use direct syscalls
     29 + */
     30 +PVOID GetSyscallAddress(
     31 + IN PVOID nt_api_address,
     32 + IN ULONG32 size_of_ntapi)
     33 +{
     34 + PVOID SyscallAddress;
     35 +#ifdef _WIN64
     36 + BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 };
     37 +#else
     38 + BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 };
     39 +#endif
     40 + 
     41 + // we will loook for a syscall;ret up to the end of the api
     42 + ULONG max_look_range = size_of_ntapi - sizeof(syscall_code) + 1;
     43 + 
     44 +#ifdef _M_IX86
     45 + if (local_is_wow64())
     46 + {
     47 + // if we are a WoW64 process, jump to WOW32Reserved
     48 + SyscallAddress = (PVOID)READ_MEMLOC(0xc0);
     49 + return SyscallAddress;
     50 + }
     51 +#endif
     52 + 
     53 + for (ULONG32 offset = 0; offset < max_look_range; offset++)
     54 + {
     55 + // we don't really care if there is a 'jmp' between
     56 + // nt_api_address and the 'syscall; ret' instructions
     57 + SyscallAddress = SW2_RVA2VA(PVOID, nt_api_address, offset);
     58 + 
     59 + if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
     60 + {
     61 + // we can use the original code for this system call :)
     62 + return SyscallAddress;
     63 + }
     64 + }
     65 + 
     66 + // the 'syscall; ret' intructions have not been found,
     67 + // we will try to use one near it, similarly to HalosGate
     68 + 
     69 + for (ULONG32 num_jumps = 1; num_jumps < SW2_MAX_ENTRIES; num_jumps++)
     70 + {
     71 + // let's try with an Nt* API below our syscall
     72 + for (ULONG32 offset = 0; offset < max_look_range; offset++)
     73 + {
     74 + SyscallAddress = SW2_RVA2VA(
     75 + PVOID,
     76 + nt_api_address,
     77 + offset + num_jumps * size_of_ntapi);
     78 + if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
     79 + return SyscallAddress;
     80 + }
     81 + 
     82 + // let's try with an Nt* API above our syscall
     83 + for (ULONG32 offset = 0; offset < max_look_range; offset++)
     84 + {
     85 + SyscallAddress = SW2_RVA2VA(
     86 + PVOID,
     87 + nt_api_address,
     88 + offset - num_jumps * size_of_ntapi);
     89 + if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
     90 + return SyscallAddress;
     91 + }
     92 + }
     93 + 
     94 + return SyscallNotFound;
     95 +}
     96 + 
     97 +DWORD SW2_HashSyscall(
     98 + IN PCSTR FunctionName)
     99 +{
     100 + DWORD i = 0;
     101 + DWORD Hash = SW2_SEED;
     102 + 
     103 + while (FunctionName[i])
     104 + {
     105 + WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++);
     106 + Hash ^= PartialName + SW2_ROR8(Hash);
     107 + }
     108 + 
     109 + return Hash;
     110 +}
     111 + 
     112 +BOOL SW2_PopulateSyscallList(VOID)
     113 +{
     114 + // Return early if the list is already populated.
     115 + if (SW2_SyscallList.Count) return TRUE;
     116 + 
     117 + PSW2_PEB Peb = (PSW2_PEB)READ_MEMLOC(PEB_OFFSET);
     118 + PSW2_PEB_LDR_DATA Ldr = Peb->Ldr;
     119 + PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
     120 + PVOID DllBase = NULL;
     121 + 
     122 + // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second
     123 + // in the list, so it's safer to loop through the full list and find it.
     124 + PSW2_LDR_DATA_TABLE_ENTRY LdrEntry;
     125 + for (LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])
     126 + {
     127 + DllBase = LdrEntry->DllBase;
     128 + PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;
     129 + PIMAGE_NT_HEADERS NtHeaders = SW2_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);
     130 + PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;
     131 + DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
     132 + if (VirtualAddress == 0) continue;
     133 + 
     134 + ExportDirectory = SW2_RVA2VA(PIMAGE_EXPORT_DIRECTORY, DllBase, VirtualAddress);
     135 + 
     136 + // If this is NTDLL.dll, exit loop.
     137 + PCHAR DllName = SW2_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);
     138 + if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue;
     139 + if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break;
     140 + }
     141 + 
     142 + if (!ExportDirectory) return FALSE;
     143 + 
     144 + DWORD NumberOfNames = ExportDirectory->NumberOfNames;
     145 + PDWORD Functions = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);
     146 + PDWORD Names = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);
     147 + PWORD Ordinals = SW2_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);
     148 + 
     149 + // Populate SW2_SyscallList with unsorted Zw* entries.
     150 + DWORD i = 0;
     151 + PSW2_SYSCALL_ENTRY Entries = SW2_SyscallList.Entries;
     152 + do
     153 + {
     154 + PCHAR FunctionName = SW2_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);
     155 + 
     156 + // Is this a system call?
     157 + if (*(USHORT*)FunctionName == 0x775a)
     158 + {
     159 + Entries[i].Hash = SW2_HashSyscall(FunctionName);
     160 + Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];
     161 + 
     162 + i++;
     163 + if (i == SW2_MAX_ENTRIES) break;
     164 + }
     165 + } while (--NumberOfNames);
     166 + 
     167 + // Save total number of system calls found.
     168 + SW2_SyscallList.Count = i;
     169 + 
     170 + // Sort the list by address in ascending order.
     171 + for (DWORD i = 0; i < SW2_SyscallList.Count - 1; i++)
     172 + {
     173 + for (DWORD j = 0; j < SW2_SyscallList.Count - i - 1; j++)
     174 + {
     175 + if (Entries[j].Address > Entries[j + 1].Address)
     176 + {
     177 + // Swap entries.
     178 + SW2_SYSCALL_ENTRY TempEntry;
     179 + 
     180 + TempEntry.Hash = Entries[j].Hash;
     181 + TempEntry.Address = Entries[j].Address;
     182 + 
     183 + Entries[j].Hash = Entries[j + 1].Hash;
     184 + Entries[j].Address = Entries[j + 1].Address;
     185 + 
     186 + Entries[j + 1].Hash = TempEntry.Hash;
     187 + Entries[j + 1].Address = TempEntry.Address;
     188 + }
     189 + }
     190 + }
     191 + 
     192 + // we need to know this in order to better search for syscall ids
     193 + ULONG size_of_ntapi = Entries[1].Address - Entries[0].Address;
     194 + 
     195 + // finally calculate the address of each syscall
     196 + for (DWORD i = 0; i < SW2_SyscallList.Count - 1; i++)
     197 + {
     198 + PVOID nt_api_address = SW2_RVA2VA(PVOID, DllBase, Entries[i].Address);
     199 + Entries[i].SyscallAddress = GetSyscallAddress(nt_api_address, size_of_ntapi);
     200 + }
     201 + 
     202 + return TRUE;
     203 +}
     204 + 
     205 +EXTERN_C DWORD SW2_GetSyscallNumber(
     206 + IN DWORD FunctionHash)
     207 +{
     208 + if (!SW2_PopulateSyscallList())
     209 + {
     210 + DPRINT_ERR("SW2_PopulateSyscallList failed");
     211 + return -1;
     212 + }
     213 + 
     214 + for (DWORD i = 0; i < SW2_SyscallList.Count; i++)
     215 + {
     216 + if (FunctionHash == SW2_SyscallList.Entries[i].Hash)
     217 + {
     218 + return i;
     219 + }
     220 + }
     221 + DPRINT_ERR("syscall with hash 0x%lx not found", FunctionHash);
     222 + return -1;
     223 +}
     224 + 
     225 +EXTERN_C PVOID SW3_GetSyscallAddress(
     226 + IN DWORD FunctionHash)
     227 +{
     228 + if (!SW2_PopulateSyscallList())
     229 + {
     230 + DPRINT_ERR("SW2_PopulateSyscallList failed");
     231 + return NULL;
     232 + }
     233 + 
     234 + for (DWORD i = 0; i < SW2_SyscallList.Count; i++)
     235 + {
     236 + if (FunctionHash == SW2_SyscallList.Entries[i].Hash)
     237 + {
     238 + return SW2_SyscallList.Entries[i].SyscallAddress;
     239 + }
     240 + }
     241 + DPRINT_ERR("syscall with hash 0x%lx not found", FunctionHash);
     242 + return NULL;
     243 +}
     244 + 
     245 +__declspec(naked) BOOL local_is_wow64(void)
     246 +{
     247 +#if defined(_WIN64)
     248 + asm(
     249 + "mov rax, 0 \n"
     250 + "ret \n"
     251 + );
     252 +#else
     253 + asm(
     254 + "mov eax, fs:[0xc0] \n"
     255 + "test eax, eax \n"
     256 + "jne wow64 \n"
     257 + "mov eax, 0 \n"
     258 + "ret \n"
     259 + "wow64: \n"
     260 + "mov eax, 1 \n"
     261 + "ret \n"
     262 + );
     263 +#endif
     264 +}
     265 + 
     266 +__declspec(naked) PVOID getIP(void)
     267 +{
     268 +#ifdef _WIN64
     269 + __asm__(
     270 + "mov rax, [rsp] \n"
     271 + "ret \n"
     272 + );
     273 +#else
     274 + __asm__(
     275 + "mov eax, [esp] \n"
     276 + "ret \n"
     277 + );
     278 +#endif
     279 +}
     280 + 
     281 +__declspec(naked) NTSTATUS NtQueryVirtualMemory(
     282 + IN HANDLE ProcessHandle,
     283 + IN PVOID BaseAddress,
     284 + IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
     285 + OUT PVOID MemoryInformation,
     286 + IN SIZE_T MemoryInformationLength,
     287 + OUT PSIZE_T ReturnLength OPTIONAL)
     288 +{
     289 +#if defined(_WIN64)
     290 + asm(
     291 + "mov [rsp +8], rcx \n"
     292 + "mov [rsp+16], rdx \n"
     293 + "mov [rsp+24], r8 \n"
     294 + "mov [rsp+32], r9 \n"
     295 + "mov rcx, 0x0393E980 \n"
     296 + "push rcx \n"
     297 + "sub rsp, 0x28 \n"
     298 + "call SW3_GetSyscallAddress \n"
     299 + "add rsp, 0x28 \n"
     300 + "pop rcx \n"
     301 + "push rax \n"
     302 + "sub rsp, 0x28 \n"
     303 + "call SW2_GetSyscallNumber \n"
     304 + "add rsp, 0x28 \n"
     305 + "pop r11 \n"
     306 + "mov rcx, [rsp+8] \n"
     307 + "mov rdx, [rsp+16] \n"
     308 + "mov r8, [rsp+24] \n"
     309 + "mov r9, [rsp+32] \n"
     310 + "mov r10, rcx \n"
     311 + "jmp r11 \n"
     312 + );
     313 +#else
     314 + asm(
     315 + "push 0x0393E980 \n"
     316 + "call SW3_GetSyscallAddress \n"
     317 + "pop ebx \n"
     318 + "push eax \n"
     319 + "push ebx \n"
     320 + "call SW2_GetSyscallNumber \n"
     321 + "add esp, 4 \n"
     322 + "pop ebx \n"
     323 + "mov edx, esp \n"
     324 + "sub edx, 4 \n"
     325 + "call ebx \n"
     326 + "ret \n"
     327 + );
     328 +#endif
     329 +}
     330 + 
     331 +__declspec(naked) NTSTATUS NtFreeVirtualMemory(
     332 + IN HANDLE ProcessHandle,
     333 + IN OUT PVOID * BaseAddress,
     334 + IN OUT PSIZE_T RegionSize,
     335 + IN ULONG FreeType)
     336 +{
     337 +#if defined(_WIN64)
     338 + asm(
     339 + "mov [rsp +8], rcx \n"
     340 + "mov [rsp+16], rdx \n"
     341 + "mov [rsp+24], r8 \n"
     342 + "mov [rsp+32], r9 \n"
     343 + "mov rcx, 0x01932F05 \n"
     344 + "push rcx \n"
     345 + "sub rsp, 0x28 \n"
     346 + "call SW3_GetSyscallAddress \n"
     347 + "add rsp, 0x28 \n"
     348 + "pop rcx \n"
     349 + "push rax \n"
     350 + "sub rsp, 0x28 \n"
     351 + "call SW2_GetSyscallNumber \n"
     352 + "add rsp, 0x28 \n"
     353 + "pop r11 \n"
     354 + "mov rcx, [rsp+8] \n"
     355 + "mov rdx, [rsp+16] \n"
     356 + "mov r8, [rsp+24] \n"
     357 + "mov r9, [rsp+32] \n"
     358 + "mov r10, rcx \n"
     359 + "jmp r11 \n"
     360 + );
     361 +#else
     362 + asm(
     363 + "push 0x01932F05 \n"
     364 + "call SW3_GetSyscallAddress \n"
     365 + "pop ebx \n"
     366 + "push eax \n"
     367 + "push ebx \n"
     368 + "call SW2_GetSyscallNumber \n"
     369 + "add esp, 4 \n"
     370 + "pop ebx \n"
     371 + "mov edx, esp \n"
     372 + "sub edx, 4 \n"
     373 + "call ebx \n"
     374 + "ret \n"
     375 + );
     376 +#endif
     377 +}
     378 + 
     379 +__declspec(naked) NTSTATUS NtUnmapViewOfSection(
     380 + HANDLE ProcessHandle,
     381 + PVOID BaseAddress)
     382 +{
     383 +#if defined(_WIN64)
     384 + asm(
     385 + "mov [rsp +8], rcx \n"
     386 + "mov [rsp+16], rdx \n"
     387 + "mov [rsp+24], r8 \n"
     388 + "mov [rsp+32], r9 \n"
     389 + "mov rcx, 0xCA1ACC8F \n"
     390 + "push rcx \n"
     391 + "sub rsp, 0x28 \n"
     392 + "call SW3_GetSyscallAddress \n"
     393 + "add rsp, 0x28 \n"
     394 + "pop rcx \n"
     395 + "push rax \n"
     396 + "sub rsp, 0x28 \n"
     397 + "call SW2_GetSyscallNumber \n"
     398 + "add rsp, 0x28 \n"
     399 + "pop r11 \n"
     400 + "mov rcx, [rsp+8] \n"
     401 + "mov rdx, [rsp+16] \n"
     402 + "mov r8, [rsp+24] \n"
     403 + "mov r9, [rsp+32] \n"
     404 + "mov r10, rcx \n"
     405 + "jmp r11 \n"
     406 + );
     407 +#else
     408 + asm(
     409 + "push 0xCA1ACC8F \n"
     410 + "call SW3_GetSyscallAddress \n"
     411 + "pop ebx \n"
     412 + "push eax \n"
     413 + "push ebx \n"
     414 + "call SW2_GetSyscallNumber \n"
     415 + "add esp, 4 \n"
     416 + "pop ebx \n"
     417 + "mov edx, esp \n"
     418 + "sub edx, 4 \n"
     419 + "call ebx \n"
     420 + "ret \n"
     421 + );
     422 +#endif
     423 +}
     424 + 
     425 + 
Please wait...
Page is in error, reload to recover