// ldr_image.c - PE32+ loader and export resolver #include #include #include #include #include #include #include #include #ifndef LDR_DEBUG_VERBOSE #define LDR_DEBUG_VERBOSE 0 #endif #if LDR_DEBUG_VERBOSE #define LdrDbg(...) DbgPrint(__VA_ARGS__) #else #define LdrDbg(...) ((void)0) #endif int _stricmp(const char* a, const char* b); // --------------------------------------------------------------------------- // PE32+ structures (just what we need) // --------------------------------------------------------------------------- #define PE_SIGNATURE 0x00004550UL // 'PE\0\0' #define IMAGE_FILE_MACHINE_AMD64 0x8664 #define IMAGE_FILE_MACHINE_I386 0x014c #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b #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; static size_t KiAlignPeSize(size_t value) { return (value + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); } static KSTATUS KiComputeRequiredImageSize(const PE_HEADER* pe, size_t fileSize, size_t* outSize) { if (!pe || !outSize) return STATUS_INVALID_PARAMETER; const OPT_HEADER64* opt = &pe->OptionalHeader; size_t required = KiAlignPeSize(opt->SizeOfImage); size_t headers = KiAlignPeSize(opt->SizeOfHeaders); if (headers > required) required = headers; const SECTION_HEADER* sec = (const SECTION_HEADER*)((const uint8_t*)&pe->OptionalHeader + pe->SizeOfOptionalHeader); for (int i = 0; i < pe->NumberOfSections; i++) { size_t va = (size_t)sec[i].VirtualAddress; size_t virt = (size_t)sec[i].VirtualSize; size_t raw = (size_t)sec[i].SizeOfRawData; size_t span = virt ? virt : raw; if (raw > span) span = raw; if (!span) continue; if (va > (size_t)-1 - span) return STATUS_NO_MEMORY; size_t end = KiAlignPeSize(va + span); if (end > required) required = end; if (raw) { size_t rawBase = (size_t)sec[i].PointerToRawData; if (rawBase > fileSize || raw > fileSize - rawBase) return STATUS_FAILURE; } } if (!required) return STATUS_FAILURE; *outSize = required; return STATUS_SUCCESS; } 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 #define IMAGE_SCN_CNT_CODE 0x00000020u #define IMAGE_SCN_MEM_EXECUTE 0x20000000u #define IMAGE_SCN_MEM_READ 0x40000000u #define IMAGE_SCN_MEM_WRITE 0x80000000u // --------------------------------------------------------------------------- // Module registry // --------------------------------------------------------------------------- #define MAX_MODULES 32 typedef struct { char Name[32]; uintptr_t Base; size_t Size; } MODULE_ENTRY; static MODULE_ENTRY gModules[MAX_MODULES]; static uint32_t gModuleCount = 0; static const char* KiCanonicalModuleName(const char* name) { if (!name) return name; if (!_stricmp(name, "kernel32.dll") || !_stricmp(name, "user32.dll") || !_stricmp(name, "gdi32.dll") || !_stricmp(name, "ole32.dll") || !_stricmp(name, "winmm.dll")) { return "win32.dll"; } if (!_stricmp(name, "msvcrt.dll")) { return "posix.dll"; } return name; } static uintptr_t KiFindModuleBaseInTable(const MODULE_ENTRY* modules, uint32_t count, const char* name) { for (uint32_t i = 0; i < count; i++) { if (_stricmp(modules[i].Name, name) == 0) return modules[i].Base; } return 0; } static void KiRegisterModuleEntry(MODULE_ENTRY* entry, const char* name, uintptr_t base, size_t size) { strncpy(entry->Name, name, 31); entry->Name[31] = '\0'; entry->Base = base; entry->Size = size; } void LdrRegisterModulesFromBootInfo(const BOOT_INFO* bootInfo) { for (uint32_t i = 0; i < bootInfo->ModuleCount && gModuleCount < MAX_MODULES; i++) { // Only register modules that have a virtual base (Kernel, HAL) if (bootInfo->Modules[i].VirtBase != 0) { KiRegisterModuleEntry(&gModules[gModuleCount], bootInfo->Modules[i].Name, bootInfo->Modules[i].VirtBase, bootInfo->Modules[i].ImageSize); gModuleCount++; } } } void LdrRegisterModule(const char* name, uintptr_t base) { if (gModuleCount < MAX_MODULES) { KiRegisterModuleEntry(&gModules[gModuleCount], name, base, 0); gModuleCount++; } } static uintptr_t KiFindGlobalModuleBase(const char* name) { return KiFindModuleBaseInTable(gModules, gModuleCount, KiCanonicalModuleName(name)); } static uintptr_t KiFindProcessModuleBaseInTable( const PROCESS_MODULE* modules, uint32_t count, const char* name ) { for (uint32_t i = 0; i < count; i++) { if (_stricmp(modules[i].Name, KiCanonicalModuleName(name)) == 0) { return modules[i].Base; } } return 0; } static uintptr_t KiFindProcessOrGlobalModuleBase(PROCESS* process, const char* name) { if (process) { uintptr_t base = KiFindProcessModuleBaseInTable(process->Modules, process->ModuleCount, name); if (base) return base; } return KiFindGlobalModuleBase(name); } static void* KiGetExportAtBase(uintptr_t base, const char* functionName) { 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; } // --------------------------------------------------------------------------- // Export lookup // --------------------------------------------------------------------------- void* LdrGetExport(const char* moduleName, const char* functionName) { uintptr_t base = KiFindGlobalModuleBase(KiCanonicalModuleName(moduleName)); return KiGetExportAtBase(base, functionName); } void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const char* functionName) { uintptr_t base = KiFindProcessOrGlobalModuleBase(process, KiCanonicalModuleName(moduleName)); return KiGetExportAtBase(base, functionName); } size_t LdrGetImageSize(uintptr_t base) { if (!base) return 0; uint32_t peOffset = *(uint32_t *)(base + 0x3C); PE_HEADER* pe = (PE_HEADER *)(base + peOffset); if (pe->Signature != PE_SIGNATURE) return 0; return (size_t)pe->OptionalHeader.SizeOfImage; } size_t LdrGetMappedImageSize(PROCESS* process, uintptr_t base) { if (!base) return 0; if (process) { size_t regionSize = MmGetUserRegionSize(process, (const void*)base); if (regionSize) return regionSize; } size_t globalRegionSize = MmGetUserRegionSizeByBase((const void*)base); if (globalRegionSize) return globalRegionSize; return LdrGetImageSize(base); } static void KiFreeLoadedImageForProcess(PROCESS* process, uintptr_t imageBase, bool user) { if (!imageBase) return; size_t imageSize = user ? LdrGetMappedImageSize(process, imageBase) : LdrGetImageSize(imageBase); if (user) MmFreeUserImageForProcess(process, (void*)imageBase, imageSize); else MmFreePool((void*)imageBase); } static void KiFreeLoadedImage(uintptr_t imageBase, bool user) { KiFreeLoadedImageForProcess(KeGetCurrentProcess(), imageBase, user); } KSTATUS LdrProtectImageForProcess(PROCESS* process, uintptr_t imageBase, bool user) { if (!imageBase) return STATUS_INVALID_PARAMETER; if (user && !process) return STATUS_INVALID_PARAMETER; size_t mappedSize = user ? LdrGetMappedImageSize(process, imageBase) : LdrGetImageSize(imageBase); if (!mappedSize) return STATUS_FAILURE; uint32_t peOffset = *(uint32_t *)(imageBase + 0x3C); if ((size_t)peOffset > mappedSize || mappedSize - (size_t)peOffset < sizeof(uint32_t) + 20) return STATUS_FAILURE; PE_HEADER* pe = (PE_HEADER *)(imageBase + peOffset); if (pe->Signature != PE_SIGNATURE) return STATUS_FAILURE; size_t sectionTableOffset = (size_t)peOffset + 24u + (size_t)pe->SizeOfOptionalHeader; size_t sectionTableSize = (size_t)pe->NumberOfSections * sizeof(SECTION_HEADER); if (sectionTableOffset > mappedSize || sectionTableSize > mappedSize - sectionTableOffset) return STATUS_FAILURE; SECTION_HEADER* sec = (SECTION_HEADER *)(imageBase + sectionTableOffset); for (int i = 0; i < pe->NumberOfSections; i++) { size_t protectSize = sec[i].VirtualSize ? sec[i].VirtualSize : sec[i].SizeOfRawData; if (!protectSize) continue; uintptr_t secBase = imageBase + (uintptr_t)sec[i].VirtualAddress; uintptr_t secEnd = secBase + protectSize; uintptr_t mapEnd = imageBase + mappedSize; if (secBase < imageBase || secBase >= mapEnd) { LdrDbg("LDR: protect skipping out-of-range section %d base=%016llx size=%llx image=%016llx mapped=%llx\n", i, (unsigned long long)secBase, (unsigned long long)protectSize, (unsigned long long)imageBase, (unsigned long long)mappedSize); continue; } if (secEnd < secBase) return STATUS_NO_MEMORY; if (secEnd > mapEnd) { LdrDbg("LDR: protect clamping section %d end=%016llx to mapEnd=%016llx\n", i, (unsigned long long)secEnd, (unsigned long long)mapEnd); protectSize = (size_t)(mapEnd - secBase); } uint64_t prot = PAGE_PRESENT | (user ? PAGE_USER : 0); if (sec[i].Characteristics & IMAGE_SCN_MEM_WRITE) prot |= PAGE_WRITE; if (!((sec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) || (sec[i].Characteristics & IMAGE_SCN_CNT_CODE))) prot |= PAGE_NX; KSTATUS s; if (user) { s = MmProtectUserRegion(process, (void*)secBase, protectSize, prot); } else { s = MmSetPageProtect(secBase, protectSize, prot); } if (!KSUCCESS(s)) { LdrDbg("LDR: protect failed section=%d va=%016llx size=%llx prot=%016llx st=%d image=%016llx mapped=%llx\n", i, (unsigned long long)secBase, (unsigned long long)protectSize, (unsigned long long)prot, s, (unsigned long long)imageBase, (unsigned long long)mappedSize); return s; } } return STATUS_SUCCESS; } // --------------------------------------------------------------------------- // 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 // --------------------------------------------------------------------------- typedef void* (*EXPORT_LOOKUP_FN)(void* ctx, const char* moduleName, const char* functionName); static void* KiGlobalExportLookup(void* ctx, const char* moduleName, const char* functionName) { (void)ctx; return LdrGetExport(moduleName, functionName); } static void* KiProcessExportLookup(void* ctx, const char* moduleName, const char* functionName) { return LdrGetExportForProcess((PROCESS*)ctx, moduleName, functionName); } static KSTATUS LdrResolveImportsCommon( PROCESS* process, uintptr_t imageBase, EXPORT_LOOKUP_FN lookup, void* ctx ) { 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; 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); //Compatibility remap. dllName = KiCanonicalModuleName(dllName); LdrDbg("LDR: resolve import dll=%s image=%016llx pid=%u\n", dllName, (unsigned long long)imageBase, process ? process->ProcessId : 0); // // Ensure dependency is loaded into the process // if (process) { uintptr_t modBase = KiFindProcessOrGlobalModuleBase(process, dllName); if (!modBase) { KSTATUS s = LdrLoadModuleForProcess(process, dllName, &modBase); if (!KSUCCESS(s)) { DbgPrint( "LdrResolveImports: Failed to load dependency %s (%d)\n", dllName, s ); return s; } } } 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)) { DbgPrint( "LdrResolveImports: Ordinal imports not supported! (%s)\n", dllName ); return STATUS_NOT_SUPPORTED; } const char* funcName = (const char *)(imageBase + (*origThunk & 0xFFFFFFFF) + 2); void* addr = lookup(ctx, dllName, funcName); if (!addr) { DbgPrint( "LdrResolveImports: Function not found! (%s!%s)\n", dllName, funcName ); return STATUS_NOT_FOUND; } *thunk = (uint64_t)addr; thunk++; origThunk++; } imp++; } return STATUS_SUCCESS; } KSTATUS LdrResolveImports(uintptr_t imageBase) { return LdrResolveImportsCommon( (PROCESS*)0, imageBase, KiGlobalExportLookup, (void*)0 ); } KSTATUS LdrResolveImportsForProcess(PROCESS* process, uintptr_t imageBase) { return LdrResolveImportsCommon( process, imageBase, KiProcessExportLookup, process ); } static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) { if (!process || !name || !base) return STATUS_INVALID_PARAMETER; for (uint32_t i = 0; i < process->ModuleCount; i++) { if (_stricmp(process->Modules[i].Name, name) == 0) { return STATUS_SUCCESS; } } if (process->ModuleCount >= PROCESS_MODULE_MAX) return STATUS_NO_MEMORY; PROCESS_MODULE* mod = &process->Modules[process->ModuleCount++]; strncpy(mod->Name, name, 31); mod->Name[31] = '\0'; mod->Base = base; mod->Size = LdrGetMappedImageSize(process, base); return STATUS_SUCCESS; } KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) { if (!process || !filename) return STATUS_INVALID_PARAMETER; filename = KiCanonicalModuleName(filename); for (uint32_t i = 0; i < process->ModuleCount; i++) { if (_stricmp(process->Modules[i].Name, filename) == 0) { if (baseOut) *baseOut = process->Modules[i].Base; return STATUS_SUCCESS; } } FILE_OBJECT* f; KSTATUS s = IoOpenFile(SysSvFromCstr(filename), &f); if (!KSUCCESS(s)) { char path[260]; memcpy(path, "0:/system/", 10); memcpy(path + 10, filename, strnlen(filename, 248) + 1); path[259] = '\0'; s = IoOpenFile(SysSvFromCstr(path), &f); if (!KSUCCESS(s)) { DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s); return s; } } uint64_t fileSize = IoGetFileSize(f); LdrDbg("LDR: load module pid=%u file=%s size=%llu\n", process->ProcessId, filename, fileSize); void* mem = MmAllocatePool((size_t)fileSize); if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; } size_t got = 0; s = IoReadFile(f, mem, (size_t)fileSize, &got); IoCloseFile(f); if (!KSUCCESS(s)) { MmFreePool(mem); return s; } uintptr_t imageBase = 0; s = LdrLoadImageForProcess(process, mem, (size_t)fileSize, &imageBase); MmFreePool(mem); if (!KSUCCESS(s)) { DbgPrint("LDR: load image for module %s failed st=%d\n", filename, s); return s; } LdrDbg("LDR: module mapped pid=%u file=%s base=%016llx size=%llx\n", process->ProcessId, filename, (unsigned long long)imageBase, (unsigned long long)LdrGetMappedImageSize(process, imageBase)); s = LdrResolveImportsForProcess(process, imageBase); if (!KSUCCESS(s)) { KiFreeLoadedImageForProcess(process, imageBase, true); return s; } s = LdrProtectImageForProcess(process, imageBase, true); if (!KSUCCESS(s)) { KiFreeLoadedImageForProcess(process, imageBase, true); return s; } s = LdrRegisterProcessModule(process, filename, imageBase); if (!KSUCCESS(s)) { KiFreeLoadedImageForProcess(process, imageBase, true); return s; } LdrDbg("LDR: module registered pid=%u name=%s base=%016llx modules=%u\n", process->ProcessId, filename, (unsigned long long)imageBase, process->ModuleCount); if (baseOut) *baseOut = imageBase; return STATUS_SUCCESS; } // LdrLoadImage - load a PE from a raw buffer into virtual space. static KSTATUS KiLdrLoadImageCommon(PROCESS* process, const void* buffer, size_t size, uintptr_t* baseOut, bool user) { if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER; if (user && !process) return STATUS_INVALID_PARAMETER; if (size < 0x40) return STATUS_FAILURE; const uint8_t* bytes = (const uint8_t*)buffer; uint32_t peOffset = *(const uint32_t *)(bytes + 0x3C); if ((size_t)peOffset > size || size - (size_t)peOffset < 24) return STATUS_FAILURE; uint32_t signature = *(const uint32_t *)(bytes + peOffset); if (signature != PE_SIGNATURE) return STATUS_FAILURE; uint16_t machine = *(const uint16_t *)(bytes + peOffset + 4); uint16_t optionalSize = *(const uint16_t *)(bytes + peOffset + 20); if (optionalSize < sizeof(OPT_HEADER64) || (size_t)peOffset + 24 + optionalSize > size) { return STATUS_FAILURE; } uint16_t optionalMagic = *(const uint16_t *)(bytes + peOffset + 24); if (machine == IMAGE_FILE_MACHINE_I386 || optionalMagic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { DbgPrint("LdrLoadImage: hard error: 32-bit PE images are not supported (machine=0x%X magic=0x%X)\n", machine, optionalMagic); return STATUS_NOT_SUPPORTED; } if (machine != IMAGE_FILE_MACHINE_AMD64 || optionalMagic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { DbgPrint("LdrLoadImage: unsupported PE image (machine=0x%X magic=0x%X)\n", machine, optionalMagic); return STATUS_NOT_SUPPORTED; } PE_HEADER* pe = (PE_HEADER *)(bytes + peOffset); OPT_HEADER64* opt = &pe->OptionalHeader; size_t imageSize = 0; KSTATUS sizeStatus = KiComputeRequiredImageSize(pe, size, &imageSize); if (!KSUCCESS(sizeStatus)) return sizeStatus; LdrDbg("LDR: image header sizeOfImage=%x required=%llx preferred=%016llx sections=%u user=%d pid=%u\n", opt->SizeOfImage, (unsigned long long)imageSize, (unsigned long long)opt->ImageBase, pe->NumberOfSections, user ? 1 : 0, process ? process->ProcessId : 0); void* mem = user ? MmAllocateUserImageForProcess(process, imageSize) : MmAllocatePool(imageSize); if (!mem) { DbgPrint("LDR: image allocation failed required=%llx user=%d pid=%u\n", (unsigned long long)imageSize, user ? 1 : 0, process ? process->ProcessId : 0); return STATUS_NO_MEMORY; } memset(mem, 0, imageSize); 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) { size_t copySize = sec[i].SizeOfRawData; if (sec[i].VirtualSize && sec[i].VirtualSize < copySize) copySize = sec[i].VirtualSize; memcpy(dst, src, copySize); } uint64_t prot = PAGE_PRESENT | PAGE_WRITE | (user ? PAGE_USER : 0); MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress, sec[i].VirtualSize ? sec[i].VirtualSize : sec[i].SizeOfRawData, prot); } uintptr_t delta = (uintptr_t)mem - opt->ImageBase; if (delta != 0) { LdrApplyRelocations((uintptr_t)mem, delta); } *baseOut = (uintptr_t)mem; return STATUS_SUCCESS; } KSTATUS LdrLoadImageForProcess(PROCESS* process, const void* buffer, size_t size, uintptr_t* baseOut) { return KiLdrLoadImageCommon(process, buffer, size, baseOut, true); } KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user) { return KiLdrLoadImageCommon(user ? KeGetCurrentProcess() : (PROCESS*)0, buffer, size, baseOut, user); }