diff --git a/inc/relocatable.c b/inc/relocatable.c index 26d22b5..43bd30f 100644 --- a/inc/relocatable.c +++ b/inc/relocatable.c @@ -18,6 +18,14 @@ */ #include +/** + * Integers. + * + * Defines macros that specify limits of integer types corresponding to types defined in other standard headers. + * https://pubs.opengroup.org/onlinepubs/009696899/basedefs/stdint.h.html + */ +#include + /** * Windows API. * @@ -35,15 +43,22 @@ #include /** - * Definitions of the two primary functions we use to utilize the entire Windows API. + * Helper Macro Functions */ -typedef HMODULE (*PIC_LoadLibraryA)(LPCSTR lpLibFileName); -typedef FARPROC (*PIC_GetProcAddress)(HMODULE hModule, LPCSTR lpProcName); +#define DEFINE_STRING(name, value) char name[] = value "\0"; + +/** + * The main struct that holds your modules & functions to be used. + */ +struct Relocatable { + struct ModuleTable modules; + struct FunctionTable functions; +}; /** * The first instruction in the shellcode must jump to the main function. */ -void prefix() { +void RelocatablePrefix() { __asm__("call __main"); } @@ -52,7 +67,7 @@ void prefix() { * * @return PEB* The current PEB. */ -void* PIC_NtGetPeb() { +void* RelocatableNtGetPeb() { #ifdef _M_X64 return (void*) __readgsqword(0x60); #elif _M_IX86 @@ -68,9 +83,23 @@ void* PIC_NtGetPeb() { * @param ptr The LIST_ENTRY pointer to retrieve the data table entry from. * @return LDR_DATA_TABLE_ENTRY* The corresponding LDR_DATA_TABLE_ENTRY pointer. */ -LDR_DATA_TABLE_ENTRY *PIC_GetDataTableEntry(const LIST_ENTRY *ptr) { - int listEntryOffset = offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); - return (LDR_DATA_TABLE_ENTRY *)((BYTE *)ptr - listEntryOffset); +LDR_DATA_TABLE_ENTRY *RelocatableGetDataTableEntry(const LIST_ENTRY *ptr) { + return (LDR_DATA_TABLE_ENTRY *)((uint8_t *)ptr - offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)); +} + +/** + * Compare two null-terminated strings. + * + * @param a First string. + * @param b Second string. + * @return true if the strings are equal, false otherwise. + */ +bool RelocatableStrCmp(const char *a, const char *b) { + while (*a && (*a == *b)) { + a++, b++; + } + + return *a == *b; } /** @@ -84,59 +113,33 @@ LDR_DATA_TABLE_ENTRY *PIC_GetDataTableEntry(const LIST_ENTRY *ptr) { * @param functionName The name of the function to search for. * @return void* The address of the function if found, NULL otherwise. */ -void* PIC_PreliminaryGetProcAddress(char moduleName[], char functionName[]) { - PEB *peb = PIC_NtGetPeb(); +void* RelocatablePreliminaryGetProcAddress(const char *moduleName, const char *functionName) { + PEB *peb = RelocatableNtGetPeb(); LIST_ENTRY *first = peb->Ldr->InMemoryOrderModuleList.Flink; LIST_ENTRY *ptr = first; do { - LDR_DATA_TABLE_ENTRY *dte = PIC_GetDataTableEntry(ptr); + LDR_DATA_TABLE_ENTRY *dte = RelocatableGetDataTableEntry(ptr); ptr = ptr->Flink; - BYTE *baseAddress = (BYTE *) dte->DllBase; - if (!baseAddress) continue; + uint8_t *base = (uint8_t *)dte->DllBase; + if (!base) continue; - IMAGE_DOS_HEADER *dosHeader = (IMAGE_DOS_HEADER *)baseAddress; - IMAGE_NT_HEADERS *ntHeaders = (IMAGE_NT_HEADERS *)(baseAddress + dosHeader->e_lfanew); - DWORD iedRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; - if (!iedRVA) continue; + IMAGE_DOS_HEADER *dosHdr = (IMAGE_DOS_HEADER *)base; + IMAGE_NT_HEADERS *ntHdrs = (IMAGE_NT_HEADERS *)(base + dosHdr->e_lfanew); + DWORD expDirRVA = ntHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + if (!expDirRVA) continue; - IMAGE_EXPORT_DIRECTORY *ied = (IMAGE_EXPORT_DIRECTORY *)(baseAddress + iedRVA); + IMAGE_EXPORT_DIRECTORY *expDir = (IMAGE_EXPORT_DIRECTORY *)(base + expDirRVA); + if (!RelocatableStrCmp(moduleName, (char *)(base + expDir->Name))) continue; - // Compare module name byte by byte - char *currentModuleName = (char *)(baseAddress + ied->Name); + DWORD *nameRVAs = (DWORD *)(base + expDir->AddressOfNames); + WORD *ordinals = (WORD *)(base + expDir->AddressOfNameOrdinals); + DWORD *funcRVAs = (DWORD *)(base + expDir->AddressOfFunctions); - // Trim any extra padding spaces or null characters - int i = 0; - while (moduleName[i] != '\0' && currentModuleName[i] != '\0') { - if (moduleName[i] != currentModuleName[i]) { - break; - } - i++; - } - - // Ensure both strings are the same length and match exactly - if (moduleName[i] != '\0' || currentModuleName[i] != '\0') { - continue; // Skip if lengths don't match or strings are different - } - - DWORD *nameRVAs = (DWORD *)(baseAddress + ied->AddressOfNames); - for (DWORD i = 0; i < ied->NumberOfNames; ++i) { - char *currentFunctionName = (char *)(baseAddress + nameRVAs[i]); - - // Compare function name byte by byte - bool functionMatch = true; - for (int j = 0; functionName[j] != '\0' || currentFunctionName[j] != '\0'; j++) { - if (functionName[j] != currentFunctionName[j]) { - functionMatch = false; - break; - } - } - - if (functionMatch) { - WORD ordinal = ((WORD *)(baseAddress + ied->AddressOfNameOrdinals))[i]; - DWORD functionRVA = ((DWORD *)(baseAddress + ied->AddressOfFunctions))[ordinal]; - return (void *)(baseAddress + functionRVA); + for (DWORD i = 0; i < expDir->NumberOfNames; i++) { + if (RelocatableStrCmp(functionName, (char *)(base + nameRVAs[i]))) { + return (void *)(base + funcRVAs[ordinals[i]]); } } } while (ptr != first); @@ -144,19 +147,17 @@ void* PIC_PreliminaryGetProcAddress(char moduleName[], char functionName[]) { return NULL; } - /** * Initialize Relocatable by resolving the two main Windows APIs it depends on. * - * @param PIC_LoadLibraryA* LoadLibraryA Addres of `LoadLibraryA` is written to this variable. - * @param PIC_GetProcAddress* GetProcAddress Addres of `GetProcAddress` is written to this variable. + * @param struct Relocatable* context A 'global' variable capturing Relocatable's entire context (loaded modules & functions) */ -void InitRelocatable(PIC_LoadLibraryA* LoadLibraryA, PIC_GetProcAddress* GetProcAddress) { - // Resolve LoadLibraryA and GetProcAddress (assuming `Kernel32.dll` is loaded) - char StringKernel32Dll[] = {'K', 'E', 'R', 'N', 'E', 'L', '3', '2', '.', 'd', 'l', 'l', 0x0 }; - char StringLoadLibraryA[] = {'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', 0x0 }; - char StringGetProcAddress[] = {'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 }; - - *LoadLibraryA = (PIC_LoadLibraryA) PIC_PreliminaryGetProcAddress(StringKernel32Dll, StringLoadLibraryA); - *GetProcAddress = (PIC_GetProcAddress) PIC_PreliminaryGetProcAddress(StringKernel32Dll, StringGetProcAddress); +void InitializeRelocatable(struct Relocatable* context) { + // Resolve LoadLibraryA and GetProcAddress (assuming `KERNEL32.dll` is loaded) + DEFINE_STRING(Kernel32ModuleName, "KERNEL32.dll"); + DEFINE_STRING(LoadLibraryAFunctionName, "LoadLibraryA"); + DEFINE_STRING(GetProcAddressFunctionName, "GetProcAddress"); + + context->functions.LoadLibraryA = (HMODULE (*)(LPCSTR lpLibFileName)) RelocatablePreliminaryGetProcAddress(Kernel32ModuleName, LoadLibraryAFunctionName); + context->functions.GetProcAddress = (FARPROC (*)(HMODULE hModule, LPCSTR lpProcName)) RelocatablePreliminaryGetProcAddress(Kernel32ModuleName, GetProcAddressFunctionName); } \ No newline at end of file diff --git a/src/main.c b/src/main.c index db6c0b7..5285683 100644 --- a/src/main.c +++ b/src/main.c @@ -10,83 +10,90 @@ * include this same license and copyright notice. */ -#include "../inc/relocatable.c" +/** + * Windows API. + * + * Contains declarations for all of the functions, macro's & data types in the Windows API. + * https://docs.microsoft.com/en-us/previous-versions//aa383749(v=vs.85)?redirectedfrom=MSDN + */ +#include -#define DEFINE_STRING(name, value) char name[] = value "\0"; - -// Structure to hold function pointers and modules -typedef struct { - HMODULE hUser32; +/** + * A struct of module definitions you would like to use. + */ +struct ModuleTable { + // Custom (add any module of your preference here) HMODULE hKernel32; + HMODULE hUser32; +}; + +/** + * A struct of function definitions you would like to use. + */ +struct FunctionTable { + // Must always be present (these are initialized by Relocatable) + HMODULE (*LoadLibraryA)(LPCSTR lpLibFileName); + FARPROC (*GetProcAddress)(HMODULE hModule, LPCSTR lpProcName); + + // Custom (add any function of your preference here) void (*MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT); void (*WinExec)(LPCSTR, UINT); -} FunctionTable; +}; -// Initialize the FunctionTable -int InitFunctionTable(FunctionTable* table) { - if (!table) return -1; +/** + * Include Relocatable helper functions. + * + * This include must be used before any of your own code, as this file + * contains the first instructions of the shellcode that will ensure that + * the `__main` function is called correctly upon running the shellcode. + */ +#include "../inc/relocatable.c" - PIC_LoadLibraryA LoadLibraryA; - PIC_GetProcAddress GetProcAddress; - InitRelocatable(&LoadLibraryA, &GetProcAddress); +/** + * Populate the context tables with modules & functions you would like to use. + * + * @param struct Relocatable* context A 'global' variable capturing Relocatable's entire context (loaded modules & functions) + */ +void PopulateTables(struct Relocatable* context) { + // Define modules + DEFINE_STRING(Kernel32ModuleName, "KERNEL32.dll"); + DEFINE_STRING(User32ModuleName, "USER32.dll"); - // Define required strings - DEFINE_STRING(StringUser32Dll, "User32.dll"); - DEFINE_STRING(StringKernel32Dll, "Kernel32.dll"); - DEFINE_STRING(StringMessageBoxA, "MessageBoxA"); - DEFINE_STRING(StringWinExec, "WinExec"); + // Load modules + context->modules.hKernel32 = context->functions.LoadLibraryA(Kernel32ModuleName); + context->modules.hUser32 = context->functions.LoadLibraryA(User32ModuleName); - // Load User32.dll - table->hUser32 = LoadLibraryA(StringUser32Dll); - if (!table->hUser32) return -1; + // Define functions + DEFINE_STRING(WinExecFunctionName, "WinExec"); + DEFINE_STRING(MessageBoxAFunctionName, "MessageBoxA"); - // Load Kernel32.dll - table->hKernel32 = LoadLibraryA(StringKernel32Dll); - if (!table->hKernel32) return -1; - - // Load MessageBoxA - table->MessageBoxA = (void (*)(HWND, LPCSTR, LPCSTR, UINT)) - GetProcAddress(table->hUser32, StringMessageBoxA); - if (!table->MessageBoxA) return -1; - - // Load WinExec - table->WinExec = (void (*)(LPCSTR, UINT)) - GetProcAddress(table->hKernel32, StringWinExec); - if (!table->WinExec) return -1; - - return 0; + // Load functions + context->functions.WinExec = (void (*)(LPCSTR, UINT)) context->functions.GetProcAddress(context->modules.hKernel32, WinExecFunctionName); + context->functions.MessageBoxA = (void (*)(HWND, LPCSTR, LPCSTR, UINT)) context->functions.GetProcAddress(context->modules.hUser32, MessageBoxAFunctionName); } - -void __main (); -void other_function(FunctionTable* table); - +/** + * The main function of your shellcode. + * + * Using `InitializeRelocatable`, two Windows API functions are at your disposal. + * Using these two functions, you can further utilize the Windows API. + * - HMODULE context.functions.LoadLibraryA([in] LPCSTR lpLibFileName); + * - FARPROC context.functions.GetProcAddress([in] HMODULE hModule, [in] LPCSTR lpProcName); + */ void __main () { - // Define strings - DEFINE_STRING(StringMessageBoxTitle, "Test Title"); - DEFINE_STRING(StringMessageBoxBody, "Test Body"); + struct Relocatable context; + InitializeRelocatable(&context); - // Initialize function table - FunctionTable table; - if (InitFunctionTable(&table) != 0) { - // Initialization failed - return; - } + // Populate module & function tables with your own dependencies + PopulateTables(&context); - // Call MessageBoxA - if (table.MessageBoxA) { - table.MessageBoxA(NULL, StringMessageBoxBody, StringMessageBoxTitle, MB_OK); - } + // Example to pop a message box + DEFINE_STRING(MessageBoxTitle, "Test Title"); + DEFINE_STRING(MessageBoxBody, "Test Body"); + context.functions.MessageBoxA(NULL, MessageBoxBody, MessageBoxTitle, MB_OK); - // Call other_function - other_function(&table); + // Example to pop a calculator + DEFINE_STRING(CalculatorBinary, "calc.exe"); + context.functions.WinExec(CalculatorBinary, SW_SHOW); } -void other_function(FunctionTable* table) { - DEFINE_STRING(StringCalc, "calc.exe"); - - // Call WinExec - if (table->WinExec) { - table->WinExec(StringCalc, SW_SHOW); - } -}