mirror of
https://github.com/tijme/dittobytes
synced 2026-06-06 16:54:30 +00:00
283 lines
9.0 KiB
C
283 lines
9.0 KiB
C
/**
|
|
* GNU General Public License, version 2.0.
|
|
*
|
|
* Copyright (c) 2025 Tijme Gommers (@tijme).
|
|
*
|
|
* This source code file is part of Dittobytes. Dittobytes is
|
|
* licensed under GNU General Public License, version 2.0, and
|
|
* you are free to use, modify, and distribute this file under
|
|
* its terms. However, any modified versions of this file must
|
|
* include this same license and copyright notice.
|
|
*/
|
|
|
|
/**
|
|
* This `calc.exe` pop example only works on Windows.
|
|
*/
|
|
#if !defined(__WINDOWS__)
|
|
#error "This `calc.exe` pop example only works on Windows AMD64 & ARM64. Build with `make beacon-all-win-all`."
|
|
#endif
|
|
|
|
/**
|
|
* Booleans.
|
|
*
|
|
* Defines boolean types.
|
|
* https://pubs.opengroup.org/onlinepubs/007904975/basedefs/stdbool.h.html
|
|
*/
|
|
#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.
|
|
*
|
|
* 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>
|
|
|
|
/**
|
|
* Internal NT API's and data structures.
|
|
*
|
|
* Helper library that contains NT API's and data structures for system services, security and identity.
|
|
* https://docs.microsoft.com/en-us/windows/win32/api/winternl/
|
|
*/
|
|
#include <winternl.h>
|
|
|
|
/**
|
|
* 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>
|
|
|
|
/**
|
|
* A struct of module definitions we are loading.
|
|
*/
|
|
struct ModuleTable {
|
|
HMODULE hKernel32;
|
|
};
|
|
|
|
/**
|
|
* A struct of function definitions we are loading.
|
|
*/
|
|
struct FunctionTable {
|
|
// Must always be present
|
|
HMODULE (*LoadLibraryA)(LPCSTR lpLibFileName);
|
|
FARPROC (*GetProcAddress)(HMODULE hModule, LPCSTR lpProcName);
|
|
|
|
// Any other functions we use in this verification
|
|
UINT (*WinExec)(LPCSTR, UINT);
|
|
};
|
|
|
|
/**
|
|
* The main struct that holds our modules & functions to be used.
|
|
*/
|
|
struct Relocatable {
|
|
struct ModuleTable modules;
|
|
struct FunctionTable functions;
|
|
};
|
|
|
|
/**
|
|
* Helper Macro Functions
|
|
*/
|
|
#define DEFINE_STRING(name, value) char name[] = value;
|
|
|
|
/**
|
|
* Get current Process Environment Block.
|
|
*
|
|
* @return PEB* The current PEB.
|
|
*/
|
|
void* RelocatableNtGetPeb();
|
|
|
|
/**
|
|
* Retrieve the LDR_DATA_TABLE_ENTRY from a given LIST_ENTRY pointer.
|
|
*
|
|
* @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 *RelocatableGetDataTableEntry(const LIST_ENTRY *ptr);
|
|
|
|
/**
|
|
* 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);
|
|
|
|
/**
|
|
* Retrieve the address of a function from a module in memory by matching
|
|
* both the module name and the function name.
|
|
*
|
|
* This function and its dependencies are inspired on ShellcodeStdio
|
|
* from @jackullrich: https://github.com/jackullrich/ShellcodeStdio/tree/master
|
|
*
|
|
* @param moduleName The name of the module to search for.
|
|
* @param functionName The name of the function to search for.
|
|
* @return void* The address of the function if found, NULL otherwise.
|
|
*/
|
|
void* RelocatablePreliminaryGetProcAddress(const char *moduleName, const char *functionName);
|
|
|
|
/**
|
|
* Initialize Relocatable by resolving the two main Windows APIs it depends on.
|
|
*
|
|
* @param struct Relocatable* context A 'global' variable capturing Relocatable's entire context (loaded modules & functions)
|
|
*/
|
|
void InitializeRelocatable(struct Relocatable* context);
|
|
|
|
/**
|
|
* 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);
|
|
|
|
/**
|
|
* The main function to pop `calc.exe`.
|
|
*/
|
|
uint32_t EntryFunction() {
|
|
struct Relocatable context;
|
|
|
|
// Populate module & function tables
|
|
InitializeRelocatable(&context);
|
|
PopulateTables(&context);
|
|
|
|
// Run WinExec and return its return value
|
|
DEFINE_STRING(CalculatorBinary, "calc.exe");
|
|
return (uint32_t) context.functions.WinExec(CalculatorBinary, SW_SHOW) > 31;
|
|
}
|
|
|
|
/**
|
|
* Get current Process Environment Block.
|
|
*
|
|
* @return PEB* The current PEB.
|
|
*/
|
|
void* RelocatableNtGetPeb() {
|
|
void* lpTEB;
|
|
void* lpPEB;
|
|
|
|
#if defined(__AMD64__)
|
|
lpPEB = (void*) __readgsqword(0x60);
|
|
#elif defined(__ARM64__)
|
|
__asm__("mov %0, x18" : "=r" (lpTEB));
|
|
lpPEB = * (void**) ((char*) lpTEB + 0x60);
|
|
#else
|
|
#error "This architecture is currently unsupported"
|
|
#endif
|
|
|
|
return lpPEB;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the LDR_DATA_TABLE_ENTRY from a given LIST_ENTRY pointer.
|
|
*
|
|
* @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 *RelocatableGetDataTableEntry(const LIST_ENTRY *ptr) {
|
|
LDR_DATA_TABLE_ENTRY* entry = (LDR_DATA_TABLE_ENTRY *) 0;
|
|
size_t offset = (size_t) &entry->InMemoryOrderLinks;
|
|
return (LDR_DATA_TABLE_ENTRY *)((uint8_t *) ptr - offset);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the address of a function from a module in memory by matching
|
|
* both the module name and the function name.
|
|
*
|
|
* This function and its dependencies are inspired on ShellcodeStdio
|
|
* from @jackullrich: https://github.com/jackullrich/ShellcodeStdio/tree/master
|
|
*
|
|
* @param moduleName The name of the module to search for.
|
|
* @param functionName The name of the function to search for.
|
|
* @return void* The address of the function if found, NULL otherwise.
|
|
*/
|
|
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 = RelocatableGetDataTableEntry(ptr);
|
|
ptr = ptr->Flink;
|
|
|
|
uint8_t *base = (uint8_t *)dte->DllBase;
|
|
if (!base) 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 *expDir = (IMAGE_EXPORT_DIRECTORY *)(base + expDirRVA);
|
|
if (!RelocatableStrCmp(moduleName, (char *)(base + expDir->Name))) continue;
|
|
|
|
DWORD *nameRVAs = (DWORD *)(base + expDir->AddressOfNames);
|
|
WORD *ordinals = (WORD *)(base + expDir->AddressOfNameOrdinals);
|
|
DWORD *funcRVAs = (DWORD *)(base + expDir->AddressOfFunctions);
|
|
|
|
for (DWORD i = 0; i < expDir->NumberOfNames; i++) {
|
|
if (RelocatableStrCmp(functionName, (char *)(base + nameRVAs[i]))) {
|
|
return (void *)(base + funcRVAs[ordinals[i]]);
|
|
}
|
|
}
|
|
} while (ptr != first);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Initialize Relocatable by resolving the two main Windows APIs it depends on.
|
|
*
|
|
* @param struct Relocatable* context A 'global' variable capturing Relocatable's entire context (loaded modules & functions)
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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");
|
|
|
|
// Load modules
|
|
context->modules.hKernel32 = context->functions.LoadLibraryA(Kernel32ModuleName);
|
|
|
|
// Define functions
|
|
DEFINE_STRING(WinExecFunctionName, "WinExec");
|
|
|
|
// Load functions
|
|
context->functions.WinExec = (UINT (*)(LPCSTR, UINT)) context->functions.GetProcAddress(context->modules.hKernel32, WinExecFunctionName);
|
|
} |