Rewritten module & function table.

This commit is contained in:
Tijme Gommers
2025-01-20 10:26:05 +01:00
parent e21a30ccb1
commit 161bdfb97e
2 changed files with 133 additions and 125 deletions
+63 -62
View File
@@ -18,6 +18,14 @@
*/
#include <stdbool.h>
/**
* 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 <stdint.h>
/**
* Windows API.
*
@@ -35,15 +43,22 @@
#include <winternl.h>
/**
* 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);
}
+70 -63
View File
@@ -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 <windows.h>
#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);
}
}