redo boot around an explicit system volume and fixed kernel/HAL images, redo volumes and filesystem paths around capabilities instead of drive numbers and kernel CWD state, add a capability/security model with derived and revocable handles and security contexts, add port IPC and rssrv to manage the userspace resource namespace and publish the system volume, move process, dll and path loading to userspace and add process builder syscalls, init now supervises rssrv and the session process, redo the syscall ABI around request structs and four arguments, add a volume manager and proper partition arrival/removal handling so FAT and NTFS mount dynamically, clean up the HAL/DDK boundary so drivers don't depend on HAL internals, save fpu state properly now, the serial driver doesn't hang on a missing port, fix AHCI startup races, fix timeout accounting, fix shared mmap writeback, more smoke tests
This commit is contained in:
1 parent
4add6789ba
commit
f882d339fb
232 files changed
+20934
-8898
No files matched your search
+119
-470
@@ -9,7 +9,6 @@
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_system_root.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#ifndef LDR_DEBUG_VERBOSE
|
||||
@@ -164,17 +163,17 @@ typedef struct {
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000u
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000u
|
||||
|
||||
// Module registry.
|
||||
#define MAX_MODULES 32
|
||||
// Global kernel-module registry. Entries are allocated as modules arrive so boot-driver count is not an ABI limit.
|
||||
#define LDR_KERNEL_MODULE_NAME_MAX 64u
|
||||
|
||||
typedef struct {
|
||||
char Name[32];
|
||||
typedef struct MODULE_ENTRY {
|
||||
char Name[LDR_KERNEL_MODULE_NAME_MAX];
|
||||
uintptr_t Base;
|
||||
size_t Size;
|
||||
size_t Size;
|
||||
struct MODULE_ENTRY* Next;
|
||||
} MODULE_ENTRY;
|
||||
|
||||
static MODULE_ENTRY gModules[MAX_MODULES];
|
||||
static uint32_t gModuleCount = 0;
|
||||
static MODULE_ENTRY* gModules;
|
||||
|
||||
// Ensures a user image page is privately writable before the kernel patches it.
|
||||
static KSTATUS KiEnsureUserImageWrite(PROCESS* process, bool user, uintptr_t address, size_t size) {
|
||||
@@ -182,62 +181,50 @@ static KSTATUS KiEnsureUserImageWrite(PROCESS* process, bool user, uintptr_t add
|
||||
return MmVadMakeImageRangePrivate(process, (void*)address, size);
|
||||
}
|
||||
|
||||
// Implements the canonical Module Name operation.
|
||||
static const char* KiCanonicalModuleName(const char* name) {
|
||||
if (!name) return name;
|
||||
// Allocates and inserts one global kernel-module registry entry.
|
||||
static KSTATUS KiRegisterGlobalModule(const char* name, uintptr_t base, size_t size) {
|
||||
if (!name || !*name || !base || !size)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
if (!_stricmp(name, "kernel32.dll") ||
|
||||
!_stricmp(name, "user32.dll") ||
|
||||
!_stricmp(name, "gdi32.dll") ||
|
||||
!_stricmp(name, "ole32.dll") ||
|
||||
!_stricmp(name, "winmm.dll")) {
|
||||
return "win32.dll";
|
||||
}
|
||||
size_t nameLength = 0;
|
||||
while (nameLength < LDR_KERNEL_MODULE_NAME_MAX && name[nameLength]) nameLength++;
|
||||
if (nameLength == 0 || nameLength >= LDR_KERNEL_MODULE_NAME_MAX)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
if (!_stricmp(name, "msvcrt.dll")) {
|
||||
return "posix.dll";
|
||||
}
|
||||
MODULE_ENTRY* entry = (MODULE_ENTRY*)MmAllocatePool(sizeof(MODULE_ENTRY));
|
||||
if (!entry)
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
// Returns module Base In Table.
|
||||
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;
|
||||
}
|
||||
|
||||
// Registers module Entry.
|
||||
static void KiRegisterModuleEntry(MODULE_ENTRY* entry, const char* name, uintptr_t base, size_t size) {
|
||||
strncpy(entry->Name, name, 31);
|
||||
entry->Name[31] = '\0';
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
memcpy(entry->Name, name, nameLength);
|
||||
entry->Name[nameLength] = '\0';
|
||||
entry->Base = base;
|
||||
entry->Size = size;
|
||||
entry->Next = gModules;
|
||||
gModules = entry;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the ldr Register Modules From Boot Info operation.
|
||||
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++;
|
||||
}
|
||||
}
|
||||
// Registers the two fixed bootstrap images described explicitly by BOOT_INFO.
|
||||
KSTATUS LdrRegisterModulesFromBootInfo(const BOOT_INFO* bootInfo) {
|
||||
if (!bootInfo)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KSTATUS status = KiRegisterGlobalModule("kernel.exe",
|
||||
bootInfo->KernelImage.VirtualBase,
|
||||
bootInfo->KernelImage.ImageSize);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
return KiRegisterGlobalModule("hal.dll",
|
||||
bootInfo->HalImage.VirtualBase,
|
||||
bootInfo->HalImage.ImageSize);
|
||||
}
|
||||
|
||||
// Implements the ldr Register Module operation.
|
||||
void LdrRegisterModule(const char* name, uintptr_t base) {
|
||||
if (gModuleCount < MAX_MODULES) {
|
||||
size_t size = base ? LdrGetImageSize(base) : 0;
|
||||
KiRegisterModuleEntry(&gModules[gModuleCount], name, base, size);
|
||||
gModuleCount++;
|
||||
}
|
||||
// Registers one loaded kernel module without imposing a fixed registry capacity.
|
||||
KSTATUS LdrRegisterModule(const char* name, uintptr_t base) {
|
||||
size_t size = base ? LdrGetImageSize(base) : 0;
|
||||
return KiRegisterGlobalModule(name, base, size);
|
||||
}
|
||||
|
||||
// Finds the loaded kernel module that owns an address.
|
||||
@@ -245,111 +232,70 @@ const char* LdrFindKernelModuleByAddress(uintptr_t address, uintptr_t* baseOut,
|
||||
if (baseOut) *baseOut = 0;
|
||||
if (sizeOut) *sizeOut = 0;
|
||||
|
||||
for (uint32_t i = 0; i < gModuleCount; i++) {
|
||||
uintptr_t base = gModules[i].Base;
|
||||
size_t size = gModules[i].Size;
|
||||
if (!base || !size)
|
||||
for (MODULE_ENTRY* module = gModules; module; module = module->Next) {
|
||||
uintptr_t base = module->Base;
|
||||
size_t size = module->Size;
|
||||
if (!base || !size || base > UINTPTR_MAX - size)
|
||||
continue;
|
||||
if (address >= base && address < base + size) {
|
||||
if (baseOut) *baseOut = base;
|
||||
if (sizeOut) *sizeOut = size;
|
||||
return gModules[i].Name;
|
||||
return module->Name;
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
// Returns global Module Base.
|
||||
// Returns the loaded global kernel module base for one exact module name.
|
||||
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;
|
||||
}
|
||||
if (!name) return 0;
|
||||
for (MODULE_ENTRY* module = gModules; module; module = module->Next) {
|
||||
if (_stricmp(module->Name, name) == 0)
|
||||
return module->Base;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns process Or Global Module Base.
|
||||
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);
|
||||
}
|
||||
|
||||
// Returns export At Base.
|
||||
static void* KiGetExportAtBase(uintptr_t base, const char* functionName) {
|
||||
if (!base) return (void *)0;
|
||||
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;
|
||||
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;
|
||||
if (opt->NumberOfRvaAndSizes < 1) return (void*)0;
|
||||
|
||||
uint32_t expRva = opt->DataDirectory[0].VirtualAddress;
|
||||
if (!expRva) return (void *)0;
|
||||
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);
|
||||
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]);
|
||||
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*)(base + functions[ord]);
|
||||
}
|
||||
}
|
||||
return (void *)0;
|
||||
return (void*)0;
|
||||
}
|
||||
|
||||
// Export lookup.
|
||||
void* LdrGetExport(const char* moduleName, const char* functionName) {
|
||||
uintptr_t base = KiFindGlobalModuleBase(KiCanonicalModuleName(moduleName));
|
||||
uintptr_t base = KiFindGlobalModuleBase(moduleName);
|
||||
return KiGetExportAtBase(base, functionName);
|
||||
}
|
||||
|
||||
// Implements the ldr Get Export For Process operation.
|
||||
void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const char* functionName) {
|
||||
uintptr_t base = KiFindProcessOrGlobalModuleBase(process, KiCanonicalModuleName(moduleName));
|
||||
return KiGetExportAtBase(base, functionName);
|
||||
}
|
||||
|
||||
// Looks up an export in a specific process module mapping.
|
||||
void* LdrGetModuleExportForProcess(PROCESS* process, uintptr_t imageBase, const char* functionName) {
|
||||
if (!process || !imageBase || !functionName) return (void*)0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
|
||||
int found = 0;
|
||||
for (uint32_t i = 0; i < process->ModuleCount; i++) {
|
||||
if (process->Modules[i].Base == imageBase) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
if (!found) return (void*)0;
|
||||
|
||||
return KiGetExportAtBase(imageBase, functionName);
|
||||
}
|
||||
|
||||
// Implements the ldr Get Image Size operation.
|
||||
size_t LdrGetImageSize(uintptr_t base) {
|
||||
if (!base) return 0;
|
||||
uint32_t peOffset = *(uint32_t *)(base + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)(base + peOffset);
|
||||
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;
|
||||
}
|
||||
@@ -401,11 +347,11 @@ KSTATUS LdrProtectImageForProcess(PROCESS* process, uintptr_t imageBase, bool us
|
||||
size_t mappedSize = user ? LdrGetMappedImageSize(process, imageBase) : LdrGetImageSize(imageBase);
|
||||
if (!mappedSize) return NTLSTATUS_FAILURE;
|
||||
|
||||
uint32_t peOffset = *(uint32_t *)(imageBase + 0x3C);
|
||||
uint32_t peOffset = *(uint32_t*)(imageBase + 0x3C);
|
||||
if ((size_t)peOffset > mappedSize || mappedSize - (size_t)peOffset < sizeof(uint32_t) + 20)
|
||||
return NTLSTATUS_FAILURE;
|
||||
|
||||
PE_HEADER* pe = (PE_HEADER *)(imageBase + peOffset);
|
||||
PE_HEADER* pe = (PE_HEADER*)(imageBase + peOffset);
|
||||
if (pe->Signature != PE_SIGNATURE) return NTLSTATUS_FAILURE;
|
||||
|
||||
size_t sectionTableOffset = (size_t)peOffset + 24u + (size_t)pe->SizeOfOptionalHeader;
|
||||
@@ -421,7 +367,7 @@ KSTATUS LdrProtectImageForProcess(PROCESS* process, uintptr_t imageBase, bool us
|
||||
for (size_t i = 0; i < pageCount; ++i)
|
||||
pageProt[i] = headerProt;
|
||||
|
||||
SECTION_HEADER* sec = (SECTION_HEADER *)(imageBase + sectionTableOffset);
|
||||
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;
|
||||
@@ -497,8 +443,8 @@ uintptr_t LdrGetEntryPoint(uintptr_t base) {
|
||||
|
||||
// Base Relocation.
|
||||
static KSTATUS LdrApplyRelocationsForProcess(PROCESS* process, uintptr_t base, uintptr_t delta, bool user) {
|
||||
uint32_t peOffset = *(uint32_t *)(base + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)(base + peOffset);
|
||||
uint32_t peOffset = *(uint32_t*)(base + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER*)(base + peOffset);
|
||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||
|
||||
if (opt->NumberOfRvaAndSizes < 6) return NTLSTATUS_SUCCESS;
|
||||
@@ -506,12 +452,12 @@ static KSTATUS LdrApplyRelocationsForProcess(PROCESS* process, uintptr_t base, u
|
||||
uint32_t relocSize = opt->DataDirectory[5].Size;
|
||||
if (!relocRva || !relocSize) return NTLSTATUS_SUCCESS;
|
||||
|
||||
BASE_RELOC* block = (BASE_RELOC *)(base + relocRva);
|
||||
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);
|
||||
uint16_t* entries = (uint16_t*)(block + 1);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
int type = entries[i] >> 12;
|
||||
@@ -519,39 +465,21 @@ static KSTATUS LdrApplyRelocationsForProcess(PROCESS* process, uintptr_t base, u
|
||||
|
||||
// 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);
|
||||
uint64_t* ptr = (uint64_t*)(base + block->VirtualAddress + offset);
|
||||
KSTATUS ws = KiEnsureUserImageWrite(process, user, (uintptr_t)ptr, sizeof(*ptr));
|
||||
if (!KSUCCESS(ws)) return ws;
|
||||
*ptr += delta;
|
||||
}
|
||||
}
|
||||
block = (BASE_RELOC *)((uint8_t *)block + block->SizeOfBlock);
|
||||
block = (BASE_RELOC*)((uint8_t*)block + block->SizeOfBlock);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Import resolution.
|
||||
typedef void* (*EXPORT_LOOKUP_FN)(void* ctx, const char* moduleName, const char* functionName);
|
||||
|
||||
// Implements the global Export Lookup operation.
|
||||
static void* KiGlobalExportLookup(void* ctx, const char* moduleName, const char* functionName) {
|
||||
(void)ctx;
|
||||
return LdrGetExport(moduleName, functionName);
|
||||
}
|
||||
|
||||
// Processes export Lookup.
|
||||
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);
|
||||
// Resolves imports for kernel images against the kernel module registry.
|
||||
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)
|
||||
@@ -561,101 +489,57 @@ static KSTATUS LdrResolveImportsCommon(
|
||||
if (!impRva)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
IMPORT_DESC* imp = (IMPORT_DESC *)(imageBase + impRva);
|
||||
|
||||
IMPORT_DESC* imp = (IMPORT_DESC*)(imageBase + impRva);
|
||||
while (imp->Name) {
|
||||
const char* dllName = (const char *)(imageBase + imp->Name);
|
||||
const char* dllName = (const char*)(imageBase + imp->Name);
|
||||
LdrDbg("LDR: resolve kernel import dll=%s image=%016llx\n",
|
||||
dllName, (unsigned long long)imageBase);
|
||||
|
||||
//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, LDR_MODULE_LOAD_IMPORT, &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));
|
||||
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
|
||||
);
|
||||
DbgPrint("LdrResolveImports: Ordinal imports not supported! (%s)\n", dllName);
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
const char* funcName =
|
||||
(const char *)(imageBase +
|
||||
(*origThunk & 0xFFFFFFFF) + 2);
|
||||
|
||||
void* addr = lookup(ctx, dllName, funcName);
|
||||
|
||||
(const char*)(imageBase + (*origThunk & 0xFFFFFFFF) + 2);
|
||||
void* addr = LdrGetExport(dllName, funcName);
|
||||
if (!addr) {
|
||||
DbgPrint(
|
||||
"LdrResolveImports: Function not found! (%s!%s)\n",
|
||||
dllName,
|
||||
funcName
|
||||
);
|
||||
DbgPrint("LdrResolveImports: Function not found! (%s!%s)\n", dllName, funcName);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
KSTATUS ws = KiEnsureUserImageWrite(process, process != (PROCESS*)0, (uintptr_t)thunk, sizeof(*thunk));
|
||||
if (!KSUCCESS(ws)) return ws;
|
||||
*thunk = (uint64_t)addr;
|
||||
|
||||
thunk++;
|
||||
origThunk++;
|
||||
}
|
||||
|
||||
imp++;
|
||||
}
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Implements the ldr Resolve Imports operation.
|
||||
KSTATUS LdrResolveImports(uintptr_t imageBase) {
|
||||
return LdrResolveImportsCommon(
|
||||
(PROCESS*)0,
|
||||
imageBase,
|
||||
KiGlobalExportLookup,
|
||||
(void*)0
|
||||
);
|
||||
}
|
||||
|
||||
// Implements the ldr Resolve Imports For Process operation.
|
||||
KSTATUS LdrResolveImportsForProcess(PROCESS* process, uintptr_t imageBase) {
|
||||
return LdrResolveImportsCommon(
|
||||
process,
|
||||
imageBase,
|
||||
KiProcessExportLookup,
|
||||
process
|
||||
);
|
||||
// Requires an already-mapped bootstrap image to have no import directory.
|
||||
KSTATUS LdrRequireNoImports(uintptr_t imageBase) {
|
||||
if (!imageBase) return NTLSTATUS_INVALID_PARAMETER;
|
||||
uint32_t peOffset = *(uint32_t*)(imageBase + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER*)(imageBase + peOffset);
|
||||
if (pe->Signature != PE_SIGNATURE ||
|
||||
pe->Machine != IMAGE_FILE_MACHINE_AMD64 ||
|
||||
pe->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
|
||||
return NTLSTATUS_FAILURE;
|
||||
if (pe->OptionalHeader.NumberOfRvaAndSizes >= 2 &&
|
||||
(pe->OptionalHeader.DataDirectory[1].VirtualAddress != 0 ||
|
||||
pe->OptionalHeader.DataDirectory[1].Size != 0))
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
if (pe->OptionalHeader.NumberOfRvaAndSizes >= 14 &&
|
||||
(pe->OptionalHeader.DataDirectory[13].VirtualAddress != 0 ||
|
||||
pe->OptionalHeader.DataDirectory[13].Size != 0))
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Loads a user image from an open file through the image-section object model.
|
||||
@@ -693,261 +577,26 @@ KSTATUS LdrLoadImageFileForProcess(PROCESS* process, FILE_OBJECT* file, uintptr_
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Converts a process module entry into the public module ABI.
|
||||
static void KiFillProcessModuleInfo(const PROCESS_MODULE* mod, SYS_MODULE_INFO* info) {
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->Size = sizeof(*info);
|
||||
info->Version = SYS_MODULE_INFO_VERSION;
|
||||
info->Flags = mod->Flags;
|
||||
if (mod->PublicRefCount)
|
||||
info->Flags |= SYS_MODULE_FLAG_PUBLIC_REF;
|
||||
if (mod->ImportRefCount)
|
||||
info->Flags |= SYS_MODULE_FLAG_IMPORT_PINNED;
|
||||
if (mod->PublicRefCount && mod->ImportRefCount == 0)
|
||||
info->Flags |= SYS_MODULE_FLAG_UNLOADABLE;
|
||||
info->Base = mod->Base;
|
||||
info->ImageSize = mod->Size;
|
||||
info->EntryPoint = mod->EntryPoint;
|
||||
info->PublicRefCount = mod->PublicRefCount;
|
||||
info->ImportRefCount = mod->ImportRefCount;
|
||||
info->TotalRefCount = mod->PublicRefCount + mod->ImportRefCount;
|
||||
strncpy(info->Name, mod->Name, sizeof(info->Name) - 1);
|
||||
info->Name[sizeof(info->Name) - 1] = '\0';
|
||||
}
|
||||
|
||||
// Adds one reference of the requested class to an existing process module.
|
||||
static void KiReferenceProcessModule(PROCESS_MODULE* mod, uint32_t loadFlags) {
|
||||
if (loadFlags & LDR_MODULE_LOAD_PUBLIC)
|
||||
mod->PublicRefCount++;
|
||||
if (loadFlags & LDR_MODULE_LOAD_IMPORT)
|
||||
mod->ImportRefCount++;
|
||||
}
|
||||
|
||||
// Finds a process module by canonical name while the caller owns ModuleLock.
|
||||
static PROCESS_MODULE* KiFindProcessModuleByNameLocked(PROCESS* process, const char* name) {
|
||||
for (uint32_t i = 0; i < process->ModuleCount; i++) {
|
||||
if (_stricmp(process->Modules[i].Name, name) == 0)
|
||||
return &process->Modules[i];
|
||||
}
|
||||
return (PROCESS_MODULE*)0;
|
||||
}
|
||||
|
||||
// Finds a process module by image base while the caller owns ModuleLock.
|
||||
static PROCESS_MODULE* KiFindProcessModuleByBaseLocked(PROCESS* process, uintptr_t base) {
|
||||
for (uint32_t i = 0; i < process->ModuleCount; i++) {
|
||||
if (process->Modules[i].Base == base)
|
||||
return &process->Modules[i];
|
||||
}
|
||||
return (PROCESS_MODULE*)0;
|
||||
}
|
||||
|
||||
// Registers a mapped process module or records a reference to an existing one.
|
||||
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base, uint32_t loadFlags, uintptr_t* registeredBaseOut) {
|
||||
if (!process || !name || !base || !loadFlags) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (registeredBaseOut) *registeredBaseOut = 0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
|
||||
PROCESS_MODULE* existing = KiFindProcessModuleByNameLocked(process, name);
|
||||
if (existing) {
|
||||
KiReferenceProcessModule(existing, loadFlags);
|
||||
if (registeredBaseOut) *registeredBaseOut = existing->Base;
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
if (process->ModuleCount >= PROCESS_MODULE_MAX) {
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
PROCESS_MODULE* mod = &process->Modules[process->ModuleCount++];
|
||||
memset(mod, 0, sizeof(*mod));
|
||||
strncpy(mod->Name, name, sizeof(mod->Name) - 1);
|
||||
mod->Name[sizeof(mod->Name) - 1] = '\0';
|
||||
mod->Base = base;
|
||||
mod->Size = LdrGetMappedImageSize(process, base);
|
||||
mod->EntryPoint = LdrGetEntryPoint(base);
|
||||
KiReferenceProcessModule(mod, loadFlags);
|
||||
if (registeredBaseOut) *registeredBaseOut = mod->Base;
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Loads a DLL-like user module into a process and records the requested reference.
|
||||
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uint32_t loadFlags, uintptr_t* baseOut) {
|
||||
if (!process || !filename || !loadFlags) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
filename = KiCanonicalModuleName(filename);
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
|
||||
PROCESS_MODULE* existing = KiFindProcessModuleByNameLocked(process, filename);
|
||||
if (existing) {
|
||||
KiReferenceProcessModule(existing, loadFlags);
|
||||
if (baseOut) *baseOut = existing->Base;
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
|
||||
FILE_OBJECT* f;
|
||||
KSTATUS s = IoOpenFile(SysSvFromCstr(filename), &f);
|
||||
if (!KSUCCESS(s)) {
|
||||
char relative[260];
|
||||
size_t nameLen = strnlen(filename, sizeof(relative) - 8);
|
||||
if (nameLen + 8 >= sizeof(relative))
|
||||
return NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
memcpy(relative, "system/", 7);
|
||||
memcpy(relative + 7, filename, nameLen);
|
||||
relative[7 + nameLen] = '\0';
|
||||
|
||||
s = KeOpenSystemRootFile(relative, &f);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LdrLoadModuleForProcess: Failed to open %s on system root (%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);
|
||||
|
||||
uintptr_t imageBase = 0;
|
||||
s = LdrLoadImageFileForProcess(process, f, &imageBase);
|
||||
IoCloseFile(f);
|
||||
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;
|
||||
}
|
||||
|
||||
uintptr_t registeredBase = 0;
|
||||
s = LdrRegisterProcessModule(process, filename, imageBase, loadFlags, ®isteredBase);
|
||||
if (s == NTLSTATUS_ALREADY_EXISTS) {
|
||||
KiFreeLoadedImageForProcess(process, imageBase, true);
|
||||
imageBase = registeredBase;
|
||||
s = NTLSTATUS_SUCCESS;
|
||||
}
|
||||
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 NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Releases one public module load reference and unmaps the image when no references remain.
|
||||
KSTATUS LdrUnloadModuleForProcess(PROCESS* process, uintptr_t imageBase) {
|
||||
if (!process || !imageBase) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
char name[PROCESS_MODULE_NAME_MAX];
|
||||
uintptr_t base = 0;
|
||||
int shouldUnmap = 0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
|
||||
PROCESS_MODULE* mod = KiFindProcessModuleByBaseLocked(process, imageBase);
|
||||
if (!mod) {
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
if (mod->PublicRefCount == 0) {
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
mod->PublicRefCount--;
|
||||
if (mod->PublicRefCount == 0 && mod->ImportRefCount == 0) {
|
||||
base = mod->Base;
|
||||
strncpy(name, mod->Name, sizeof(name) - 1);
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
uint32_t index = (uint32_t)(mod - process->Modules);
|
||||
if (index + 1 < process->ModuleCount) {
|
||||
memmove(&process->Modules[index],
|
||||
&process->Modules[index + 1],
|
||||
(process->ModuleCount - index - 1) * sizeof(process->Modules[0]));
|
||||
}
|
||||
process->ModuleCount--;
|
||||
shouldUnmap = 1;
|
||||
}
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
|
||||
if (shouldUnmap) {
|
||||
LdrDbg("LDR: unload module pid=%u name=%s base=%016llx\n",
|
||||
process->ProcessId, name, (unsigned long long)base);
|
||||
KiFreeLoadedImageForProcess(process, base, true);
|
||||
}
|
||||
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Queries one loaded process module by image base.
|
||||
KSTATUS LdrQueryModuleForProcess(PROCESS* process, uintptr_t imageBase, SYS_MODULE_INFO* infoOut) {
|
||||
if (!process || !imageBase || !infoOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
|
||||
PROCESS_MODULE* mod = KiFindProcessModuleByBaseLocked(process, imageBase);
|
||||
if (!mod) {
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
KiFillProcessModuleInfo(mod, infoOut);
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Copies a bounded snapshot of the process module table.
|
||||
KSTATUS LdrCopyProcessModuleInfo(PROCESS* process, SYS_MODULE_INFO* infoOut, size_t maxCount, size_t* countOut) {
|
||||
if (!process || !countOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&process->ModuleLock);
|
||||
size_t count = process->ModuleCount;
|
||||
*countOut = count;
|
||||
size_t toCopy = count;
|
||||
if (toCopy > maxCount) toCopy = maxCount;
|
||||
if (toCopy && !infoOut) {
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
for (size_t i = 0; i < toCopy; i++)
|
||||
KiFillProcessModuleInfo(&process->Modules[i], &infoOut[i]);
|
||||
KeReleaseSpinlock(&process->ModuleLock, flags);
|
||||
return (maxCount < count) ? NTLSTATUS_BUFFER_TOO_SMALL : NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Loads a boot/kernel PE image from a raw memory buffer into kernel space.
|
||||
static KSTATUS KiLdrLoadKernelImageFromBuffer(const void* buffer, size_t size, uintptr_t* baseOut) {
|
||||
if (!buffer || !size || !baseOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (size < 0x40) return NTLSTATUS_FAILURE;
|
||||
|
||||
const uint8_t* bytes = (const uint8_t*)buffer;
|
||||
uint32_t peOffset = *(const uint32_t *)(bytes + 0x3C);
|
||||
uint32_t peOffset = *(const uint32_t*)(bytes + 0x3C);
|
||||
if ((size_t)peOffset > size || size - (size_t)peOffset < 24) return NTLSTATUS_FAILURE;
|
||||
|
||||
uint32_t signature = *(const uint32_t *)(bytes + peOffset);
|
||||
uint32_t signature = *(const uint32_t*)(bytes + peOffset);
|
||||
if (signature != PE_SIGNATURE) return NTLSTATUS_FAILURE;
|
||||
|
||||
uint16_t machine = *(const uint16_t *)(bytes + peOffset + 4);
|
||||
uint16_t optionalSize = *(const uint16_t *)(bytes + peOffset + 20);
|
||||
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 NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
uint16_t optionalMagic = *(const uint16_t *)(bytes + peOffset + 24);
|
||||
uint16_t optionalMagic = *(const uint16_t*)(bytes + peOffset + 24);
|
||||
if (machine == IMAGE_FILE_MACHINE_I386 || optionalMagic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||
DbgPrint("LdrLoadKernelImage: hard error: 32-bit PE images are not supported (machine=0x%X magic=0x%X)\n",
|
||||
machine, optionalMagic);
|
||||
@@ -960,7 +609,7 @@ static KSTATUS KiLdrLoadKernelImageFromBuffer(const void* buffer, size_t size, u
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
PE_HEADER* pe = (PE_HEADER *)(bytes + peOffset);
|
||||
PE_HEADER* pe = (PE_HEADER*)(bytes + peOffset);
|
||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||
size_t imageSize = 0;
|
||||
KSTATUS sizeStatus = KiComputeRequiredImageSize(pe, size, &imageSize);
|
||||
@@ -981,10 +630,10 @@ static KSTATUS KiLdrLoadKernelImageFromBuffer(const void* buffer, size_t size, u
|
||||
memset(mem, 0, imageSize);
|
||||
|
||||
memcpy(mem, buffer, opt->SizeOfHeaders);
|
||||
SECTION_HEADER* sec = (SECTION_HEADER *)((uint8_t *)&pe->OptionalHeader + pe->SizeOfOptionalHeader);
|
||||
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;
|
||||
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;
|
||||
|
||||
Reference in new issue
Block a user