mirror of
https://github.com/tijme/relocatable
synced 2026-06-08 17:50:01 +00:00
Rewritten module & function table.
This commit is contained in:
+63
-62
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user