// Userspace PE module registry and dependency loader. Kernel process objects know only VADs. #include #include #include #include #include #include #include #include "module_loader_internal.h" #define NTL_MODULE_CONTEXT_MAGIC 0x4D4F444Cu #define NTL_MODULE_PAGE_SIZE 4096u #define NTL_LOADER_DATA_MAGIC 0x30444C4Eu #define NTL_LOADER_DATA_VERSION 2u typedef struct NTL_LOADER_IMAGE_RECORD { uintptr_t Base; size_t Size; uintptr_t EntryPoint; char Name[NTL_MODULE_NAME_MAX]; } NTL_LOADER_IMAGE_RECORD; typedef struct NTL_LOADER_DATA { uint32_t Magic; uint32_t Version; uint32_t Size; uint32_t ImageCount; uint32_t CurrentDirectoryOffset; uint32_t CurrentDirectoryLength; uint32_t Reserved[2]; NTL_LOADER_IMAGE_RECORD Images[]; } NTL_LOADER_DATA; typedef struct NTL_MODULE_ENTRY { struct NTL_MODULE_ENTRY* Next; uintptr_t Base; size_t Size; uintptr_t EntryPoint; uint32_t PublicRefCount; uint32_t ImportPinned; uint32_t Initial; char Name[NTL_MODULE_NAME_MAX]; } NTL_MODULE_ENTRY; struct NTL_MODULE_CONTEXT { uint32_t Magic; SYS_HANDLE ProcessHandle; SYS_HANDLE ModuleRootHandle; SYS_STRING_VIEW ModuleDirectory; NTL_MODULE_NAME_RESOLVER Resolver; void* ResolverContext; NTL_MODULE_ENTRY* Head; uint32_t Count; uint32_t OwnsStorage; }; static NTL_MODULE_CONTEXT gCurrentContext; static volatile long gCurrentContextState; static volatile long gCurrentContextBusy; // Converts one ASCII character to lowercase for module-name comparisons. static char NtlpLower(char c) { return (c >= 'A' && c <= 'Z') ? (char)(c - 'A' + 'a') : c; } // Compares one string view with a module name without case sensitivity. static int NtlpViewEqualsNameI(SYS_STRING_VIEW view, const char* name) { if (!name) return 0; size_t n = strlen(name); if (view.Length != n) return 0; for (size_t i = 0; i < n; ++i) { if (NtlpLower(view.Data[i]) != NtlpLower(name[i])) return 0; } return 1; } // Allocates page-backed userspace metadata and clears the complete allocation. static KSTATUS NtlpAllocZero(size_t bytes, void** out) { if (!bytes || !out) return NTLSTATUS_INVALID_PARAMETER; *out = 0; if (bytes > SIZE_MAX - (NTL_MODULE_PAGE_SIZE - 1u)) return NTLSTATUS_NO_MEMORY; size_t pages = (bytes + NTL_MODULE_PAGE_SIZE - 1u) / NTL_MODULE_PAGE_SIZE; SYS_ALLOC_VM_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Protect = SYS_VM_READWRITE; request.Flags = SYS_VM_FLAG_NONE; request.PageCount = pages; request.BaseOut = out; KSTATUS status = SysAllocVm(&request); if (KSUCCESS(status)) memset(*out, 0, pages * NTL_MODULE_PAGE_SIZE); return status; } // Releases page-backed userspace metadata. static void NtlpFree(void* p) { if (p) (void)SysFreeVm(p); } // Copies and validates one basename-only module identity. static KSTATUS NtlpCopyName(SYS_STRING_VIEW name, char out[NTL_MODULE_NAME_MAX]) { if (!SysSvIsValid(name) || name.Length == 0 || name.Length >= NTL_MODULE_NAME_MAX || SysSvContainsByte(name, '\0')) return NTLSTATUS_INVALID_PARAMETER; for (size_t i = 0; i < name.Length; ++i) { if (name.Data[i] == '/' || name.Data[i] == '\\') return NTLSTATUS_INVALID_PARAMETER; } memcpy(out, name.Data, name.Length); out[name.Length] = '\0'; return NTLSTATUS_SUCCESS; } // Returns the final native path component without modifying the source view. static SYS_STRING_VIEW NtlpBasename(SYS_STRING_VIEW path) { if (!SysSvIsValid(path)) return SysSvFromParts((const char*)0, 0); size_t start = 0; for (size_t i = 0; i < path.Length; ++i) { if (path.Data[i] == '/') start = i + 1u; } return SysSvFromParts(path.Data + start, path.Length - start); } // Finds one process-local module by canonical case-insensitive name. static NTL_MODULE_ENTRY* NtlpFindName(NTL_MODULE_CONTEXT* context, SYS_STRING_VIEW name) { for (NTL_MODULE_ENTRY* entry = context ? context->Head : 0; entry; entry = entry->Next) { if (NtlpViewEqualsNameI(name, entry->Name)) return entry; } return 0; } // Resolves one opaque userspace module handle against the live registry. static NTL_MODULE_ENTRY* NtlpFindHandle(NTL_MODULE_CONTEXT* context, NTL_MODULE_HANDLE handle) { if (!context || handle == NTL_MODULE_HANDLE_INVALID) return 0; NTL_MODULE_ENTRY* wanted = (NTL_MODULE_ENTRY*)(uintptr_t)handle; for (NTL_MODULE_ENTRY* entry = context->Head; entry; entry = entry->Next) { if (entry == wanted) return entry; } return 0; } // Copies one private module entry into the public userspace information shape. static void NtlpFillInfo(const NTL_MODULE_ENTRY* entry, NTL_MODULE_INFO* info) { if (!entry || !info) return; memset(info, 0, sizeof(*info)); info->Base = entry->Base; info->Size = entry->Size; info->EntryPoint = entry->EntryPoint; info->PublicRefCount = entry->PublicRefCount; info->ImportPinned = entry->ImportPinned; strncpy(info->Name, entry->Name, sizeof(info->Name) - 1u); } // Preserves an import name when the caller already uses canonical module names. KSTATUS NtlResolveModuleNameIdentity(void* context, SYS_STRING_VIEW requestedName, SYS_STRING_VIEW* canonicalNameOut) { (void)context; if (!canonicalNameOut || !SysSvIsValid(requestedName) || requestedName.Length == 0) return NTLSTATUS_INVALID_PARAMETER; *canonicalNameOut = requestedName; return NTLSTATUS_SUCCESS; } // Resolves installed personality compatibility aliases for generic launchers. KSTATUS NtlResolveSystemModuleName(void* context, SYS_STRING_VIEW requestedName, SYS_STRING_VIEW* canonicalNameOut) { (void)context; if (!canonicalNameOut || !SysSvIsValid(requestedName) || requestedName.Length == 0) return NTLSTATUS_INVALID_PARAMETER; #define NTL_SYSTEM_ALIAS(name, target) do { \ if (NtlpViewEqualsNameI(requestedName, name)) { \ *canonicalNameOut = SYS_SV_LITERAL(target); \ return NTLSTATUS_SUCCESS; \ } \ } while (0) NTL_SYSTEM_ALIAS("kernel32.dll", "win32.dll"); NTL_SYSTEM_ALIAS("user32.dll", "win32.dll"); NTL_SYSTEM_ALIAS("gdi32.dll", "win32.dll"); NTL_SYSTEM_ALIAS("ole32.dll", "win32.dll"); NTL_SYSTEM_ALIAS("winmm.dll", "win32.dll"); NTL_SYSTEM_ALIAS("msvcrt.dll", "posix.dll"); #undef NTL_SYSTEM_ALIAS *canonicalNameOut = requestedName; return NTLSTATUS_SUCCESS; } // Applies caller-owned import policy and validates the resulting module basename. static KSTATUS NtlpCanonicalName(NTL_MODULE_CONTEXT* context, SYS_STRING_VIEW requested, SYS_STRING_VIEW* canonicalOut) { if (!context || !canonicalOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = context->Resolver ? context->Resolver(context->ResolverContext, requested, canonicalOut) : NtlResolveModuleNameIdentity(0, requested, canonicalOut); if (!KSUCCESS(status)) return status; char scratch[NTL_MODULE_NAME_MAX]; return NtlpCopyName(*canonicalOut, scratch); } // Builds the capability-relative dependency path beneath the configured module directory. static KSTATUS NtlpBuildModulePath(NTL_MODULE_CONTEXT* context, SYS_STRING_VIEW canonical, char out[SYS_PATH_MAX]) { if (!context || !out) return NTLSTATUS_INVALID_PARAMETER; char name[NTL_MODULE_NAME_MAX]; KSTATUS status = NtlpCopyName(canonical, name); if (!KSUCCESS(status)) return status; size_t dirLength = context->ModuleDirectory.Length; if (!SysSvIsValid(context->ModuleDirectory) || SysSvContainsByte(context->ModuleDirectory, '\0')) return NTLSTATUS_INVALID_PARAMETER; size_t total = dirLength + (dirLength ? 1u : 0u) + canonical.Length; if (total + 1u > SYS_PATH_MAX) return NTLSTATUS_BUFFER_TOO_SMALL; if (dirLength) memcpy(out, context->ModuleDirectory.Data, dirLength); size_t pos = dirLength; if (dirLength) out[pos++] = '/'; memcpy(out + pos, canonical.Data, canonical.Length); out[total] = '\0'; return NTLSTATUS_SUCCESS; } // Replaces the final component of an explicit module path with its canonical policy name. static KSTATUS NtlpBuildCanonicalPath(SYS_STRING_VIEW path, SYS_STRING_VIEW canonical, char out[SYS_PATH_MAX], SYS_STRING_VIEW* pathOut) { if (!pathOut || !out || !SysSvIsValid(path) || path.Length == 0) return NTLSTATUS_INVALID_PARAMETER; char name[NTL_MODULE_NAME_MAX]; KSTATUS status = NtlpCopyName(canonical, name); if (!KSUCCESS(status)) return status; SYS_STRING_VIEW base = NtlpBasename(path); if (!base.Data || base.Length == 0) return NTLSTATUS_INVALID_PARAMETER; size_t prefix = (size_t)(base.Data - path.Data); if (prefix > path.Length || canonical.Length > SYS_PATH_MAX - 1u - prefix) return NTLSTATUS_BUFFER_TOO_SMALL; memcpy(out, path.Data, prefix); memcpy(out + prefix, canonical.Data, canonical.Length); size_t length = prefix + canonical.Length; out[length] = '\0'; *pathOut = SysSvFromParts(out, length); return NTLSTATUS_SUCCESS; } // Opens one canonical dependency through the caller-supplied module-root capability. static KSTATUS NtlpOpenDependency(NTL_MODULE_CONTEXT* context, SYS_STRING_VIEW canonical, SYS_HANDLE* fileOut) { if (!context || context->ModuleRootHandle == SYS_HANDLE_INVALID || !fileOut) return NTLSTATUS_INVALID_PARAMETER; *fileOut = SYS_HANDLE_INVALID; char path[SYS_PATH_MAX]; KSTATUS status = NtlpBuildModulePath(context, canonical, path); if (!KSUCCESS(status)) return status; SYS_OPEN_FILE_AT_REQUEST open = SysMakeOpenFileAtRequest(context->ModuleRootHandle, SysSvFromCstr(path), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, fileOut); return SysOpenFileAt(&open); } // Publishes a mapped image before import binding so cyclic dependencies can resolve it. static KSTATUS NtlpPreparedModule(void* opaque, const NTL_PE_IMAGE* image) { NTL_MODULE_ENTRY* entry = (NTL_MODULE_ENTRY*)opaque; if (!entry || !image || !image->Base || !image->Size) return NTLSTATUS_INVALID_PARAMETER; entry->Base = image->Base; entry->Size = image->Size; entry->EntryPoint = image->EntryPoint; return NTLSTATUS_SUCCESS; } // Resolves one imported symbol through the recursive userspace dependency registry. static KSTATUS NtlpResolveImport(void* opaque, SYS_STRING_VIEW moduleName, SYS_STRING_VIEW symbolName, uintptr_t* addressOut); // Loads or reuses one dependency and import-pins it for the process lifetime. static KSTATUS NtlpLoadDependency(NTL_MODULE_CONTEXT* context, SYS_STRING_VIEW requested, NTL_MODULE_ENTRY** entryOut) { if (!context || !entryOut) return NTLSTATUS_INVALID_PARAMETER; *entryOut = 0; SYS_STRING_VIEW canonical; KSTATUS status = NtlpCanonicalName(context, requested, &canonical); if (!KSUCCESS(status)) return status; NTL_MODULE_ENTRY* existing = NtlpFindName(context, canonical); if (existing) { existing->ImportPinned = 1u; *entryOut = existing; return NTLSTATUS_SUCCESS; } NTL_MODULE_ENTRY* entry = 0; status = NtlpAllocZero(sizeof(*entry), (void**)&entry); if (!KSUCCESS(status)) return status; status = NtlpCopyName(canonical, entry->Name); if (!KSUCCESS(status)) { NtlpFree(entry); return status; } entry->ImportPinned = 1u; entry->Next = context->Head; context->Head = entry; context->Count++; SYS_HANDLE file = SYS_HANDLE_INVALID; status = NtlpOpenDependency(context, canonical, &file); if (KSUCCESS(status)) { NTL_PE_IMAGE image; memset(&image, 0, sizeof(image)); NTL_PE_LOAD_REQUEST load; memset(&load, 0, sizeof(load)); load.Size = sizeof(load); load.ProcessHandle = context->ProcessHandle; load.FileHandle = file; load.Flags = NTL_PE_LOAD_FLAG_NONE; load.ResolveImport = NtlpResolveImport; load.ResolveContext = context; load.Prepared = NtlpPreparedModule; load.PreparedContext = entry; load.ImageOut = ℑ status = NtlLoadPeImage(&load); (void)SysCloseHandle(file); } if (!KSUCCESS(status)) { NTL_MODULE_ENTRY** link = &context->Head; while (*link && *link != entry) link = &(*link)->Next; if (*link == entry) *link = entry->Next; if (context->Count) context->Count--; NtlpFree(entry); return status; } *entryOut = entry; return NTLSTATUS_SUCCESS; } static KSTATUS NtlpResolveImport(void* opaque, SYS_STRING_VIEW moduleName, SYS_STRING_VIEW symbolName, uintptr_t* addressOut) { if (!opaque || !addressOut) return NTLSTATUS_INVALID_PARAMETER; *addressOut = 0; NTL_MODULE_ENTRY* entry = 0; KSTATUS status = NtlpLoadDependency((NTL_MODULE_CONTEXT*)opaque, moduleName, &entry); if (!KSUCCESS(status)) return status; if (!entry->Base) return NTLSTATUS_WOULD_BLOCK; return NtlGetPeExport(entry->Base, symbolName, addressOut); } // Creates loader state for a child process or seeds the current process from PEB loader data. KSTATUS NtlpCreateModuleContext(SYS_HANDLE processHandle, SYS_HANDLE moduleRootHandle, SYS_STRING_VIEW moduleDirectory, NTL_MODULE_NAME_RESOLVER resolver, void* resolverContext, int seedCurrentProcess, NTL_MODULE_CONTEXT** contextOut) { if (!contextOut || moduleRootHandle == SYS_HANDLE_INVALID || !SysSvIsValid(moduleDirectory) || SysSvContainsByte(moduleDirectory, '\0')) return NTLSTATUS_INVALID_PARAMETER; *contextOut = 0; NTL_MODULE_CONTEXT* context = 0; KSTATUS status = NtlpAllocZero(sizeof(*context), (void**)&context); if (!KSUCCESS(status)) return status; context->Magic = NTL_MODULE_CONTEXT_MAGIC; context->ProcessHandle = processHandle; context->ModuleRootHandle = moduleRootHandle; context->ModuleDirectory = moduleDirectory; context->Resolver = resolver; context->ResolverContext = resolverContext; context->OwnsStorage = 1u; if (seedCurrentProcess) { SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION) { NtlpFree(context); return NTLSTATUS_FAILURE; } if (peb->LoaderData) { SYS_VM_REGION_INFO region; memset(®ion, 0, sizeof(region)); status = SysQueryVm((void*)peb->LoaderData, ®ion); if (!KSUCCESS(status) || !region.Base || !region.PageCount) { NtlpFree(context); return NTLSTATUS_FAILURE; } const NTL_LOADER_DATA* data = (const NTL_LOADER_DATA*)peb->LoaderData; if (region.PageCount > SIZE_MAX / NTL_MODULE_PAGE_SIZE) { NtlpFree(context); return NTLSTATUS_FAILURE; } size_t regionBytes = region.PageCount * NTL_MODULE_PAGE_SIZE; uintptr_t regionBase = (uintptr_t)region.Base; uintptr_t dataBase = (uintptr_t)data; if (dataBase < regionBase || dataBase - regionBase > regionBytes || regionBytes - (dataBase - regionBase) < sizeof(*data) || data->Magic != NTL_LOADER_DATA_MAGIC || data->Version != NTL_LOADER_DATA_VERSION || data->Size < sizeof(*data)) { NtlpFree(context); return NTLSTATUS_FAILURE; } size_t recordsBytes = (size_t)data->ImageCount * sizeof(NTL_LOADER_IMAGE_RECORD); if (data->ImageCount && recordsBytes / sizeof(NTL_LOADER_IMAGE_RECORD) != data->ImageCount) { NtlpFree(context); return NTLSTATUS_FAILURE; } size_t recordsEnd = sizeof(*data) + recordsBytes; if (recordsEnd < sizeof(*data) || data->Size < recordsEnd || data->Size > regionBytes - (dataBase - regionBase) || data->CurrentDirectoryOffset < recordsEnd || data->CurrentDirectoryLength == 0 || data->CurrentDirectoryOffset > data->Size || data->CurrentDirectoryLength > data->Size - data->CurrentDirectoryOffset || data->CurrentDirectoryLength == data->Size - data->CurrentDirectoryOffset || ((const char*)data)[data->CurrentDirectoryOffset + data->CurrentDirectoryLength] != '\0') { NtlpFree(context); return NTLSTATUS_FAILURE; } for (uint32_t i = 0; i < data->ImageCount; ++i) { const NTL_LOADER_IMAGE_RECORD* image = &data->Images[i]; size_t nameLength = 0; while (nameLength < NTL_MODULE_NAME_MAX && image->Name[nameLength]) nameLength++; if (!image->Base || !image->Size || nameLength == 0 || nameLength >= NTL_MODULE_NAME_MAX) { NtlpDestroyModuleContext(context); return NTLSTATUS_FAILURE; } NTL_MODULE_ENTRY* entry = 0; status = NtlpAllocZero(sizeof(*entry), (void**)&entry); if (!KSUCCESS(status)) { NtlpDestroyModuleContext(context); return status; } entry->Base = image->Base; entry->Size = image->Size; entry->EntryPoint = image->EntryPoint; entry->ImportPinned = 1u; entry->Initial = 1u; memcpy(entry->Name, image->Name, nameLength + 1u); if (NtlpFindName(context, SysSvFromParts(entry->Name, nameLength))) { NtlpFree(entry); NtlpDestroyModuleContext(context); return NTLSTATUS_OBJECT_NAME_COLLISION; } entry->Next = context->Head; context->Head = entry; context->Count++; } } } *contextOut = context; return NTLSTATUS_SUCCESS; } // Releases loader metadata without unmapping images owned by the target process. void NtlpDestroyModuleContext(NTL_MODULE_CONTEXT* context) { if (!context || context->Magic != NTL_MODULE_CONTEXT_MAGIC) return; NTL_MODULE_ENTRY* entry = context->Head; while (entry) { NTL_MODULE_ENTRY* next = entry->Next; NtlpFree(entry); entry = next; } context->Head = 0; context->Magic = 0; if (context->OwnsStorage) NtlpFree(context); } // Materializes the main PE while resolving all startup dependencies through this context. KSTATUS NtlpLoadMainImage(NTL_MODULE_CONTEXT* context, SYS_HANDLE fileHandle, NTL_PE_IMAGE* imageOut) { if (!context || context->Magic != NTL_MODULE_CONTEXT_MAGIC || fileHandle == SYS_HANDLE_INVALID || !imageOut) return NTLSTATUS_INVALID_PARAMETER; NTL_PE_LOAD_REQUEST load; memset(&load, 0, sizeof(load)); load.Size = sizeof(load); load.ProcessHandle = context->ProcessHandle; load.FileHandle = fileHandle; load.Flags = NTL_PE_LOAD_FLAG_NONE; load.ResolveImport = NtlpResolveImport; load.ResolveContext = context; load.ImageOut = imageOut; return NtlLoadPeImage(&load); } // Serializes startup dependency metadata into a read-only child-owned VAD. KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context, SYS_STRING_VIEW currentDirectory, void** loaderDataOut) { if (!context || context->Magic != NTL_MODULE_CONTEXT_MAGIC || !loaderDataOut || context->ProcessHandle == SYS_HANDLE_INVALID || !SysSvIsValid(currentDirectory) || currentDirectory.Length == 0 || currentDirectory.Length > SYS_PATH_MAX || currentDirectory.Data[0] != '/' || SysSvContainsByte(currentDirectory, '\\') || SysSvContainsByte(currentDirectory, '\0')) return NTLSTATUS_INVALID_PARAMETER; *loaderDataOut = 0; size_t recordsBytes = (size_t)context->Count * sizeof(NTL_LOADER_IMAGE_RECORD); if (context->Count && recordsBytes / sizeof(NTL_LOADER_IMAGE_RECORD) != context->Count) return NTLSTATUS_NO_MEMORY; size_t bytes = sizeof(NTL_LOADER_DATA) + recordsBytes; if (bytes < sizeof(NTL_LOADER_DATA) || currentDirectory.Length > SIZE_MAX - bytes - 1u) return NTLSTATUS_NO_MEMORY; size_t currentDirectoryOffset = bytes; bytes += currentDirectory.Length + 1u; if (bytes > UINT32_MAX) return NTLSTATUS_NO_MEMORY; if (bytes > SIZE_MAX - (NTL_MODULE_PAGE_SIZE - 1u)) return NTLSTATUS_NO_MEMORY; size_t pages = (bytes + NTL_MODULE_PAGE_SIZE - 1u) / NTL_MODULE_PAGE_SIZE; void* base = 0; SYS_ALLOC_PROCESS_VM_REQUEST alloc; memset(&alloc, 0, sizeof(alloc)); alloc.Size = sizeof(alloc); alloc.ProcessHandle = context->ProcessHandle; alloc.Protect = SYS_VM_READWRITE; alloc.Flags = SYS_VM_FLAG_NONE; alloc.PageCount = pages; alloc.BaseOut = &base; KSTATUS status = SysAllocProcessVm(&alloc); if (!KSUCCESS(status)) return status; NTL_LOADER_DATA* data = (NTL_LOADER_DATA*)base; memset(data, 0, pages * NTL_MODULE_PAGE_SIZE); data->Magic = NTL_LOADER_DATA_MAGIC; data->Version = NTL_LOADER_DATA_VERSION; data->Size = (uint32_t)bytes; data->ImageCount = context->Count; data->CurrentDirectoryOffset = (uint32_t)currentDirectoryOffset; data->CurrentDirectoryLength = (uint32_t)currentDirectory.Length; uint32_t i = 0; for (NTL_MODULE_ENTRY* entry = context->Head; entry; entry = entry->Next) { NTL_LOADER_IMAGE_RECORD* image = &data->Images[i++]; image->Base = entry->Base; image->Size = entry->Size; image->EntryPoint = entry->EntryPoint; strncpy(image->Name, entry->Name, sizeof(image->Name) - 1u); } char* cwd = (char*)data + currentDirectoryOffset; memcpy(cwd, currentDirectory.Data, currentDirectory.Length); cwd[currentDirectory.Length] = '\0'; status = SysProtectProcessVm(context->ProcessHandle, base, pages, SYS_VM_READ); if (!KSUCCESS(status)) return status; *loaderDataOut = base; return NTLSTATUS_SUCCESS; } // Returns the canonical inherited resource CWD stored in this process's loader data. KSTATUS NtlpGetLoaderCurrentDirectory(SYS_STRING_VIEW* currentDirectoryOut) { if (!currentDirectoryOut) return NTLSTATUS_INVALID_PARAMETER; *currentDirectoryOut = SysSvFromParts((const char*)0, 0); SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION || !peb->LoaderData) return NTLSTATUS_NOT_FOUND; SYS_VM_REGION_INFO region; memset(®ion, 0, sizeof(region)); KSTATUS status = SysQueryVm((void*)peb->LoaderData, ®ion); if (!KSUCCESS(status) || !region.Base || !region.PageCount || region.PageCount > SIZE_MAX / NTL_MODULE_PAGE_SIZE) return NTLSTATUS_FAILURE; size_t regionBytes = region.PageCount * NTL_MODULE_PAGE_SIZE; uintptr_t regionBase = (uintptr_t)region.Base; uintptr_t dataBase = (uintptr_t)peb->LoaderData; if (dataBase < regionBase || dataBase - regionBase > regionBytes || regionBytes - (dataBase - regionBase) < sizeof(NTL_LOADER_DATA)) return NTLSTATUS_FAILURE; const NTL_LOADER_DATA* data = (const NTL_LOADER_DATA*)peb->LoaderData; if (data->Magic != NTL_LOADER_DATA_MAGIC || data->Version != NTL_LOADER_DATA_VERSION || data->Size < sizeof(*data) || data->Size > regionBytes - (dataBase - regionBase) || data->CurrentDirectoryOffset < sizeof(*data) || data->CurrentDirectoryLength == 0 || data->CurrentDirectoryOffset > data->Size || data->CurrentDirectoryLength > data->Size - data->CurrentDirectoryOffset || data->CurrentDirectoryLength == data->Size - data->CurrentDirectoryOffset) return NTLSTATUS_FAILURE; const char* cwd = (const char*)data + data->CurrentDirectoryOffset; SYS_STRING_VIEW view = SysSvFromParts(cwd, data->CurrentDirectoryLength); if (cwd[data->CurrentDirectoryLength] != '\0' || cwd[0] != '/' || SysSvContainsByte(view, '\\') || SysSvContainsByte(view, '\0')) return NTLSTATUS_FAILURE; *currentDirectoryOut = view; return NTLSTATUS_SUCCESS; } // Lazily seeds the current-process registry and refreshes per-call resolution authority. static KSTATUS NtlpEnsureCurrentContext(SYS_HANDLE moduleRoot, SYS_STRING_VIEW moduleDirectory, NTL_MODULE_NAME_RESOLVER resolver, void* resolverContext, NTL_MODULE_CONTEXT** contextOut) { if (!contextOut) return NTLSTATUS_INVALID_PARAMETER; if (_InterlockedCompareExchange(&gCurrentContextState, 1, 0) == 0) { NTL_MODULE_CONTEXT* temporary = 0; KSTATUS status = NtlpCreateModuleContext(SYS_HANDLE_INVALID, moduleRoot, moduleDirectory, resolver, resolverContext, 1, &temporary); if (!KSUCCESS(status)) { _InterlockedExchange(&gCurrentContextState, 0); return status; } gCurrentContext = *temporary; gCurrentContext.OwnsStorage = 0u; temporary->Head = 0; temporary->Magic = 0; NtlpFree(temporary); _InterlockedExchange(&gCurrentContextState, 2); } else { while (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) == 1) SysYield(); if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) return NTLSTATUS_FAILURE; gCurrentContext.ModuleRootHandle = moduleRoot; gCurrentContext.ModuleDirectory = moduleDirectory; gCurrentContext.Resolver = resolver; gCurrentContext.ResolverContext = resolverContext; } *contextOut = &gCurrentContext; return NTLSTATUS_SUCCESS; } // Serializes current-process module mutations because loader metadata is process-global. static KSTATUS NtlpEnterCurrent(void) { while (_InterlockedCompareExchange(&gCurrentContextBusy, 1, 0) != 0) SysYield(); return NTLSTATUS_SUCCESS; } // Releases the current-process loader serialization guard. static void NtlpLeaveCurrent(void) { _InterlockedExchange(&gCurrentContextBusy, 0); } // Loads or references one DLL-like PE in the current process through capability-relative I/O. KSTATUS NtlLoadModuleAt(const NTL_LOAD_MODULE_AT_REQUEST* request) { if (!request || request->Size != sizeof(*request) || request->Flags != NTL_MODULE_LOAD_FLAG_NONE || request->RootHandle == SYS_HANDLE_INVALID || request->ModuleRootHandle == SYS_HANDLE_INVALID || !SysSvIsValid(request->Path) || request->Path.Length == 0 || !request->ModuleHandleOut) return NTLSTATUS_INVALID_PARAMETER; *request->ModuleHandleOut = NTL_MODULE_HANDLE_INVALID; KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; NTL_MODULE_CONTEXT* context = 0; status = NtlpEnsureCurrentContext(request->ModuleRootHandle, request->ModuleDirectory, request->ResolveModuleName, request->ResolveContext, &context); if (!KSUCCESS(status)) { NtlpLeaveCurrent(); return status; } SYS_STRING_VIEW baseName = NtlpBasename(request->Path); SYS_STRING_VIEW canonical = baseName; status = NtlpCanonicalName(context, baseName, &canonical); if (!KSUCCESS(status)) { NtlpLeaveCurrent(); return status; } NTL_MODULE_ENTRY* existing = NtlpFindName(context, canonical); if (existing) { existing->PublicRefCount++; *request->ModuleHandleOut = (NTL_MODULE_HANDLE)(uintptr_t)existing; if (request->InfoOut) NtlpFillInfo(existing, request->InfoOut); NtlpLeaveCurrent(); return NTLSTATUS_SUCCESS; } char canonicalPath[SYS_PATH_MAX]; SYS_STRING_VIEW openPath; status = NtlpBuildCanonicalPath(request->Path, canonical, canonicalPath, &openPath); if (!KSUCCESS(status)) { NtlpLeaveCurrent(); return status; } SYS_HANDLE file = SYS_HANDLE_INVALID; SYS_OPEN_FILE_AT_REQUEST open = SysMakeOpenFileAtRequest(request->RootHandle, openPath, SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, &file); status = SysOpenFileAt(&open); if (!KSUCCESS(status)) { NtlpLeaveCurrent(); return status; } NTL_MODULE_ENTRY* entry = 0; status = NtlpAllocZero(sizeof(*entry), (void**)&entry); if (KSUCCESS(status)) status = NtlpCopyName(canonical, entry->Name); if (!KSUCCESS(status)) { (void)SysCloseHandle(file); NtlpFree(entry); NtlpLeaveCurrent(); return status; } entry->PublicRefCount = 1u; entry->Next = context->Head; context->Head = entry; context->Count++; NTL_PE_IMAGE image; memset(&image, 0, sizeof(image)); NTL_PE_LOAD_REQUEST load; memset(&load, 0, sizeof(load)); load.Size = sizeof(load); load.ProcessHandle = SYS_HANDLE_INVALID; load.FileHandle = file; load.Flags = NTL_PE_LOAD_FLAG_NONE; load.ResolveImport = NtlpResolveImport; load.ResolveContext = context; load.Prepared = NtlpPreparedModule; load.PreparedContext = entry; load.ImageOut = ℑ status = NtlLoadPeImage(&load); (void)SysCloseHandle(file); if (!KSUCCESS(status)) { context->Head = entry->Next; context->Count--; NtlpFree(entry); NtlpLeaveCurrent(); return status; } *request->ModuleHandleOut = (NTL_MODULE_HANDLE)(uintptr_t)entry; if (request->InfoOut) NtlpFillInfo(entry, request->InfoOut); NtlpLeaveCurrent(); return NTLSTATUS_SUCCESS; } // Releases one public module reference and unmaps modules that are no longer pinned. KSTATUS NtlUnloadModule(NTL_MODULE_HANDLE module) { KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } NTL_MODULE_ENTRY* entry = NtlpFindHandle(&gCurrentContext, module); if (!entry || entry->PublicRefCount == 0) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } entry->PublicRefCount--; if (entry->PublicRefCount == 0 && !entry->ImportPinned && !entry->Initial) { NTL_MODULE_ENTRY** link = &gCurrentContext.Head; while (*link && *link != entry) link = &(*link)->Next; if (*link == entry) *link = entry->Next; gCurrentContext.Count--; if (entry->Base) (void)SysFreeVm((void*)entry->Base); NtlpFree(entry); } NtlpLeaveCurrent(); return NTLSTATUS_SUCCESS; } // Queries one live userspace module handle. KSTATUS NtlGetModuleInfo(NTL_MODULE_HANDLE module, NTL_MODULE_INFO* infoOut) { if (!infoOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } NTL_MODULE_ENTRY* entry = NtlpFindHandle(&gCurrentContext, module); if (!entry) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } NtlpFillInfo(entry, infoOut); NtlpLeaveCurrent(); return NTLSTATUS_SUCCESS; } // Resolves one named export from a live userspace module handle. KSTATUS NtlGetModuleExport(NTL_MODULE_HANDLE module, SYS_STRING_VIEW name, uintptr_t* addressOut) { if (!addressOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } NTL_MODULE_ENTRY* entry = NtlpFindHandle(&gCurrentContext, module); if (!entry) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_HANDLE; } status = NtlGetPeExport(entry->Base, name, addressOut); NtlpLeaveCurrent(); return status; } // Snapshots the current process-local userspace module registry. KSTATUS NtlGetModuleList(NTL_MODULE_INFO* modulesOut, size_t maxCount, size_t* countOut) { if (!countOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NtlpEnterCurrent(); if (!KSUCCESS(status)) return status; if (_InterlockedCompareExchange(&gCurrentContextState, 2, 2) != 2) { *countOut = 0; NtlpLeaveCurrent(); return NTLSTATUS_SUCCESS; } *countOut = gCurrentContext.Count; if (gCurrentContext.Count > maxCount) { NtlpLeaveCurrent(); return NTLSTATUS_BUFFER_TOO_SMALL; } if (gCurrentContext.Count && !modulesOut) { NtlpLeaveCurrent(); return NTLSTATUS_INVALID_PARAMETER; } size_t i = 0; for (NTL_MODULE_ENTRY* entry = gCurrentContext.Head; entry; entry = entry->Next) NtlpFillInfo(entry, &modulesOut[i++]); NtlpLeaveCurrent(); return NTLSTATUS_SUCCESS; }