i think it works
This commit is contained in:
commit
0e1479b168
167 files changed
+27008
No files matched your search
@@ -0,0 +1,301 @@
|
||||
// ldr_image.c - PE32+ loader and export resolver
|
||||
//
|
||||
// At boot all images are already mapped by the bootloader; LdrLoadImage is
|
||||
// used for any image loaded later (currently unused - all modules come in via
|
||||
// BOOT_INFO). LdrGetExport scans the PE export directory of a registered module.
|
||||
#include "ldr_image.h"
|
||||
#include "boot_info.h"
|
||||
#include "mm_virt.h"
|
||||
#include "ke_debug.h"
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PE32+ structures (just what we need)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define PE_SIGNATURE 0x00004550UL // 'PE\0\0'
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint16_t Magic;
|
||||
uint8_t MajorLinkerVersion;
|
||||
uint8_t MinorLinkerVersion;
|
||||
uint32_t SizeOfCode;
|
||||
uint32_t SizeOfInitializedData;
|
||||
uint32_t SizeOfUninitializedData;
|
||||
uint32_t AddressOfEntryPoint;
|
||||
uint32_t BaseOfCode;
|
||||
uint64_t ImageBase;
|
||||
uint32_t SectionAlignment;
|
||||
uint32_t FileAlignment;
|
||||
uint16_t MajorOSVersion;
|
||||
uint16_t MinorOSVersion;
|
||||
uint16_t MajorImageVersion;
|
||||
uint16_t MinorImageVersion;
|
||||
uint16_t MajorSubsystemVersion;
|
||||
uint16_t MinorSubsystemVersion;
|
||||
uint32_t Win32VersionValue;
|
||||
uint32_t SizeOfImage;
|
||||
uint32_t SizeOfHeaders;
|
||||
uint32_t CheckSum;
|
||||
uint16_t Subsystem;
|
||||
uint16_t DllCharacteristics;
|
||||
uint64_t SizeOfStackReserve;
|
||||
uint64_t SizeOfStackCommit;
|
||||
uint64_t SizeOfHeapReserve;
|
||||
uint64_t SizeOfHeapCommit;
|
||||
uint32_t LoaderFlags;
|
||||
uint32_t NumberOfRvaAndSizes;
|
||||
struct { uint32_t VirtualAddress; uint32_t Size; } DataDirectory[16];
|
||||
} OPT_HEADER64;
|
||||
|
||||
typedef struct {
|
||||
uint32_t Signature;
|
||||
uint16_t Machine;
|
||||
uint16_t NumberOfSections;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t PointerToSymbolTable;
|
||||
uint32_t NumberOfSymbols;
|
||||
uint16_t SizeOfOptionalHeader;
|
||||
uint16_t Characteristics;
|
||||
OPT_HEADER64 OptionalHeader;
|
||||
} PE_HEADER;
|
||||
|
||||
typedef struct {
|
||||
char Name[8];
|
||||
uint32_t VirtualSize;
|
||||
uint32_t VirtualAddress;
|
||||
uint32_t SizeOfRawData;
|
||||
uint32_t PointerToRawData;
|
||||
uint32_t PointerToRelocations;
|
||||
uint32_t PointerToLinenumbers;
|
||||
uint16_t NumberOfRelocations;
|
||||
uint16_t NumberOfLinenumbers;
|
||||
uint32_t Characteristics;
|
||||
} SECTION_HEADER;
|
||||
|
||||
typedef struct {
|
||||
uint32_t Characteristics;
|
||||
uint32_t TimeDateStamp;
|
||||
uint16_t MajorVersion;
|
||||
uint16_t MinorVersion;
|
||||
uint32_t Name;
|
||||
uint32_t Base;
|
||||
uint32_t NumberOfFunctions;
|
||||
uint32_t NumberOfNames;
|
||||
uint32_t AddressOfFunctions;
|
||||
uint32_t AddressOfNames;
|
||||
uint32_t AddressOfNameOrdinals;
|
||||
} EXPORT_DIR;
|
||||
|
||||
typedef struct {
|
||||
uint32_t OriginalFirstThunk;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t ForwarderChain;
|
||||
uint32_t Name;
|
||||
uint32_t FirstThunk;
|
||||
} IMPORT_DESC;
|
||||
|
||||
typedef struct {
|
||||
uint32_t VirtualAddress;
|
||||
uint32_t SizeOfBlock;
|
||||
} BASE_RELOC;
|
||||
#pragma pack(pop)
|
||||
|
||||
#define IMAGE_REL_BASED_DIR64 10
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Module registry
|
||||
// ---------------------------------------------------------------------------
|
||||
#define MAX_MODULES 32
|
||||
|
||||
typedef struct {
|
||||
char Name[32];
|
||||
uintptr_t Base;
|
||||
} MODULE_ENTRY;
|
||||
|
||||
static MODULE_ENTRY gModules[MAX_MODULES];
|
||||
static int gModuleCount = 0;
|
||||
|
||||
void LdrRegisterModulesFromBootInfo(const BOOT_INFO* bootInfo) {
|
||||
for (uint32_t i = 0; i < bootInfo->ModuleCount && i < MAX_MODULES; i++) {
|
||||
strncpy(gModules[gModuleCount].Name, bootInfo->Modules[i].Name, 31);
|
||||
gModules[gModuleCount].Base = bootInfo->Modules[i].VirtBase;
|
||||
gModuleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void LdrRegisterModule(const char* name, uintptr_t base) {
|
||||
if (gModuleCount < MAX_MODULES) {
|
||||
strncpy(gModules[gModuleCount].Name, name, 31);
|
||||
gModules[gModuleCount].Base = base;
|
||||
gModuleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
static uintptr_t FindModuleBase(const char* name) {
|
||||
for (int i = 0; i < gModuleCount; i++) {
|
||||
if (_stricmp(gModules[i].Name, name) == 0) return gModules[i].Base;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Export lookup
|
||||
// ---------------------------------------------------------------------------
|
||||
void* LdrGetExport(const char* moduleName, const char* functionName) {
|
||||
uintptr_t base = FindModuleBase(moduleName);
|
||||
if (!base) return (void *)0;
|
||||
|
||||
uint32_t peOffset = *(uint32_t *)(base + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)(base + peOffset);
|
||||
if (pe->Signature != PE_SIGNATURE) return (void *)0;
|
||||
|
||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||
if (opt->NumberOfRvaAndSizes < 1) return (void *)0;
|
||||
|
||||
uint32_t expRva = opt->DataDirectory[0].VirtualAddress;
|
||||
if (!expRva) return (void *)0;
|
||||
|
||||
EXPORT_DIR* exp = (EXPORT_DIR *)(base + expRva);
|
||||
uint32_t* names = (uint32_t *)(base + exp->AddressOfNames);
|
||||
uint16_t* ordinals = (uint16_t *)(base + exp->AddressOfNameOrdinals);
|
||||
uint32_t* functions = (uint32_t *)(base + exp->AddressOfFunctions);
|
||||
|
||||
for (uint32_t i = 0; i < exp->NumberOfNames; i++) {
|
||||
const char* name = (const char *)(base + names[i]);
|
||||
if (strcmp(name, functionName) == 0) {
|
||||
uint16_t ord = ordinals[i];
|
||||
return (void *)(base + functions[ord]);
|
||||
}
|
||||
}
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Entry point lookup
|
||||
// ---------------------------------------------------------------------------
|
||||
uintptr_t LdrGetEntryPoint(uintptr_t base) {
|
||||
uint32_t pe_offset = *(uint32_t*)(base + 0x3C);
|
||||
uint32_t entry_rva = *(uint32_t*)(base + pe_offset + 0x28);
|
||||
return base + entry_rva;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Base Relocation
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS LdrApplyRelocations(uintptr_t base, uintptr_t delta) {
|
||||
uint32_t peOffset = *(uint32_t *)(base + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)(base + peOffset);
|
||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||
|
||||
if (opt->NumberOfRvaAndSizes < 6) return STATUS_SUCCESS;
|
||||
uint32_t relocRva = opt->DataDirectory[5].VirtualAddress;
|
||||
uint32_t relocSize = opt->DataDirectory[5].Size;
|
||||
if (!relocRva || !relocSize) return STATUS_SUCCESS;
|
||||
|
||||
BASE_RELOC* block = (BASE_RELOC *)(base + relocRva);
|
||||
uintptr_t end = (uintptr_t)block + relocSize;
|
||||
|
||||
while ((uintptr_t)block < end && block->SizeOfBlock > 0) {
|
||||
uint32_t count = (block->SizeOfBlock - sizeof(BASE_RELOC)) / sizeof(uint16_t);
|
||||
uint16_t* entries = (uint16_t *)(block + 1);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
int type = entries[i] >> 12;
|
||||
int offset = entries[i] & 0xFFF;
|
||||
|
||||
// For 64-bit PEs, we mostly care about DIR64 (Type 10)
|
||||
if (type == IMAGE_REL_BASED_DIR64) {
|
||||
uint64_t* ptr = (uint64_t *)(base + block->VirtualAddress + offset);
|
||||
*ptr += delta;
|
||||
}
|
||||
}
|
||||
block = (BASE_RELOC *)((uint8_t *)block + block->SizeOfBlock);
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Import resolution
|
||||
// ---------------------------------------------------------------------------
|
||||
KSTATUS LdrResolveImports(uintptr_t imageBase) {
|
||||
uint32_t peOffset = *(uint32_t *)(imageBase + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)(imageBase + peOffset);
|
||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||
|
||||
if (opt->NumberOfRvaAndSizes < 2) return STATUS_SUCCESS; // no imports
|
||||
uint32_t impRva = opt->DataDirectory[1].VirtualAddress;
|
||||
if (!impRva) return STATUS_SUCCESS;
|
||||
|
||||
IMPORT_DESC* imp = (IMPORT_DESC *)(imageBase + impRva);
|
||||
while (imp->Name) {
|
||||
const char* dllName = (const char *)(imageBase + imp->Name);
|
||||
uint64_t* thunk = (uint64_t *)(imageBase + imp->FirstThunk);
|
||||
uint64_t* origThunk = (uint64_t *)(imageBase + (imp->OriginalFirstThunk
|
||||
? imp->OriginalFirstThunk
|
||||
: imp->FirstThunk));
|
||||
while (*origThunk) {
|
||||
if (*origThunk & (1ULL << 63)) {
|
||||
// Import by ordinal - not supported.
|
||||
DbgPrint("LdrResolveImports: Ordinal imports not supported! (%s)\n", dllName);
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
const char* funcName = (const char *)(imageBase + (*origThunk & 0xFFFFFFFF) + 2);
|
||||
void* addr = LdrGetExport(dllName, funcName);
|
||||
if (!addr) {
|
||||
DbgPrint("LdrResolveImports: Function not found! (%s)\n", funcName);
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
*thunk = (uint64_t)addr;
|
||||
thunk++;
|
||||
origThunk++;
|
||||
}
|
||||
imp++;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// LdrLoadImage - load a PE from a raw buffer into virtual space.
|
||||
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user) {
|
||||
if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t peOffset = *(uint32_t *)((uint8_t *)buffer + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)((uint8_t *)buffer + peOffset);
|
||||
if (pe->Signature != PE_SIGNATURE) return STATUS_FAILURE;
|
||||
|
||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||
size_t imageSize = opt->SizeOfImage;
|
||||
|
||||
void* mem = user ? MmAllocateUserMemory(imageSize) : MmAllocatePool(imageSize);
|
||||
if (!mem) return STATUS_NO_MEMORY;
|
||||
memset(mem, 0, imageSize);
|
||||
|
||||
// Copy headers and sections
|
||||
memcpy(mem, buffer, opt->SizeOfHeaders);
|
||||
SECTION_HEADER* sec = (SECTION_HEADER *)((uint8_t *)&pe->OptionalHeader + pe->SizeOfOptionalHeader);
|
||||
for (int i = 0; i < pe->NumberOfSections; i++) {
|
||||
void* dst = (uint8_t *)mem + sec[i].VirtualAddress;
|
||||
const void* src = (const uint8_t *)buffer + sec[i].PointerToRawData;
|
||||
if (sec[i].SizeOfRawData) {
|
||||
memcpy(dst, src, sec[i].SizeOfRawData < sec[i].VirtualSize ? sec[i].SizeOfRawData : sec[i].VirtualSize);
|
||||
}
|
||||
|
||||
uint64_t prot = PAGE_PRESENT | PAGE_WRITE | (user ? PAGE_USER : 0);
|
||||
MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress, sec[i].VirtualSize, prot);
|
||||
}
|
||||
|
||||
// Apply Relocations
|
||||
uintptr_t delta = (uintptr_t)mem - opt->ImageBase;
|
||||
if (delta != 0) {
|
||||
LdrApplyRelocations((uintptr_t)mem, delta);
|
||||
}
|
||||
|
||||
// Resolve Imports
|
||||
KSTATUS status = LdrResolveImports((uintptr_t)mem);
|
||||
if (status != STATUS_SUCCESS) return status;
|
||||
|
||||
*baseOut = (uintptr_t)mem + opt->AddressOfEntryPoint;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
Reference in new issue
Block a user