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
@@ -0,0 +1,796 @@
|
||||
// Userspace PE module registry and dependency loader. Kernel process objects know only VADs.
|
||||
|
||||
#include <ntlite/module_loader.h>
|
||||
#include <ntlite/pe_loader.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Internal shared state for userspace PE dependency loading.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ntlite/module_loader.h>
|
||||
#include <ntlite/pe_loader.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
typedef struct NTL_MODULE_CONTEXT NTL_MODULE_CONTEXT;
|
||||
|
||||
// Creates dependency-loader state for one constructing child or the current process.
|
||||
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);
|
||||
// Releases loader metadata without releasing image mappings owned elsewhere.
|
||||
void NtlpDestroyModuleContext(NTL_MODULE_CONTEXT* context);
|
||||
// Materializes the main image while resolving startup dependencies through the context.
|
||||
KSTATUS NtlpLoadMainImage(NTL_MODULE_CONTEXT* context,
|
||||
SYS_HANDLE fileHandle,
|
||||
NTL_PE_IMAGE* imageOut);
|
||||
// Builds the opaque read-only loader-data block consumed later by userspace base services.
|
||||
KSTATUS NtlpBuildLoaderData(NTL_MODULE_CONTEXT* context,
|
||||
SYS_STRING_VIEW currentDirectory,
|
||||
void** loaderDataOut);
|
||||
// Returns the inherited canonical resource CWD stored in the current process loader data.
|
||||
KSTATUS NtlpGetLoaderCurrentDirectory(SYS_STRING_VIEW* currentDirectoryOut);
|
||||
@@ -0,0 +1,499 @@
|
||||
// Native userspace path policy. RSSRV resolves global resource prefixes; kernel I/O sees only capabilities and relative paths.
|
||||
|
||||
#include <ntlite/path.h>
|
||||
#include <ntlite/resource.h>
|
||||
#include <string.h>
|
||||
#include "module_loader_internal.h"
|
||||
|
||||
#define NTL_PATH_COMPONENT_MAX 255u
|
||||
|
||||
typedef struct NTL_RESOLVED_ROOT {
|
||||
SYS_HANDLE Handle;
|
||||
SYS_STRING_VIEW RelativePath;
|
||||
uint32_t Kind;
|
||||
size_t ResolvedLength;
|
||||
} NTL_RESOLVED_ROOT;
|
||||
|
||||
static volatile long gPathState;
|
||||
static volatile long gPathLock;
|
||||
static char gCurrentDirectory[SYS_PATH_MAX + 1u];
|
||||
static size_t gCurrentDirectoryLength;
|
||||
static size_t gCurrentDirectoryRootLength;
|
||||
|
||||
// Acquires the tiny userspace CWD lock without introducing a kernel object.
|
||||
static void NtlpLockPathState(void) {
|
||||
while (_InterlockedCompareExchange(&gPathLock, 1, 0) != 0)
|
||||
(void)SysYield();
|
||||
}
|
||||
|
||||
// Releases the userspace CWD lock.
|
||||
static void NtlpUnlockPathState(void) {
|
||||
_InterlockedExchange(&gPathLock, 0);
|
||||
}
|
||||
|
||||
// Initializes the process-local CWD from opaque loader data, or /SystemRoot for bootstrap init.
|
||||
static KSTATUS NtlpEnsurePathState(void) {
|
||||
if (_InterlockedCompareExchange(&gPathState, 2, 2) == 2)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
|
||||
if (_InterlockedCompareExchange(&gPathState, 1, 0) == 0) {
|
||||
SYS_STRING_VIEW inherited;
|
||||
KSTATUS status = NtlpGetLoaderCurrentDirectory(&inherited);
|
||||
if (!KSUCCESS(status))
|
||||
inherited = SYS_SV_LITERAL("/SystemRoot");
|
||||
if (!SysSvIsValid(inherited) || inherited.Length == 0 || inherited.Length > SYS_PATH_MAX ||
|
||||
inherited.Data[0] != '/' || SysSvContainsByte(inherited, '\\') || SysSvContainsByte(inherited, '\0')) {
|
||||
_InterlockedExchange(&gPathState, 0);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
memcpy(gCurrentDirectory, inherited.Data, inherited.Length);
|
||||
gCurrentDirectory[inherited.Length] = '\0';
|
||||
gCurrentDirectoryLength = inherited.Length;
|
||||
gCurrentDirectoryRootLength = 0;
|
||||
_InterlockedExchange(&gPathState, 2);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
while (_InterlockedCompareExchange(&gPathState, 2, 2) == 1)
|
||||
(void)SysYield();
|
||||
return _InterlockedCompareExchange(&gPathState, 2, 2) == 2
|
||||
? NTLSTATUS_SUCCESS : NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
// Appends one normalized path component while enforcing the owning resource boundary.
|
||||
static KSTATUS NtlpAppendComponent(char* output,
|
||||
size_t outputSize,
|
||||
size_t* lengthInOut,
|
||||
size_t componentStart,
|
||||
const char* component,
|
||||
size_t componentLength,
|
||||
uint32_t clampAtBoundary) {
|
||||
if (!output || !lengthInOut || !component || componentLength == 0 ||
|
||||
componentLength > NTL_PATH_COMPONENT_MAX)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
size_t length = *lengthInOut;
|
||||
if (componentLength == 1 && component[0] == '.')
|
||||
return NTLSTATUS_SUCCESS;
|
||||
if (componentLength == 2 && component[0] == '.' && component[1] == '.') {
|
||||
if (length <= componentStart)
|
||||
return clampAtBoundary ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED;
|
||||
size_t slash = length;
|
||||
while (slash > componentStart && output[slash - 1u] != '/') slash--;
|
||||
if (slash <= componentStart)
|
||||
return clampAtBoundary ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED;
|
||||
length = slash - 1u;
|
||||
*lengthInOut = length;
|
||||
output[length] = '\0';
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (length && output[length - 1u] != '/') {
|
||||
if (length + 1u >= outputSize) return NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
output[length++] = '/';
|
||||
}
|
||||
if (componentLength > outputSize - length - 1u)
|
||||
return NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
memcpy(output + length, component, componentLength);
|
||||
length += componentLength;
|
||||
output[length] = '\0';
|
||||
*lengthInOut = length;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns whether one relative path contains a parent-traversal component.
|
||||
static uint32_t NtlpContainsParentComponent(SYS_STRING_VIEW path) {
|
||||
size_t componentStart = 0;
|
||||
for (size_t i = 0; i <= path.Length; ++i) {
|
||||
if (i != path.Length && path.Data[i] != '/')
|
||||
continue;
|
||||
size_t componentLength = i - componentStart;
|
||||
if (componentLength == 2 && path.Data[componentStart] == '.' &&
|
||||
path.Data[componentStart + 1u] == '.')
|
||||
return 1;
|
||||
componentStart = i + 1u;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Builds the canonical absolute prefix preceding the first parent component.
|
||||
static KSTATUS NtlpBuildAbsoluteParentPrefix(SYS_STRING_VIEW path,
|
||||
char output[SYS_PATH_MAX + 1u],
|
||||
size_t* lengthOut) {
|
||||
if (!output || !lengthOut || !SysSvIsValid(path) || path.Length == 0 || path.Data[0] != '/')
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
size_t length = 1u;
|
||||
output[0] = '/';
|
||||
output[1] = '\0';
|
||||
|
||||
size_t componentStart = 1u;
|
||||
for (size_t i = 1u; i <= path.Length; ++i) {
|
||||
if (i != path.Length && path.Data[i] != '/')
|
||||
continue;
|
||||
size_t componentLength = i - componentStart;
|
||||
if (componentLength == 1u && path.Data[componentStart] == '.') {
|
||||
componentStart = i + 1u;
|
||||
continue;
|
||||
}
|
||||
if (componentLength == 2u && path.Data[componentStart] == '.' &&
|
||||
path.Data[componentStart + 1u] == '.') {
|
||||
if (length <= 1u) return NTLSTATUS_ACCESS_DENIED;
|
||||
*lengthOut = length;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
if (componentLength != 0) {
|
||||
KSTATUS status = NtlpAppendComponent(output, SYS_PATH_MAX + 1u, &length, 1u,
|
||||
path.Data + componentStart, componentLength, 0);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
}
|
||||
componentStart = i + 1u;
|
||||
}
|
||||
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Resolves one canonical path solely to discover the owning RSSRV resource-prefix boundary.
|
||||
static KSTATUS NtlpResolveResourceBoundary(const char* canonical,
|
||||
size_t canonicalLength,
|
||||
size_t* boundaryOut) {
|
||||
if (!canonical || canonicalLength < 2u || !boundaryOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*boundaryOut = 0;
|
||||
|
||||
SYS_HANDLE session = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = NtlResourceConnect(&session);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_HANDLE root = SYS_HANDLE_INVALID;
|
||||
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
||||
uint32_t resolvedLength = 0;
|
||||
status = NtlResourceResolve(session, SysSvFromParts(canonical, canonicalLength),
|
||||
NTL_RESOURCE_FLAG_NONE, &root, &kind, &resolvedLength);
|
||||
(void)NtlResourceClose(session);
|
||||
if (root != SYS_HANDLE_INVALID) (void)SysCloseHandle(root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (kind == NTL_RESOURCE_KIND_INVALID || resolvedLength < 2u || resolvedLength > canonicalLength ||
|
||||
(resolvedLength != canonicalLength && canonical[resolvedLength] != '/'))
|
||||
return NTLSTATUS_FAILURE;
|
||||
|
||||
*boundaryOut = resolvedLength;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Normalizes one absolute or CWD-relative native path into a canonical resource path.
|
||||
static KSTATUS NtlpNormalizePath(SYS_STRING_VIEW path, char output[SYS_PATH_MAX + 1u], size_t* lengthOut) {
|
||||
if (!output || !lengthOut || !SysSvIsValid(path) || SysSvContainsByte(path, '\0') ||
|
||||
SysSvContainsByte(path, '\\'))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
KSTATUS status = NtlpEnsurePathState();
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
size_t outputLength = 0;
|
||||
size_t rootBoundary = 1u;
|
||||
size_t sourceOffset = 0;
|
||||
uint32_t absolutePath = path.Length && path.Data[0] == '/';
|
||||
if (absolutePath) {
|
||||
output[0] = '/';
|
||||
output[1] = '\0';
|
||||
outputLength = 1u;
|
||||
sourceOffset = 1u;
|
||||
|
||||
if (NtlpContainsParentComponent(path)) {
|
||||
char prefix[SYS_PATH_MAX + 1u];
|
||||
size_t prefixLength = 0;
|
||||
status = NtlpBuildAbsoluteParentPrefix(path, prefix, &prefixLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = NtlpResolveResourceBoundary(prefix, prefixLength, &rootBoundary);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
}
|
||||
} else {
|
||||
NtlpLockPathState();
|
||||
if (gCurrentDirectoryLength > SYS_PATH_MAX ||
|
||||
gCurrentDirectoryRootLength > gCurrentDirectoryLength) {
|
||||
NtlpUnlockPathState();
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
memcpy(output, gCurrentDirectory, gCurrentDirectoryLength + 1u);
|
||||
outputLength = gCurrentDirectoryLength;
|
||||
rootBoundary = gCurrentDirectoryRootLength;
|
||||
NtlpUnlockPathState();
|
||||
sourceOffset = 0;
|
||||
|
||||
if (rootBoundary == 0 && NtlpContainsParentComponent(path)) {
|
||||
status = NtlpResolveResourceBoundary(output, outputLength, &rootBoundary);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NtlpLockPathState();
|
||||
if (gCurrentDirectoryLength == outputLength &&
|
||||
memcmp(gCurrentDirectory, output, outputLength + 1u) == 0)
|
||||
gCurrentDirectoryRootLength = rootBoundary;
|
||||
NtlpUnlockPathState();
|
||||
}
|
||||
}
|
||||
|
||||
size_t componentStart = sourceOffset;
|
||||
for (size_t i = sourceOffset; i <= path.Length; ++i) {
|
||||
if (i != path.Length && path.Data[i] != '/')
|
||||
continue;
|
||||
size_t componentLength = i - componentStart;
|
||||
if (componentLength != 0) {
|
||||
status = NtlpAppendComponent(output, SYS_PATH_MAX + 1u, &outputLength,
|
||||
rootBoundary, path.Data + componentStart, componentLength,
|
||||
absolutePath ? 0u : 1u);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
}
|
||||
componentStart = i + 1u;
|
||||
}
|
||||
|
||||
while (outputLength > 1u && output[outputLength - 1u] == '/') outputLength--;
|
||||
output[outputLength] = '\0';
|
||||
if (outputLength < 2u || output[0] != '/') return NTLSTATUS_INVALID_PARAMETER;
|
||||
*lengthOut = outputLength;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Resolves a canonical resource path to one volume/directory capability and a relative tail.
|
||||
static KSTATUS NtlpResolveRoot(const char* canonical,
|
||||
size_t canonicalLength,
|
||||
uint32_t modify,
|
||||
NTL_RESOLVED_ROOT* rootOut) {
|
||||
if (!canonical || canonicalLength < 2u || !rootOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
memset(rootOut, 0, sizeof(*rootOut));
|
||||
|
||||
SYS_HANDLE session = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = NtlResourceConnect(&session);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_HANDLE root = SYS_HANDLE_INVALID;
|
||||
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
||||
uint32_t resolvedLength = 0;
|
||||
status = NtlResourceResolve(session,
|
||||
SysSvFromParts(canonical, canonicalLength),
|
||||
modify ? NTL_RESOURCE_RESOLVE_FLAG_MODIFY : NTL_RESOURCE_FLAG_NONE,
|
||||
&root, &kind, &resolvedLength);
|
||||
(void)NtlResourceClose(session);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
size_t relativeOffset = resolvedLength;
|
||||
if (relativeOffset < canonicalLength) relativeOffset++;
|
||||
if (relativeOffset > canonicalLength) {
|
||||
(void)SysCloseHandle(root);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
rootOut->Handle = root;
|
||||
rootOut->Kind = kind;
|
||||
rootOut->ResolvedLength = resolvedLength;
|
||||
rootOut->RelativePath = SysSvFromParts(canonical + relativeOffset, canonicalLength - relativeOffset);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns one canonical absolute resource path.
|
||||
KSTATUS NtlRealPath(SYS_STRING_VIEW path,
|
||||
char* buffer,
|
||||
size_t bufferSize,
|
||||
size_t* requiredSizeOut) {
|
||||
if (requiredSizeOut) *requiredSizeOut = 0;
|
||||
if (!buffer && bufferSize) return NTLSTATUS_INVALID_PARAMETER;
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
size_t required = length + 1u;
|
||||
if (requiredSizeOut) *requiredSizeOut = required;
|
||||
if (!buffer) return NTLSTATUS_SUCCESS;
|
||||
if (bufferSize < required) return NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
memcpy(buffer, canonical, required);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns the process-local userspace current directory.
|
||||
KSTATUS NtlGetCurrentDirectory(char* buffer, size_t bufferSize, size_t* requiredSizeOut) {
|
||||
if (requiredSizeOut) *requiredSizeOut = 0;
|
||||
if (!buffer && bufferSize) return NTLSTATUS_INVALID_PARAMETER;
|
||||
KSTATUS status = NtlpEnsurePathState();
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NtlpLockPathState();
|
||||
size_t required = gCurrentDirectoryLength + 1u;
|
||||
if (requiredSizeOut) *requiredSizeOut = required;
|
||||
if (buffer && bufferSize >= required)
|
||||
memcpy(buffer, gCurrentDirectory, required);
|
||||
NtlpUnlockPathState();
|
||||
if (!buffer) return NTLSTATUS_SUCCESS;
|
||||
return bufferSize >= required ? NTLSTATUS_SUCCESS : NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
// Validates and installs one process-local userspace current directory.
|
||||
KSTATUS NtlSetCurrentDirectory(SYS_STRING_VIEW path) {
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NTL_RESOLVED_ROOT root;
|
||||
status = NtlpResolveRoot(canonical, length, 0, &root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_HANDLE directory = SYS_HANDLE_INVALID;
|
||||
SYS_OPEN_DIR_AT_REQUEST open = SysMakeOpenDirAtRequest(root.Handle, root.RelativePath,
|
||||
SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN,
|
||||
SYS_DIR_FLAG_NONE, &directory);
|
||||
status = SysOpenDirAt(&open);
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
(void)SysCloseHandle(directory);
|
||||
|
||||
NtlpLockPathState();
|
||||
memcpy(gCurrentDirectory, canonical, length + 1u);
|
||||
gCurrentDirectoryLength = length;
|
||||
gCurrentDirectoryRootLength = root.ResolvedLength;
|
||||
NtlpUnlockPathState();
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Opens or creates one file through RSSRV resource resolution and capability-relative kernel I/O.
|
||||
KSTATUS NtlOpenFile(SYS_STRING_VIEW path,
|
||||
uint32_t access,
|
||||
uint32_t disposition,
|
||||
uint32_t flags,
|
||||
SYS_HANDLE* handleOut) {
|
||||
if (!handleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*handleOut = SYS_HANDLE_INVALID;
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
uint32_t modify = ((access & (SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_APPEND | SYS_FILE_ACCESS_LOCK)) != 0 ||
|
||||
disposition != SYS_FILE_OPEN_EXISTING);
|
||||
NTL_RESOLVED_ROOT root;
|
||||
status = NtlpResolveRoot(canonical, length, modify, &root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (root.RelativePath.Length == 0) {
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SYS_OPEN_FILE_AT_REQUEST request = SysMakeOpenFileAtRequest(root.Handle, root.RelativePath,
|
||||
access, disposition, flags, handleOut);
|
||||
status = SysOpenFileAt(&request);
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Opens one directory through RSSRV resource resolution and capability-relative kernel I/O.
|
||||
KSTATUS NtlOpenDir(SYS_STRING_VIEW path, uint32_t access, uint32_t flags, SYS_HANDLE* handleOut) {
|
||||
if (!handleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*handleOut = SYS_HANDLE_INVALID;
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NTL_RESOLVED_ROOT root;
|
||||
status = NtlpResolveRoot(canonical, length, (access & SYS_DIR_ACCESS_MODIFY) != 0, &root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
SYS_OPEN_DIR_AT_REQUEST request = SysMakeOpenDirAtRequest(root.Handle, root.RelativePath,
|
||||
access, flags, handleOut);
|
||||
status = SysOpenDirAt(&request);
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Queries file metadata through one resolved root capability.
|
||||
KSTATUS NtlStatPath(SYS_STRING_VIEW path, SYS_FILE_STAT* statOut) {
|
||||
if (!statOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
NTL_RESOLVED_ROOT root;
|
||||
status = NtlpResolveRoot(canonical, length, 0, &root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
SYS_STAT_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath, statOut };
|
||||
status = SysStatAt(&request);
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Deletes one file through one resolved writable root capability.
|
||||
KSTATUS NtlDeleteFile(SYS_STRING_VIEW path) {
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
NTL_RESOLVED_ROOT root;
|
||||
status = NtlpResolveRoot(canonical, length, 1, &root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
SYS_PATH_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath };
|
||||
status = root.RelativePath.Length ? SysDeleteAt(&request) : NTLSTATUS_INVALID_PARAMETER;
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Renames one file using independently resolved writable roots.
|
||||
KSTATUS NtlRenameFile(SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath) {
|
||||
char oldCanonical[SYS_PATH_MAX + 1u];
|
||||
char newCanonical[SYS_PATH_MAX + 1u];
|
||||
size_t oldLength = 0, newLength = 0;
|
||||
KSTATUS status = NtlpNormalizePath(oldPath, oldCanonical, &oldLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = NtlpNormalizePath(newPath, newCanonical, &newLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NTL_RESOLVED_ROOT oldRoot, newRoot;
|
||||
status = NtlpResolveRoot(oldCanonical, oldLength, 1, &oldRoot);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = NtlpResolveRoot(newCanonical, newLength, 1, &newRoot);
|
||||
if (!KSUCCESS(status)) { (void)SysCloseHandle(oldRoot.Handle); return status; }
|
||||
if (oldRoot.RelativePath.Length == 0 || newRoot.RelativePath.Length == 0) {
|
||||
(void)SysCloseHandle(newRoot.Handle);
|
||||
(void)SysCloseHandle(oldRoot.Handle);
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SYS_RENAME_AT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.OldRootHandle = oldRoot.Handle;
|
||||
request.OldPath = oldRoot.RelativePath;
|
||||
request.NewRootHandle = newRoot.Handle;
|
||||
request.NewPath = newRoot.RelativePath;
|
||||
status = SysRenameAt(&request);
|
||||
(void)SysCloseHandle(newRoot.Handle);
|
||||
(void)SysCloseHandle(oldRoot.Handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Creates one directory through one resolved writable root capability.
|
||||
KSTATUS NtlCreateDirectory(SYS_STRING_VIEW path) {
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
NTL_RESOLVED_ROOT root;
|
||||
status = NtlpResolveRoot(canonical, length, 1, &root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
SYS_PATH_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath };
|
||||
status = root.RelativePath.Length ? SysCreateDirAt(&request) : NTLSTATUS_INVALID_PARAMETER;
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Removes one directory through one resolved writable root capability.
|
||||
KSTATUS NtlRemoveDirectory(SYS_STRING_VIEW path) {
|
||||
char canonical[SYS_PATH_MAX + 1u];
|
||||
size_t length = 0;
|
||||
KSTATUS status = NtlpNormalizePath(path, canonical, &length);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
NTL_RESOLVED_ROOT root;
|
||||
status = NtlpResolveRoot(canonical, length, 1, &root);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
SYS_PATH_AT_REQUEST request = { sizeof(request), root.Handle, root.RelativePath };
|
||||
status = root.RelativePath.Length ? SysRemoveDirAt(&request) : NTLSTATUS_INVALID_PARAMETER;
|
||||
(void)SysCloseHandle(root.Handle);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
// Userspace PE32+ image loader. The kernel sees only generic process memory and execution state.
|
||||
|
||||
#include <ntlite/pe_loader.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PE_DOS_MAGIC 0x5A4Du
|
||||
#define PE_SIGNATURE 0x00004550u
|
||||
#define PE_MACHINE_AMD64 0x8664u
|
||||
#define PE_FILE_RELOCS_STRIPPED 0x0001u
|
||||
#define PE_OPTIONAL_MAGIC_PE32_PLUS 0x020Bu
|
||||
#define PE_OPTIONAL_MIN_BYTES 112u
|
||||
#define PE_SECTION_BYTES 40u
|
||||
#define PE_DIRECTORY_IMPORT 1u
|
||||
#define PE_DIRECTORY_RELOC 5u
|
||||
#define PE_DIRECTORY_DELAY_IMPORT 13u
|
||||
#define PE_ORDINAL_FLAG64 0x8000000000000000ull
|
||||
#define PE_RELOC_DIR64 10u
|
||||
#define PE_PAGE_SIZE 4096u
|
||||
#define PE_SCN_MEM_EXECUTE 0x20000000u
|
||||
#define PE_SCN_MEM_READ 0x40000000u
|
||||
#define PE_SCN_MEM_WRITE 0x80000000u
|
||||
|
||||
typedef struct PE_VIEW {
|
||||
const uint8_t* File;
|
||||
size_t FileSize;
|
||||
size_t OptionalOffset;
|
||||
size_t SectionOffset;
|
||||
uint16_t SectionCount;
|
||||
uint16_t OptionalSize;
|
||||
uint16_t FileCharacteristics;
|
||||
uint32_t DirectoryCount;
|
||||
uint64_t ImageBase;
|
||||
uint32_t ImageSize;
|
||||
uint32_t HeaderSize;
|
||||
uint32_t EntryRva;
|
||||
} PE_VIEW;
|
||||
|
||||
static bool PeRange(size_t offset, size_t length, size_t size) {
|
||||
return offset <= size && length <= size - offset;
|
||||
}
|
||||
|
||||
static uint16_t PeU16(const void* p) { uint16_t v; memcpy(&v, p, sizeof(v)); return v; }
|
||||
static uint32_t PeU32(const void* p) { uint32_t v; memcpy(&v, p, sizeof(v)); return v; }
|
||||
static uint64_t PeU64(const void* p) { uint64_t v; memcpy(&v, p, sizeof(v)); return v; }
|
||||
static void PeW64(void* p, uint64_t v) { memcpy(p, &v, sizeof(v)); }
|
||||
|
||||
static bool PeImageRange(uint32_t rva, size_t length, size_t imageSize) {
|
||||
return (size_t)rva <= imageSize && length <= imageSize - (size_t)rva;
|
||||
}
|
||||
|
||||
static bool PeImageString(const uint8_t* image, size_t imageSize, uint32_t rva, size_t* lengthOut) {
|
||||
if ((size_t)rva >= imageSize) return false;
|
||||
for (size_t i = rva; i < imageSize; ++i) {
|
||||
if (image[i] == 0) {
|
||||
if (lengthOut) *lengthOut = i - rva;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static KSTATUS PeParse(const void* file, size_t fileSize, PE_VIEW* out) {
|
||||
if (!file || !out || fileSize < 64u) return NTLSTATUS_FAILURE;
|
||||
const uint8_t* b = (const uint8_t*)file;
|
||||
if (PeU16(b) != PE_DOS_MAGIC) return NTLSTATUS_FAILURE;
|
||||
size_t nt = PeU32(b + 0x3cu);
|
||||
if (!PeRange(nt, 24u, fileSize) || PeU32(b + nt) != PE_SIGNATURE) return NTLSTATUS_FAILURE;
|
||||
if (PeU16(b + nt + 4u) != PE_MACHINE_AMD64) return NTLSTATUS_NOT_SUPPORTED;
|
||||
uint16_t sections = PeU16(b + nt + 6u);
|
||||
uint16_t optionalSize = PeU16(b + nt + 20u);
|
||||
uint16_t fileCharacteristics = PeU16(b + nt + 22u);
|
||||
size_t optional = nt + 24u;
|
||||
if (optionalSize < PE_OPTIONAL_MIN_BYTES || !PeRange(optional, optionalSize, fileSize)) return NTLSTATUS_FAILURE;
|
||||
if (PeU16(b + optional) != PE_OPTIONAL_MAGIC_PE32_PLUS) return NTLSTATUS_NOT_SUPPORTED;
|
||||
|
||||
uint64_t imageBase = PeU64(b + optional + 24u);
|
||||
uint32_t imageSize = PeU32(b + optional + 56u);
|
||||
uint32_t headerSize = PeU32(b + optional + 60u);
|
||||
uint32_t entryRva = PeU32(b + optional + 16u);
|
||||
uint32_t directories = PeU32(b + optional + 108u);
|
||||
uint32_t available = (uint32_t)((optionalSize - PE_OPTIONAL_MIN_BYTES) / 8u);
|
||||
if (!imageBase || !imageSize || !headerSize || headerSize > imageSize || entryRva >= imageSize || directories > available)
|
||||
return NTLSTATUS_FAILURE;
|
||||
if (headerSize > fileSize) return NTLSTATUS_FAILURE;
|
||||
if (directories > PE_DIRECTORY_DELAY_IMPORT) {
|
||||
const uint8_t* delay = b + optional + PE_OPTIONAL_MIN_BYTES + PE_DIRECTORY_DELAY_IMPORT * 8u;
|
||||
if (PeU32(delay) || PeU32(delay + 4u)) return NTLSTATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
size_t sectionOffset = optional + optionalSize;
|
||||
size_t sectionBytes = (size_t)sections * PE_SECTION_BYTES;
|
||||
if (sections && sectionBytes / PE_SECTION_BYTES != sections) return NTLSTATUS_FAILURE;
|
||||
if (!PeRange(sectionOffset, sectionBytes, fileSize)) return NTLSTATUS_FAILURE;
|
||||
|
||||
for (uint16_t i = 0; i < sections; ++i) {
|
||||
const uint8_t* sec = b + sectionOffset + (size_t)i * PE_SECTION_BYTES;
|
||||
uint32_t va = PeU32(sec + 12u);
|
||||
uint32_t rawSize = PeU32(sec + 16u);
|
||||
uint32_t raw = PeU32(sec + 20u);
|
||||
uint32_t virtualSize = PeU32(sec + 8u);
|
||||
uint32_t span = virtualSize > rawSize ? virtualSize : rawSize;
|
||||
if ((va & (PE_PAGE_SIZE - 1u)) != 0) return NTLSTATUS_NOT_SUPPORTED;
|
||||
if (!PeImageRange(va, span, imageSize)) return NTLSTATUS_FAILURE;
|
||||
if (rawSize && !PeRange(raw, rawSize, fileSize)) return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->File = b;
|
||||
out->FileSize = fileSize;
|
||||
out->OptionalOffset = optional;
|
||||
out->SectionOffset = sectionOffset;
|
||||
out->SectionCount = sections;
|
||||
out->OptionalSize = optionalSize;
|
||||
out->FileCharacteristics = fileCharacteristics;
|
||||
out->DirectoryCount = directories;
|
||||
out->ImageBase = imageBase;
|
||||
out->ImageSize = imageSize;
|
||||
out->HeaderSize = headerSize;
|
||||
out->EntryRva = entryRva;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static bool PeDirectory(const PE_VIEW* view, uint32_t index, uint32_t* rva, uint32_t* size) {
|
||||
if (!view || index >= view->DirectoryCount) return false;
|
||||
const uint8_t* d = view->File + view->OptionalOffset + PE_OPTIONAL_MIN_BYTES + (size_t)index * 8u;
|
||||
if (rva) *rva = PeU32(d);
|
||||
if (size) *size = PeU32(d + 4u);
|
||||
return true;
|
||||
}
|
||||
|
||||
static KSTATUS PeAllocCurrent(size_t bytes, void** out) {
|
||||
if (!out || !bytes) return NTLSTATUS_INVALID_PARAMETER;
|
||||
size_t pages = (bytes + PE_PAGE_SIZE - 1u) / PE_PAGE_SIZE;
|
||||
SYS_ALLOC_VM_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Size = sizeof(req);
|
||||
req.Protect = SYS_VM_READWRITE;
|
||||
req.Flags = SYS_VM_FLAG_NONE;
|
||||
req.PageCount = pages;
|
||||
req.BaseOut = out;
|
||||
return SysAllocVm(&req);
|
||||
}
|
||||
|
||||
static KSTATUS PeReadFile(SYS_HANDLE file, void** bufferOut, size_t* sizeOut) {
|
||||
if (!bufferOut || !sizeOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*bufferOut = 0; *sizeOut = 0;
|
||||
uint64_t fileSize64 = 0;
|
||||
KSTATUS s = SysGetFileSize(file, &fileSize64);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!fileSize64 || fileSize64 > (uint64_t)SIZE_MAX) return NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
size_t fileSize = (size_t)fileSize64;
|
||||
void* buffer = 0;
|
||||
s = PeAllocCurrent(fileSize, &buffer);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
s = SysSeekFile(file, 0);
|
||||
if (!KSUCCESS(s)) { SysFreeVm(buffer); return s; }
|
||||
size_t got = 0;
|
||||
SYS_FILE_IO_REQUEST read = SysMakeFileIoRequest(file, buffer, fileSize, &got);
|
||||
s = SysReadFile(&read);
|
||||
if (!KSUCCESS(s) || got != fileSize) {
|
||||
SysFreeVm(buffer);
|
||||
return KSUCCESS(s) ? NTLSTATUS_DEVICE_ERROR : s;
|
||||
}
|
||||
*bufferOut = buffer; *sizeOut = fileSize;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS PeRelocate(uint8_t* image, size_t imageSize, const PE_VIEW* view, uintptr_t actualBase) {
|
||||
uintptr_t delta = actualBase - (uintptr_t)view->ImageBase;
|
||||
if (!delta) return NTLSTATUS_SUCCESS;
|
||||
uint32_t rva = 0, size = 0;
|
||||
if (!PeDirectory(view, PE_DIRECTORY_RELOC, &rva, &size) || !rva || !size) {
|
||||
// An image with no base fixups can still be position-independent. Only the
|
||||
// COFF RELOCS_STRIPPED bit says missing relocation data makes rebasing invalid.
|
||||
return (view->FileCharacteristics & PE_FILE_RELOCS_STRIPPED)
|
||||
? NTLSTATUS_NOT_SUPPORTED
|
||||
: NTLSTATUS_SUCCESS;
|
||||
}
|
||||
if (!PeImageRange(rva, size, imageSize)) return NTLSTATUS_FAILURE;
|
||||
size_t offset = rva;
|
||||
size_t end = (size_t)rva + size;
|
||||
while (offset < end) {
|
||||
if (end - offset < 8u) return NTLSTATUS_FAILURE;
|
||||
uint32_t pageRva = PeU32(image + offset);
|
||||
uint32_t blockSize = PeU32(image + offset + 4u);
|
||||
if (blockSize < 8u || blockSize > end - offset || ((blockSize - 8u) & 1u)) return NTLSTATUS_FAILURE;
|
||||
uint32_t count = (blockSize - 8u) / 2u;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
uint16_t e = PeU16(image + offset + 8u + (size_t)i * 2u);
|
||||
uint16_t type = e >> 12;
|
||||
uint16_t pageOffset = e & 0x0fffu;
|
||||
if (type == 0) continue;
|
||||
if (type != PE_RELOC_DIR64) return NTLSTATUS_NOT_SUPPORTED;
|
||||
uint32_t fixRva = pageRva + pageOffset;
|
||||
if (fixRva < pageRva || !PeImageRange(fixRva, 8u, imageSize)) return NTLSTATUS_FAILURE;
|
||||
PeW64(image + fixRva, PeU64(image + fixRva) + delta);
|
||||
}
|
||||
offset += blockSize;
|
||||
}
|
||||
return offset == end ? NTLSTATUS_SUCCESS : NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
static KSTATUS PeBindImports(uint8_t* image, size_t imageSize, const PE_VIEW* view,
|
||||
NTL_PE_IMPORT_RESOLVER resolver, void* context) {
|
||||
uint32_t rva = 0, size = 0;
|
||||
if (!PeDirectory(view, PE_DIRECTORY_IMPORT, &rva, &size) || !rva || !size)
|
||||
return NTLSTATUS_SUCCESS;
|
||||
if (!resolver) return NTLSTATUS_NOT_FOUND;
|
||||
if (!PeImageRange(rva, size, imageSize)) return NTLSTATUS_FAILURE;
|
||||
|
||||
size_t offset = rva;
|
||||
size_t end = (size_t)rva + size;
|
||||
while (offset + 20u <= end) {
|
||||
uint8_t* d = image + offset;
|
||||
uint32_t originalThunk = PeU32(d + 0u);
|
||||
uint32_t nameRva = PeU32(d + 12u);
|
||||
uint32_t firstThunk = PeU32(d + 16u);
|
||||
if (!originalThunk && !nameRva && !firstThunk) return NTLSTATUS_SUCCESS;
|
||||
size_t moduleLen = 0;
|
||||
if (!nameRva || !PeImageString(image, imageSize, nameRva, &moduleLen)) return NTLSTATUS_FAILURE;
|
||||
uint32_t sourceThunk = originalThunk ? originalThunk : firstThunk;
|
||||
if (!sourceThunk || !firstThunk) return NTLSTATUS_FAILURE;
|
||||
|
||||
for (uint32_t index = 0;; ++index) {
|
||||
size_t sourceRva = (size_t)sourceThunk + (size_t)index * 8u;
|
||||
size_t targetRva = (size_t)firstThunk + (size_t)index * 8u;
|
||||
if (sourceRva + 8u > imageSize || targetRva + 8u > imageSize) return NTLSTATUS_FAILURE;
|
||||
uint64_t thunk = PeU64(image + sourceRva);
|
||||
if (!thunk) break;
|
||||
if (thunk & PE_ORDINAL_FLAG64) return NTLSTATUS_NOT_SUPPORTED;
|
||||
uint32_t hintNameRva = (uint32_t)thunk;
|
||||
if (!PeImageRange(hintNameRva, 3u, imageSize)) return NTLSTATUS_FAILURE;
|
||||
size_t symbolLen = 0;
|
||||
if (!PeImageString(image, imageSize, hintNameRva + 2u, &symbolLen)) return NTLSTATUS_FAILURE;
|
||||
uintptr_t address = 0;
|
||||
KSTATUS s = resolver(context,
|
||||
SysSvFromParts((const char*)image + nameRva, moduleLen),
|
||||
SysSvFromParts((const char*)image + hintNameRva + 2u, symbolLen),
|
||||
&address);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!address) return NTLSTATUS_NOT_FOUND;
|
||||
PeW64(image + targetRva, address);
|
||||
}
|
||||
offset += 20u;
|
||||
}
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
static uint32_t PeSectionProtect(uint32_t characteristics) {
|
||||
bool r = (characteristics & PE_SCN_MEM_READ) != 0;
|
||||
bool w = (characteristics & PE_SCN_MEM_WRITE) != 0;
|
||||
bool x = (characteristics & PE_SCN_MEM_EXECUTE) != 0;
|
||||
if (x && w) return SYS_VM_EXECUTE_READWRITE;
|
||||
if (x && r) return SYS_VM_EXECUTE_READ;
|
||||
if (x) return SYS_VM_EXECUTE;
|
||||
if (w) return SYS_VM_READWRITE;
|
||||
if (r) return SYS_VM_READ;
|
||||
return SYS_VM_NOACCESS;
|
||||
}
|
||||
|
||||
static KSTATUS PeProtectRange(SYS_HANDLE process, void* base, size_t pages, uint32_t protect) {
|
||||
if (process != SYS_HANDLE_INVALID)
|
||||
return SysProtectProcessVm(process, base, pages, protect);
|
||||
return SysProtectVm(base, pages, protect, (uint32_t*)0);
|
||||
}
|
||||
|
||||
static KSTATUS PeProtectImage(SYS_HANDLE process, uint8_t* image, const PE_VIEW* view) {
|
||||
size_t pages = ((size_t)view->ImageSize + PE_PAGE_SIZE - 1u) / PE_PAGE_SIZE;
|
||||
KSTATUS s = PeProtectRange(process, image, pages, SYS_VM_READ);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
for (uint16_t i = 0; i < view->SectionCount; ++i) {
|
||||
const uint8_t* sec = view->File + view->SectionOffset + (size_t)i * PE_SECTION_BYTES;
|
||||
uint32_t va = PeU32(sec + 12u);
|
||||
uint32_t virtualSize = PeU32(sec + 8u);
|
||||
uint32_t rawSize = PeU32(sec + 16u);
|
||||
uint32_t span = virtualSize > rawSize ? virtualSize : rawSize;
|
||||
if (!span) continue;
|
||||
size_t sectionPages = ((size_t)span + PE_PAGE_SIZE - 1u) / PE_PAGE_SIZE;
|
||||
s = PeProtectRange(process, image + va, sectionPages, PeSectionProtect(PeU32(sec + 36u)));
|
||||
if (!KSUCCESS(s)) return s;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS PeAllocateImage(SYS_HANDLE process, size_t pages, void** baseOut) {
|
||||
if (process == SYS_HANDLE_INVALID) {
|
||||
SYS_ALLOC_VM_REQUEST alloc;
|
||||
memset(&alloc, 0, sizeof(alloc));
|
||||
alloc.Size = sizeof(alloc);
|
||||
alloc.Protect = SYS_VM_READWRITE;
|
||||
alloc.Flags = SYS_VM_FLAG_NONE;
|
||||
alloc.PageCount = pages;
|
||||
alloc.BaseOut = baseOut;
|
||||
return SysAllocVm(&alloc);
|
||||
}
|
||||
|
||||
SYS_ALLOC_PROCESS_VM_REQUEST alloc;
|
||||
memset(&alloc, 0, sizeof(alloc));
|
||||
alloc.Size = sizeof(alloc);
|
||||
alloc.ProcessHandle = process;
|
||||
alloc.Protect = SYS_VM_READWRITE;
|
||||
alloc.Flags = SYS_VM_FLAG_NONE;
|
||||
alloc.PageCount = pages;
|
||||
alloc.BaseOut = baseOut;
|
||||
return SysAllocProcessVm(&alloc);
|
||||
}
|
||||
|
||||
KSTATUS NtlLoadPeImage(const NTL_PE_LOAD_REQUEST* request) {
|
||||
if (!request || request->Size != sizeof(*request) || request->Flags != NTL_PE_LOAD_FLAG_NONE ||
|
||||
request->FileHandle == SYS_HANDLE_INVALID || !request->ImageOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
void* file = 0;
|
||||
size_t fileSize = 0;
|
||||
KSTATUS s = PeReadFile(request->FileHandle, &file, &fileSize);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
PE_VIEW view;
|
||||
s = PeParse(file, fileSize, &view);
|
||||
if (!KSUCCESS(s)) { SysFreeVm(file); return s; }
|
||||
|
||||
size_t pages = ((size_t)view.ImageSize + PE_PAGE_SIZE - 1u) / PE_PAGE_SIZE;
|
||||
void* imageBase = 0;
|
||||
s = PeAllocateImage(request->ProcessHandle, pages, &imageBase);
|
||||
if (!KSUCCESS(s)) { SysFreeVm(file); return s; }
|
||||
|
||||
uint8_t* image = (uint8_t*)imageBase;
|
||||
memset(image, 0, pages * PE_PAGE_SIZE);
|
||||
memcpy(image, file, view.HeaderSize);
|
||||
for (uint16_t i = 0; i < view.SectionCount; ++i) {
|
||||
const uint8_t* sec = view.File + view.SectionOffset + (size_t)i * PE_SECTION_BYTES;
|
||||
uint32_t va = PeU32(sec + 12u);
|
||||
uint32_t rawSize = PeU32(sec + 16u);
|
||||
uint32_t raw = PeU32(sec + 20u);
|
||||
if (rawSize) memcpy(image + va, (const uint8_t*)file + raw, rawSize);
|
||||
}
|
||||
|
||||
s = PeRelocate(image, view.ImageSize, &view, (uintptr_t)image);
|
||||
if (KSUCCESS(s)) {
|
||||
request->ImageOut->Base = (uintptr_t)image;
|
||||
request->ImageOut->Size = view.ImageSize;
|
||||
request->ImageOut->EntryPoint = (uintptr_t)image + view.EntryRva;
|
||||
request->ImageOut->PreferredBase = view.ImageBase;
|
||||
if (request->Prepared)
|
||||
s = request->Prepared(request->PreparedContext, request->ImageOut);
|
||||
}
|
||||
if (KSUCCESS(s)) s = PeBindImports(image, view.ImageSize, &view, request->ResolveImport, request->ResolveContext);
|
||||
if (KSUCCESS(s)) s = PeProtectImage(request->ProcessHandle, image, &view);
|
||||
if (!KSUCCESS(s) && request->ProcessHandle == SYS_HANDLE_INVALID)
|
||||
(void)SysFreeVm(image);
|
||||
SysFreeVm(file);
|
||||
return s;
|
||||
}
|
||||
|
||||
KSTATUS NtlGetPeExport(uintptr_t imageBase, SYS_STRING_VIEW name, uintptr_t* addressOut) {
|
||||
if (!imageBase || !addressOut || !SysSvIsValid(name) || name.Length == 0 ||
|
||||
SysSvContainsByte(name, '\0'))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*addressOut = 0;
|
||||
|
||||
const uint8_t* image = (const uint8_t*)imageBase;
|
||||
if (PeU16(image) != PE_DOS_MAGIC) return NTLSTATUS_FAILURE;
|
||||
uint32_t nt = PeU32(image + 0x3cu);
|
||||
if (PeU32(image + nt) != PE_SIGNATURE || PeU16(image + nt + 4u) != PE_MACHINE_AMD64)
|
||||
return NTLSTATUS_FAILURE;
|
||||
const uint8_t* optional = image + nt + 24u;
|
||||
if (PeU16(optional) != PE_OPTIONAL_MAGIC_PE32_PLUS) return NTLSTATUS_FAILURE;
|
||||
uint32_t imageSize = PeU32(optional + 56u);
|
||||
uint32_t directories = PeU32(optional + 108u);
|
||||
if (directories < 1u) return NTLSTATUS_NOT_FOUND;
|
||||
uint32_t exportRva = PeU32(optional + PE_OPTIONAL_MIN_BYTES);
|
||||
uint32_t exportSize = PeU32(optional + PE_OPTIONAL_MIN_BYTES + 4u);
|
||||
if (!exportRva || !exportSize || !PeImageRange(exportRva, exportSize, imageSize) ||
|
||||
!PeImageRange(exportRva, 40u, imageSize))
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
|
||||
const uint8_t* exp = image + exportRva;
|
||||
uint32_t functionCount = PeU32(exp + 20u);
|
||||
uint32_t nameCount = PeU32(exp + 24u);
|
||||
uint32_t functionsRva = PeU32(exp + 28u);
|
||||
uint32_t namesRva = PeU32(exp + 32u);
|
||||
uint32_t ordinalsRva = PeU32(exp + 36u);
|
||||
if (!PeImageRange(functionsRva, (size_t)functionCount * 4u, imageSize) ||
|
||||
!PeImageRange(namesRva, (size_t)nameCount * 4u, imageSize) ||
|
||||
!PeImageRange(ordinalsRva, (size_t)nameCount * 2u, imageSize))
|
||||
return NTLSTATUS_FAILURE;
|
||||
|
||||
for (uint32_t i = 0; i < nameCount; ++i) {
|
||||
uint32_t nameRva = PeU32(image + namesRva + (size_t)i * 4u);
|
||||
size_t exportedLength = 0;
|
||||
if (!PeImageString(image, imageSize, nameRva, &exportedLength)) return NTLSTATUS_FAILURE;
|
||||
if (exportedLength != name.Length || memcmp(image + nameRva, name.Data, name.Length) != 0)
|
||||
continue;
|
||||
uint16_t ordinal = PeU16(image + ordinalsRva + (size_t)i * 2u);
|
||||
if (ordinal >= functionCount) return NTLSTATUS_FAILURE;
|
||||
uint32_t functionRva = PeU32(image + functionsRva + (size_t)ordinal * 4u);
|
||||
if (!functionRva || !PeImageRange(functionRva, 1u, imageSize)) return NTLSTATUS_NOT_FOUND;
|
||||
if (functionRva >= exportRva && functionRva < exportRva + exportSize)
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
*addressOut = imageBase + functionRva;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
// Native userspace process construction built on capability-relative file open and PE materialization.
|
||||
|
||||
#include <ntlite/process_loader.h>
|
||||
#include <ntlite/path.h>
|
||||
#include <ntlite/resource.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <string.h>
|
||||
#include "module_loader_internal.h"
|
||||
|
||||
// Terminates one failed constructing child and releases the parent process handle.
|
||||
static void NtlDiscardChild(SYS_HANDLE processHandle) {
|
||||
if (processHandle == SYS_HANDLE_INVALID)
|
||||
return;
|
||||
(void)SysKillProcess(processHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
|
||||
(void)SysCloseHandle(processHandle);
|
||||
}
|
||||
|
||||
// Creates and starts one child from an already-open PE file capability.
|
||||
static KSTATUS NtlCreateProcessFile(const NTL_CREATE_PROCESS_AT_REQUEST* request, SYS_HANDLE fileHandle) {
|
||||
if (!request || fileHandle == SYS_HANDLE_INVALID)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
SYS_HANDLE processHandle = SYS_HANDLE_INVALID;
|
||||
SYS_CREATE_PROCESS_OBJECT_REQUEST create;
|
||||
memset(&create, 0, sizeof(create));
|
||||
create.Size = sizeof(create);
|
||||
create.Flags = SYS_CREATE_PROCESS_OBJECT_FLAG_NONE;
|
||||
create.ImagePath = request->Path;
|
||||
create.CommandLine = request->CommandLine;
|
||||
create.SecurityContext = request->SecurityContext;
|
||||
create.ProcessHandleOut = &processHandle;
|
||||
KSTATUS status = SysCreateProcessObject(&create);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
NTL_MODULE_CONTEXT* modules = 0;
|
||||
status = NtlpCreateModuleContext(processHandle,
|
||||
request->ModuleRootHandle,
|
||||
request->ModuleDirectory,
|
||||
request->ResolveModuleName,
|
||||
request->ResolveContext,
|
||||
0,
|
||||
&modules);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
|
||||
NTL_PE_IMAGE image;
|
||||
memset(&image, 0, sizeof(image));
|
||||
status = NtlpLoadMainImage(modules, fileHandle, &image);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
|
||||
SYS_STARTUP_CAPABILITY childCapabilities[SYS_STARTUP_CAPABILITY_MAX];
|
||||
memset(childCapabilities, 0, sizeof(childCapabilities));
|
||||
for (uint32_t i = 0; i < request->StartupCapabilityCount; ++i) {
|
||||
const NTL_PROCESS_STARTUP_CAPABILITY* source = &request->StartupCapabilities[i];
|
||||
if (!source->Tag || source->SourceHandle == SYS_HANDLE_INVALID) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
for (uint32_t j = 0; j < i; ++j) {
|
||||
if (childCapabilities[j].Tag == source->Tag) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
SYS_HANDLE childHandle = SYS_HANDLE_INVALID;
|
||||
SYS_DUPLICATE_HANDLE_REQUEST duplicate;
|
||||
memset(&duplicate, 0, sizeof(duplicate));
|
||||
duplicate.Size = sizeof(duplicate);
|
||||
duplicate.SourceHandle = source->SourceHandle;
|
||||
duplicate.TargetProcessHandle = processHandle;
|
||||
duplicate.DesiredRights = source->DesiredRights;
|
||||
duplicate.Flags = source->DuplicateFlags;
|
||||
duplicate.TargetHandleOut = &childHandle;
|
||||
status = SysDuplicateHandle(&duplicate);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
childCapabilities[i].Tag = source->Tag;
|
||||
childCapabilities[i].Handle = childHandle;
|
||||
}
|
||||
|
||||
char currentDirectoryBuffer[SYS_PATH_MAX + 1u];
|
||||
SYS_STRING_VIEW currentDirectory;
|
||||
if (request->CurrentDirectory.Length != 0) {
|
||||
size_t required = 0;
|
||||
status = NtlRealPath(request->CurrentDirectory, currentDirectoryBuffer,
|
||||
sizeof(currentDirectoryBuffer), &required);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
currentDirectory = SysSvFromParts(currentDirectoryBuffer, required - 1u);
|
||||
|
||||
SYS_HANDLE cwdHandle = SYS_HANDLE_INVALID;
|
||||
status = NtlOpenDir(currentDirectory, SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN,
|
||||
SYS_DIR_FLAG_NONE, &cwdHandle);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
(void)SysCloseHandle(cwdHandle);
|
||||
} else {
|
||||
size_t required = 0;
|
||||
status = NtlGetCurrentDirectory(currentDirectoryBuffer, sizeof(currentDirectoryBuffer), &required);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
currentDirectory = SysSvFromParts(currentDirectoryBuffer, required - 1u);
|
||||
}
|
||||
|
||||
void* loaderData = 0;
|
||||
status = NtlpBuildLoaderData(modules, currentDirectory, &loaderData);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlpDestroyModuleContext(modules);
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
|
||||
SYS_START_PROCESS_REQUEST startRequest;
|
||||
memset(&startRequest, 0, sizeof(startRequest));
|
||||
startRequest.Size = sizeof(startRequest);
|
||||
startRequest.ProcessHandle = processHandle;
|
||||
startRequest.Flags = SYS_START_PROCESS_FLAG_NONE;
|
||||
startRequest.MainImageBase = image.Base;
|
||||
startRequest.MainImageSize = image.Size;
|
||||
startRequest.EntryPoint = image.EntryPoint;
|
||||
startRequest.StartupCapabilities = childCapabilities;
|
||||
startRequest.StartupCapabilityCount = request->StartupCapabilityCount;
|
||||
startRequest.LoaderData = loaderData;
|
||||
status = SysStartProcess(&startRequest);
|
||||
NtlpDestroyModuleContext(modules);
|
||||
if (!KSUCCESS(status)) {
|
||||
NtlDiscardChild(processHandle);
|
||||
return status;
|
||||
}
|
||||
|
||||
*request->ProcessHandleOut = processHandle;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Creates and starts one child from a PE file beneath a capability-relative root.
|
||||
KSTATUS NtlCreateProcessAt(const NTL_CREATE_PROCESS_AT_REQUEST* request) {
|
||||
if (!request || request->Size != sizeof(*request) ||
|
||||
request->Flags != NTL_PROCESS_LOAD_FLAG_NONE ||
|
||||
request->RootHandle == SYS_HANDLE_INVALID ||
|
||||
request->ModuleRootHandle == SYS_HANDLE_INVALID ||
|
||||
!request->Path.Data || request->Path.Length == 0 ||
|
||||
!SysSvIsValid(request->ModuleDirectory) ||
|
||||
request->StartupCapabilityCount > SYS_STARTUP_CAPABILITY_MAX ||
|
||||
(request->StartupCapabilityCount && !request->StartupCapabilities) ||
|
||||
!request->ProcessHandleOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
*request->ProcessHandleOut = SYS_HANDLE_INVALID;
|
||||
|
||||
SYS_HANDLE fileHandle = SYS_HANDLE_INVALID;
|
||||
SYS_OPEN_FILE_AT_REQUEST open;
|
||||
memset(&open, 0, sizeof(open));
|
||||
open.Size = sizeof(open);
|
||||
open.RootHandle = request->RootHandle;
|
||||
open.Path = request->Path;
|
||||
open.Access = SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY;
|
||||
open.Disposition = SYS_FILE_OPEN_EXISTING;
|
||||
open.Flags = SYS_FILE_FLAG_NONE;
|
||||
open.HandleOut = &fileHandle;
|
||||
KSTATUS status = SysOpenFileAt(&open);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
status = NtlCreateProcessFile(request, fileHandle);
|
||||
(void)SysCloseHandle(fileHandle);
|
||||
return status;
|
||||
}
|
||||
|
||||
KSTATUS NtlCreateSystemProcess(const NTL_CREATE_SYSTEM_PROCESS_REQUEST* request) {
|
||||
if (!request || request->Size != sizeof(*request) ||
|
||||
request->Flags != NTL_PROCESS_LOAD_FLAG_NONE || !request->Path.Data ||
|
||||
request->Path.Length == 0 || !request->ProcessHandleOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
*request->ProcessHandleOut = SYS_HANDLE_INVALID;
|
||||
|
||||
SYS_HANDLE systemRoot = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &systemRoot);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NTL_CREATE_PROCESS_AT_REQUEST create;
|
||||
memset(&create, 0, sizeof(create));
|
||||
create.Size = sizeof(create);
|
||||
create.Flags = request->Flags;
|
||||
create.RootHandle = systemRoot;
|
||||
create.Path = request->Path;
|
||||
create.CommandLine = request->CommandLine;
|
||||
create.CurrentDirectory = request->CurrentDirectory;
|
||||
create.SecurityContext = request->SecurityContext;
|
||||
create.ModuleRootHandle = systemRoot;
|
||||
create.ModuleDirectory = SysSvFromCstr("system");
|
||||
create.ResolveModuleName = request->ResolveModuleName;
|
||||
create.ResolveContext = request->ResolveContext;
|
||||
create.StartupCapabilities = request->StartupCapabilities;
|
||||
create.StartupCapabilityCount = request->StartupCapabilityCount;
|
||||
create.ProcessHandleOut = request->ProcessHandleOut;
|
||||
if (request->ImageFile != SYS_HANDLE_INVALID) {
|
||||
status = NtlCreateProcessFile(&create, request->ImageFile);
|
||||
} else {
|
||||
SYS_HANDLE imageFile = SYS_HANDLE_INVALID;
|
||||
status = NtlOpenFile(request->Path, SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
|
||||
SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, &imageFile);
|
||||
if (KSUCCESS(status)) {
|
||||
status = NtlCreateProcessFile(&create, imageFile);
|
||||
(void)SysCloseHandle(imageFile);
|
||||
}
|
||||
}
|
||||
(void)SysCloseHandle(systemRoot);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
// Native resource-service client. Resource naming policy stays in rssrv; this file only speaks its Port protocol.
|
||||
|
||||
#include <ntlite/resource.h>
|
||||
#include <ntlite/service.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <string.h>
|
||||
|
||||
// Validates the canonical global resource-path syntax understood by rssrv.
|
||||
static KSTATUS NtlValidateResourcePath(SYS_STRING_VIEW path) {
|
||||
if (!path.Data || path.Length < 2 || path.Length > NTL_RESOURCE_PATH_MAX || path.Data[0] != '/')
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (path.Data[path.Length - 1] == '/')
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
size_t componentStart = 1;
|
||||
for (size_t i = 1; i <= path.Length; ++i) {
|
||||
if (i < path.Length) {
|
||||
char c = path.Data[i];
|
||||
if (c == '\\' || c == '\0')
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (c != '/')
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t componentLength = i - componentStart;
|
||||
if (componentLength == 0 ||
|
||||
(componentLength == 1 && path.Data[componentStart] == '.') ||
|
||||
(componentLength == 2 && path.Data[componentStart] == '.' && path.Data[componentStart + 1] == '.'))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
componentStart = i + 1;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Builds one inline request header and path payload.
|
||||
static KSTATUS NtlBuildResourceRequest(uint8_t* buffer,
|
||||
size_t bufferSize,
|
||||
uint32_t operation,
|
||||
SYS_STRING_VIEW path,
|
||||
SYS_RIGHTS rights,
|
||||
SYS_RIGHTS exportRights,
|
||||
uint32_t kind,
|
||||
uint32_t* lengthOut) {
|
||||
KSTATUS status = NtlValidateResourcePath(path);
|
||||
if (!KSUCCESS(status) || !buffer || !lengthOut)
|
||||
return KSUCCESS(status) ? NTLSTATUS_INVALID_PARAMETER : status;
|
||||
size_t total = sizeof(NTL_RESOURCE_REQUEST) + path.Length;
|
||||
if (total > bufferSize || total > SYS_PORT_MAX_INLINE_BYTES)
|
||||
return NTLSTATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
NTL_RESOURCE_REQUEST header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.Size = sizeof(header);
|
||||
header.Version = NTL_RESOURCE_PROTOCOL_VERSION;
|
||||
header.Operation = operation;
|
||||
header.Flags = NTL_RESOURCE_FLAG_NONE;
|
||||
header.Rights = rights;
|
||||
header.ExportRights = exportRights;
|
||||
header.Kind = kind;
|
||||
header.PathLength = (uint32_t)path.Length;
|
||||
memcpy(buffer, &header, sizeof(header));
|
||||
memcpy(buffer + sizeof(header), path.Data, path.Length);
|
||||
*lengthOut = (uint32_t)total;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Executes one resource request and validates its fixed protocol reply.
|
||||
static KSTATUS NtlCallResource(SYS_HANDLE session,
|
||||
const void* requestPayload,
|
||||
uint32_t requestLength,
|
||||
const SYS_PORT_HANDLE_TRANSFER* requestHandles,
|
||||
uint32_t requestHandleCount,
|
||||
NTL_RESOURCE_REPLY* replyOut,
|
||||
SYS_HANDLE* replyHandleOut) {
|
||||
if (session == SYS_HANDLE_INVALID || !requestPayload || !requestLength || !replyOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
NTL_RESOURCE_REPLY reply;
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
SYS_PORT_MESSAGE_INFO info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.Size = sizeof(info);
|
||||
SYS_HANDLE replyHandle = SYS_HANDLE_INVALID;
|
||||
|
||||
SYS_CALL_PORT_REQUEST call;
|
||||
memset(&call, 0, sizeof(call));
|
||||
call.Size = sizeof(call);
|
||||
call.EndpointHandle = session;
|
||||
call.Flags = SYS_PORT_FLAG_NONE;
|
||||
call.RequestPayload = requestPayload;
|
||||
call.RequestLength = requestLength;
|
||||
call.RequestHandles = requestHandles;
|
||||
call.RequestHandleCount = requestHandleCount;
|
||||
call.ReplyPayload = &reply;
|
||||
call.ReplyCapacity = sizeof(reply);
|
||||
call.ReplyHandlesOut = replyHandleOut ? &replyHandle : (SYS_HANDLE*)0;
|
||||
call.ReplyHandleCapacity = replyHandleOut ? 1u : 0u;
|
||||
call.ReplyInfoOut = &info;
|
||||
call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
|
||||
KSTATUS status = SysCallPort(&call);
|
||||
if (!KSUCCESS(status))
|
||||
return status;
|
||||
|
||||
if (info.Type != SYS_PORT_MESSAGE_REPLY || info.PayloadLength != sizeof(reply) ||
|
||||
reply.Size != sizeof(reply) || reply.Version != NTL_RESOURCE_PROTOCOL_VERSION) {
|
||||
if (replyHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(replyHandle);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
if ((!replyHandleOut && info.HandleCount != 0) || (replyHandleOut && info.HandleCount > 1)) {
|
||||
if (replyHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(replyHandle);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
*replyOut = reply;
|
||||
if (replyHandleOut) *replyHandleOut = replyHandle;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Connects to the registered resource listener.
|
||||
KSTATUS NtlResourceConnect(SYS_HANDLE* sessionOut) {
|
||||
if (!sessionOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*sessionOut = SYS_HANDLE_INVALID;
|
||||
SYS_CONNECT_SYSTEM_SERVICE_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.ServiceId = SYS_SERVICE_RESOURCE;
|
||||
request.EndpointHandleOut = sessionOut;
|
||||
return SysConnectSystemService(&request);
|
||||
}
|
||||
|
||||
// Resolves the canonical /SystemRoot resource and closes the transient resource session.
|
||||
KSTATUS NtlResourceOpenSystemRoot(SYS_RIGHTS desiredRights, SYS_HANDLE* handleOut) {
|
||||
if (!desiredRights || !handleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*handleOut = SYS_HANDLE_INVALID;
|
||||
SYS_HANDLE session = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = NtlResourceConnect(&session);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = NtlResourceLookup(session, SysSvFromCstr("/SystemRoot"), desiredRights, handleOut);
|
||||
(void)NtlResourceClose(session);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Connects through one delegated resource-listener capability.
|
||||
KSTATUS NtlResourceConnectListener(SYS_HANDLE listenerHandle, SYS_HANDLE* sessionOut) {
|
||||
if (listenerHandle == SYS_HANDLE_INVALID || !sessionOut)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*sessionOut = SYS_HANDLE_INVALID;
|
||||
SYS_CONNECT_PORT_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.ListenerHandle = listenerHandle;
|
||||
request.Flags = SYS_PORT_CONNECT_FLAG_NONE;
|
||||
request.EndpointHandleOut = sessionOut;
|
||||
return SysConnectPort(&request);
|
||||
}
|
||||
|
||||
// Looks up one resource and requests an attenuated alias of its bound capability.
|
||||
KSTATUS NtlResourceLookup(SYS_HANDLE session,
|
||||
SYS_STRING_VIEW path,
|
||||
SYS_RIGHTS desiredRights,
|
||||
SYS_HANDLE* handleOut) {
|
||||
if (!desiredRights || !handleOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*handleOut = SYS_HANDLE_INVALID;
|
||||
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
|
||||
uint32_t payloadLength = 0;
|
||||
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_LOOKUP,
|
||||
path, desiredRights, 0, NTL_RESOURCE_KIND_INVALID,
|
||||
&payloadLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NTL_RESOURCE_REPLY reply;
|
||||
SYS_HANDLE handle = SYS_HANDLE_INVALID;
|
||||
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
|
||||
&reply, &handle);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (reply.Operation != NTL_RESOURCE_OP_LOOKUP) {
|
||||
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
if (!KSUCCESS(reply.Status)) {
|
||||
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
|
||||
return reply.Status;
|
||||
}
|
||||
if (handle == SYS_HANDLE_INVALID) return NTLSTATUS_FAILURE;
|
||||
*handleOut = handle;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Queries one bound resource kind without transferring authority.
|
||||
KSTATUS NtlResourceQuery(SYS_HANDLE session, SYS_STRING_VIEW path, uint32_t* kindOut) {
|
||||
if (!kindOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*kindOut = NTL_RESOURCE_KIND_INVALID;
|
||||
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
|
||||
uint32_t payloadLength = 0;
|
||||
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_QUERY,
|
||||
path, 0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NTL_RESOURCE_REPLY reply;
|
||||
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
|
||||
&reply, (SYS_HANDLE*)0);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (reply.Operation != NTL_RESOURCE_OP_QUERY) return NTLSTATUS_FAILURE;
|
||||
if (!KSUCCESS(reply.Status)) return reply.Status;
|
||||
*kindOut = reply.Kind;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Resolves the longest bound resource prefix into one traversal capability.
|
||||
KSTATUS NtlResourceResolve(SYS_HANDLE session,
|
||||
SYS_STRING_VIEW path,
|
||||
uint32_t flags,
|
||||
SYS_HANDLE* rootHandleOut,
|
||||
uint32_t* kindOut,
|
||||
uint32_t* resolvedLengthOut) {
|
||||
if (!rootHandleOut || !kindOut || !resolvedLengthOut ||
|
||||
(flags & ~NTL_RESOURCE_RESOLVE_FLAG_MODIFY) != 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*rootHandleOut = SYS_HANDLE_INVALID;
|
||||
*kindOut = NTL_RESOURCE_KIND_INVALID;
|
||||
*resolvedLengthOut = 0;
|
||||
|
||||
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
|
||||
uint32_t payloadLength = 0;
|
||||
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_RESOLVE,
|
||||
path, 0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
((NTL_RESOURCE_REQUEST*)payload)->Flags = flags;
|
||||
|
||||
NTL_RESOURCE_REPLY reply;
|
||||
SYS_HANDLE handle = SYS_HANDLE_INVALID;
|
||||
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
|
||||
&reply, &handle);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (reply.Operation != NTL_RESOURCE_OP_RESOLVE || reply.Flags != flags) {
|
||||
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
if (!KSUCCESS(reply.Status)) {
|
||||
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
|
||||
return reply.Status;
|
||||
}
|
||||
if (handle == SYS_HANDLE_INVALID || reply.Kind == NTL_RESOURCE_KIND_INVALID ||
|
||||
reply.ResolvedLength < 2 || reply.ResolvedLength > path.Length ||
|
||||
(reply.ResolvedLength != path.Length && path.Data[reply.ResolvedLength] != '/')) {
|
||||
if (handle != SYS_HANDLE_INVALID) (void)SysCloseHandle(handle);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
*rootHandleOut = handle;
|
||||
*kindOut = reply.Kind;
|
||||
*resolvedLengthOut = reply.ResolvedLength;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Binds an explicitly attenuated capability into an administrative resource session.
|
||||
KSTATUS NtlResourceBind(SYS_HANDLE session,
|
||||
SYS_STRING_VIEW path,
|
||||
uint32_t kind,
|
||||
SYS_HANDLE resourceHandle,
|
||||
SYS_RIGHTS namespaceRights,
|
||||
SYS_RIGHTS exportRights) {
|
||||
if (kind == NTL_RESOURCE_KIND_INVALID || resourceHandle == SYS_HANDLE_INVALID ||
|
||||
!(namespaceRights & SYS_RIGHT_TRANSFER) || !exportRights ||
|
||||
(exportRights & ~namespaceRights) != 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
|
||||
uint32_t payloadLength = 0;
|
||||
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_BIND,
|
||||
path, namespaceRights, exportRights, kind, &payloadLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_PORT_HANDLE_TRANSFER transfer;
|
||||
transfer.Handle = resourceHandle;
|
||||
transfer.DesiredRights = namespaceRights;
|
||||
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
||||
NTL_RESOURCE_REPLY reply;
|
||||
status = NtlCallResource(session, payload, payloadLength, &transfer, 1, &reply, (SYS_HANDLE*)0);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (reply.Operation != NTL_RESOURCE_OP_BIND) return NTLSTATUS_FAILURE;
|
||||
return reply.Status;
|
||||
}
|
||||
|
||||
// Removes one resource name through an administrative session.
|
||||
KSTATUS NtlResourceUnbind(SYS_HANDLE session, SYS_STRING_VIEW path) {
|
||||
uint8_t payload[sizeof(NTL_RESOURCE_REQUEST) + NTL_RESOURCE_PATH_MAX];
|
||||
uint32_t payloadLength = 0;
|
||||
KSTATUS status = NtlBuildResourceRequest(payload, sizeof(payload), NTL_RESOURCE_OP_UNBIND,
|
||||
path, 0, 0, NTL_RESOURCE_KIND_INVALID, &payloadLength);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
NTL_RESOURCE_REPLY reply;
|
||||
status = NtlCallResource(session, payload, payloadLength, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
|
||||
&reply, (SYS_HANDLE*)0);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (reply.Operation != NTL_RESOURCE_OP_UNBIND) return NTLSTATUS_FAILURE;
|
||||
return reply.Status;
|
||||
}
|
||||
|
||||
// Disconnects and closes one resource session.
|
||||
KSTATUS NtlResourceClose(SYS_HANDLE session) {
|
||||
if (session == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_HANDLE;
|
||||
KSTATUS disconnect = SysDisconnectPort(session);
|
||||
KSTATUS close = SysCloseHandle(session);
|
||||
if (!KSUCCESS(disconnect) && disconnect != NTLSTATUS_PORT_DISCONNECTED)
|
||||
return disconnect;
|
||||
return close;
|
||||
}
|
||||
+159
-141
@@ -1,163 +1,181 @@
|
||||
; Syscall numbers shared by MASM user-mode stubs.
|
||||
|
||||
; Process, threads, modules, waits, APC, and exceptions.
|
||||
; Process, threads, waits, APC, and exceptions.
|
||||
SYS_EXIT_PROCESS EQU 0
|
||||
SYS_EXIT_THREAD EQU 1
|
||||
SYS_CREATE_PROCESS EQU 2
|
||||
SYS_OPEN_PROCESS EQU 3
|
||||
SYS_GET_PROCESS_EXIT_CODE EQU 4
|
||||
SYS_KILL_PROCESS EQU 5
|
||||
SYS_GET_PROCESS_LIST EQU 6
|
||||
SYS_GET_PROCESS_PID EQU 7
|
||||
SYS_CREATE_THREAD EQU 8
|
||||
SYS_RESUME_THREAD EQU 9
|
||||
SYS_GET_THREAD_LIST EQU 10
|
||||
SYS_LOAD_MODULE EQU 11
|
||||
SYS_UNLOAD_MODULE EQU 12
|
||||
SYS_GET_MODULE_INFO EQU 13
|
||||
SYS_GET_MODULE_EXPORT EQU 14
|
||||
SYS_GET_MODULE_LIST EQU 15
|
||||
SYS_GET_CURRENT_TID EQU 16
|
||||
SYS_GET_CURRENT_PID EQU 17
|
||||
SYS_SET_THREAD_PRIORITY EQU 18
|
||||
SYS_YIELD EQU 19
|
||||
SYS_SLEEP EQU 20
|
||||
SYS_WAIT_SINGLE EQU 21
|
||||
SYS_WAIT_MANY EQU 22
|
||||
SYS_SET_USER_APC_DISPATCHER EQU 23
|
||||
SYS_APC_RETURN EQU 24
|
||||
SYS_SET_EXCEPTION_DISPATCHER EQU 25
|
||||
SYS_GET_EXCEPTION_DISPATCHER EQU 26
|
||||
SYS_CONTINUE_EXCEPTION EQU 27
|
||||
SYS_RAISE_EXCEPTION EQU 28
|
||||
SYS_CREATE_PROCESS_OBJECT EQU 2
|
||||
SYS_ALLOC_PROCESS_VM EQU 3
|
||||
SYS_PROTECT_PROCESS_VM EQU 4
|
||||
SYS_START_PROCESS EQU 5
|
||||
SYS_OPEN_PROCESS EQU 6
|
||||
SYS_GET_PROCESS_EXIT_CODE EQU 7
|
||||
SYS_KILL_PROCESS EQU 8
|
||||
SYS_GET_PROCESS_LIST EQU 9
|
||||
SYS_GET_PROCESS_PID EQU 10
|
||||
SYS_CREATE_THREAD EQU 11
|
||||
SYS_RESUME_THREAD EQU 12
|
||||
SYS_GET_THREAD_LIST EQU 13
|
||||
SYS_GET_CURRENT_TID EQU 14
|
||||
SYS_GET_CURRENT_PID EQU 15
|
||||
SYS_SET_THREAD_PRIORITY EQU 16
|
||||
SYS_YIELD EQU 17
|
||||
SYS_SLEEP EQU 18
|
||||
SYS_WAIT_SINGLE EQU 19
|
||||
SYS_WAIT_MANY EQU 20
|
||||
SYS_SET_USER_APC_DISPATCHER EQU 21
|
||||
SYS_APC_RETURN EQU 22
|
||||
SYS_SET_EXCEPTION_DISPATCHER EQU 23
|
||||
SYS_GET_EXCEPTION_DISPATCHER EQU 24
|
||||
SYS_CONTINUE_EXCEPTION EQU 25
|
||||
SYS_RAISE_EXCEPTION EQU 26
|
||||
|
||||
; Virtual memory, mappings, and handles.
|
||||
SYS_ALLOC_VM EQU 29
|
||||
SYS_FREE_VM EQU 30
|
||||
SYS_PROTECT_VM EQU 31
|
||||
SYS_QUERY_VM EQU 32
|
||||
SYS_CLOSE_HANDLE EQU 33
|
||||
SYS_DUPLICATE_HANDLE EQU 34
|
||||
SYS_CREATE_FILE_MAPPING EQU 35
|
||||
SYS_MAP_FILE EQU 36
|
||||
SYS_ALLOC_VM EQU 27
|
||||
SYS_FREE_VM EQU 28
|
||||
SYS_PROTECT_VM EQU 29
|
||||
SYS_QUERY_VM EQU 30
|
||||
SYS_CLOSE_HANDLE EQU 31
|
||||
SYS_DUPLICATE_HANDLE EQU 32
|
||||
SYS_DERIVE_HANDLE EQU 33
|
||||
SYS_REVOKE_HANDLE EQU 34
|
||||
SYS_DERIVE_SECURITY_CONTEXT EQU 35
|
||||
SYS_QUERY_SECURITY_CONTEXT EQU 36
|
||||
SYS_CREATE_FILE_MAPPING EQU 37
|
||||
SYS_MAP_FILE EQU 38
|
||||
|
||||
; Synchronization objects.
|
||||
SYS_CREATE_MUTEX EQU 37
|
||||
SYS_TRY_ACQUIRE_MUTEX EQU 38
|
||||
SYS_RELEASE_MUTEX EQU 39
|
||||
SYS_CREATE_SEM EQU 40
|
||||
SYS_TRY_WAIT_SEM EQU 41
|
||||
SYS_SIGNAL_SEM EQU 42
|
||||
SYS_CREATE_EVENT EQU 43
|
||||
SYS_SIGNAL_EVENT EQU 44
|
||||
SYS_CLEAR_EVENT EQU 45
|
||||
SYS_CREATE_MUTEX EQU 39
|
||||
SYS_TRY_ACQUIRE_MUTEX EQU 40
|
||||
SYS_RELEASE_MUTEX EQU 41
|
||||
SYS_CREATE_SEM EQU 42
|
||||
SYS_TRY_WAIT_SEM EQU 43
|
||||
SYS_SIGNAL_SEM EQU 44
|
||||
SYS_CREATE_EVENT EQU 45
|
||||
SYS_SIGNAL_EVENT EQU 46
|
||||
SYS_CLEAR_EVENT EQU 47
|
||||
|
||||
; Queues and named queues.
|
||||
SYS_CREATE_QUEUE EQU 46
|
||||
SYS_SEND_QUEUE EQU 47
|
||||
SYS_RECV_QUEUE EQU 48
|
||||
SYS_PUBLISH_QUEUE EQU 49
|
||||
SYS_OPEN_NAMED_QUEUE EQU 50
|
||||
SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 51
|
||||
SYS_CREATE_QUEUE EQU 48
|
||||
SYS_SEND_QUEUE EQU 49
|
||||
SYS_RECV_QUEUE EQU 50
|
||||
SYS_PUBLISH_QUEUE EQU 51
|
||||
SYS_OPEN_NAMED_QUEUE EQU 52
|
||||
SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 53
|
||||
SYS_UNPUBLISH_QUEUE EQU 54
|
||||
|
||||
; Files, directories, and current directory.
|
||||
SYS_OPEN_FILE EQU 52
|
||||
SYS_READ_FILE EQU 53
|
||||
SYS_WRITE_FILE EQU 54
|
||||
SYS_READ_FILE_ASYNC EQU 55
|
||||
SYS_WRITE_FILE_ASYNC EQU 56
|
||||
SYS_CANCEL_IO EQU 57
|
||||
SYS_SEEK_FILE EQU 58
|
||||
SYS_GET_FILE_POS EQU 59
|
||||
SYS_GET_FILE_SIZE EQU 60
|
||||
SYS_GET_FILE_STAT EQU 61
|
||||
SYS_STAT_FILE EQU 62
|
||||
SYS_TRUNCATE_FILE EQU 63
|
||||
SYS_FLUSH_FILE EQU 64
|
||||
SYS_DELETE_FILE EQU 65
|
||||
SYS_RENAME_FILE EQU 66
|
||||
SYS_LOCK_FILE EQU 67
|
||||
SYS_UNLOCK_FILE EQU 68
|
||||
SYS_OPEN_DIR EQU 69
|
||||
SYS_READ_DIR EQU 70
|
||||
SYS_CREATE_DIR EQU 71
|
||||
SYS_REMOVE_DIR EQU 72
|
||||
SYS_SET_CWD EQU 73
|
||||
SYS_REAL_PATH EQU 74
|
||||
; LPC-like Ports and well-known system services.
|
||||
SYS_CREATE_PORT EQU 55
|
||||
SYS_CONNECT_PORT EQU 56
|
||||
SYS_ACCEPT_PORT EQU 57
|
||||
SYS_SEND_PORT EQU 58
|
||||
SYS_RECEIVE_PORT EQU 59
|
||||
SYS_CALL_PORT EQU 60
|
||||
SYS_REPLY_PORT EQU 61
|
||||
SYS_DISCONNECT_PORT EQU 62
|
||||
SYS_REGISTER_SYSTEM_SERVICE EQU 63
|
||||
SYS_UNREGISTER_SYSTEM_SERVICE EQU 64
|
||||
SYS_CONNECT_SYSTEM_SERVICE EQU 65
|
||||
|
||||
; Mounted-volume manager control plane.
|
||||
SYS_QUERY_VOLUMES EQU 66
|
||||
SYS_OPEN_VOLUME EQU 67
|
||||
SYS_WAIT_VOLUME_CHANGE EQU 68
|
||||
|
||||
; Files and directories.
|
||||
SYS_OPEN_FILE_AT EQU 69
|
||||
SYS_OPEN_DIR_AT EQU 70
|
||||
SYS_STAT_AT EQU 71
|
||||
SYS_DELETE_AT EQU 72
|
||||
SYS_RENAME_AT EQU 73
|
||||
SYS_CREATE_DIR_AT EQU 74
|
||||
SYS_REMOVE_DIR_AT EQU 75
|
||||
SYS_READ_FILE EQU 76
|
||||
SYS_WRITE_FILE EQU 77
|
||||
SYS_READ_FILE_ASYNC EQU 78
|
||||
SYS_WRITE_FILE_ASYNC EQU 79
|
||||
SYS_CANCEL_IO EQU 80
|
||||
SYS_SEEK_FILE EQU 81
|
||||
SYS_GET_FILE_POS EQU 82
|
||||
SYS_GET_FILE_SIZE EQU 83
|
||||
SYS_GET_FILE_STAT EQU 84
|
||||
SYS_TRUNCATE_FILE EQU 85
|
||||
SYS_FLUSH_FILE EQU 86
|
||||
SYS_LOCK_FILE EQU 87
|
||||
SYS_UNLOCK_FILE EQU 88
|
||||
SYS_READ_DIR EQU 89
|
||||
|
||||
; Console, display, and surfaces.
|
||||
SYS_WRITE_CONSOLE EQU 75
|
||||
SYS_GET_CONSOLE_INFO EQU 76
|
||||
SYS_SET_CONSOLE_CURSOR EQU 77
|
||||
SYS_FILL_CONSOLE_CHARACTER EQU 78
|
||||
SYS_FILL_CONSOLE_ATTRIBUTE EQU 79
|
||||
SYS_PUT_SCREEN_CHAR EQU 80
|
||||
SYS_CLEAR_SCREEN EQU 81
|
||||
SYS_GET_SCREEN_SIZE EQU 82
|
||||
SYS_QUERY_DISPLAY_INFO EQU 83
|
||||
SYS_OPEN_DISPLAY_CLOCK EQU 84
|
||||
SYS_CREATE_SURFACE EQU 85
|
||||
SYS_MAP_SURFACE EQU 86
|
||||
SYS_QUERY_SURFACE EQU 87
|
||||
SYS_PRESENT_SURFACE EQU 88
|
||||
SYS_WRITE_CONSOLE EQU 90
|
||||
SYS_GET_CONSOLE_INFO EQU 91
|
||||
SYS_SET_CONSOLE_CURSOR EQU 92
|
||||
SYS_FILL_CONSOLE_CHARACTER EQU 93
|
||||
SYS_FILL_CONSOLE_ATTRIBUTE EQU 94
|
||||
SYS_PUT_SCREEN_CHAR EQU 95
|
||||
SYS_CLEAR_SCREEN EQU 96
|
||||
SYS_GET_SCREEN_SIZE EQU 97
|
||||
SYS_QUERY_DISPLAY_INFO EQU 98
|
||||
SYS_OPEN_DISPLAY_CLOCK EQU 99
|
||||
SYS_CREATE_SURFACE EQU 100
|
||||
SYS_MAP_SURFACE EQU 101
|
||||
SYS_QUERY_SURFACE EQU 102
|
||||
SYS_PRESENT_SURFACE EQU 103
|
||||
|
||||
; Native render audio.
|
||||
SYS_AUDIO_ENUMERATE_ENDPOINTS EQU 89
|
||||
SYS_AUDIO_OPEN_STREAM EQU 90
|
||||
SYS_AUDIO_WRITE EQU 91
|
||||
SYS_AUDIO_ARM_WAIT EQU 92
|
||||
SYS_AUDIO_QUERY_STREAM EQU 93
|
||||
SYS_AUDIO_START EQU 94
|
||||
SYS_AUDIO_PAUSE EQU 95
|
||||
SYS_AUDIO_RESUME EQU 96
|
||||
SYS_AUDIO_DRAIN EQU 97
|
||||
SYS_AUDIO_STOP EQU 98
|
||||
SYS_AUDIO_RESET EQU 99
|
||||
SYS_AUDIO_SET_VOLUME EQU 100
|
||||
SYS_AUDIO_SET_MASTER_VOLUME EQU 101
|
||||
SYS_AUDIO_ENUMERATE_ENDPOINTS EQU 104
|
||||
SYS_AUDIO_OPEN_STREAM EQU 105
|
||||
SYS_AUDIO_WRITE EQU 106
|
||||
SYS_AUDIO_ARM_WAIT EQU 107
|
||||
SYS_AUDIO_QUERY_STREAM EQU 108
|
||||
SYS_AUDIO_START EQU 109
|
||||
SYS_AUDIO_PAUSE EQU 110
|
||||
SYS_AUDIO_RESUME EQU 111
|
||||
SYS_AUDIO_DRAIN EQU 112
|
||||
SYS_AUDIO_STOP EQU 113
|
||||
SYS_AUDIO_RESET EQU 114
|
||||
SYS_AUDIO_SET_VOLUME EQU 115
|
||||
SYS_AUDIO_SET_MASTER_VOLUME EQU 116
|
||||
|
||||
; Input.
|
||||
SYS_POLL_KEYBOARD EQU 102
|
||||
SYS_IS_KEY_DOWN EQU 103
|
||||
SYS_READ_KEYBOARD_CHAR EQU 104
|
||||
SYS_READ_KEY EQU 105
|
||||
SYS_GET_KEY_MODIFIERS EQU 106
|
||||
SYS_QUERY_GLOBAL_INPUT_STATE EQU 107
|
||||
SYS_OPEN_INPUT_QUEUE EQU 108
|
||||
SYS_READ_INPUT_EVENT EQU 109
|
||||
SYS_PEEK_INPUT_EVENT EQU 110
|
||||
SYS_PEEK_MOUSE EQU 111
|
||||
SYS_READ_MOUSE EQU 112
|
||||
SYS_GET_MOUSE_POS EQU 113
|
||||
SYS_POLL_KEYBOARD EQU 117
|
||||
SYS_IS_KEY_DOWN EQU 118
|
||||
SYS_READ_KEYBOARD_CHAR EQU 119
|
||||
SYS_READ_KEY EQU 120
|
||||
SYS_GET_KEY_MODIFIERS EQU 121
|
||||
SYS_QUERY_GLOBAL_INPUT_STATE EQU 122
|
||||
SYS_OPEN_INPUT_QUEUE EQU 123
|
||||
SYS_READ_INPUT_EVENT EQU 124
|
||||
SYS_PEEK_INPUT_EVENT EQU 125
|
||||
SYS_PEEK_MOUSE EQU 126
|
||||
SYS_READ_MOUSE EQU 127
|
||||
SYS_GET_MOUSE_POS EQU 128
|
||||
|
||||
; Time, debug, and system information.
|
||||
SYS_CREATE_TIMER EQU 114
|
||||
SYS_SET_TIMER EQU 115
|
||||
SYS_CANCEL_TIMER EQU 116
|
||||
SYS_GET_TICK EQU 117
|
||||
SYS_GET_TICK_FREQUENCY EQU 118
|
||||
SYS_GET_SYSTEM_TIME EQU 119
|
||||
SYS_PRINT_DEBUG EQU 120
|
||||
SYS_QUERY_TRAP_FRAME EQU 121
|
||||
SYS_GET_MEMORY_STATUS EQU 122
|
||||
SYS_QUERY_KERNEL_STATS EQU 123
|
||||
SYS_CREATE_TIMER EQU 129
|
||||
SYS_SET_TIMER EQU 130
|
||||
SYS_CANCEL_TIMER EQU 131
|
||||
SYS_GET_TICK EQU 132
|
||||
SYS_GET_TICK_FREQUENCY EQU 133
|
||||
SYS_GET_SYSTEM_TIME EQU 134
|
||||
SYS_PRINT_DEBUG EQU 135
|
||||
SYS_QUERY_TRAP_FRAME EQU 136
|
||||
SYS_GET_MEMORY_STATUS EQU 137
|
||||
SYS_QUERY_KERNEL_STATS EQU 138
|
||||
|
||||
; Native sockets and polling.
|
||||
SYS_CREATE_SOCKET EQU 124
|
||||
SYS_SOCKET_IO_ASYNC EQU 125
|
||||
SYS_BIND_SOCKET EQU 126
|
||||
SYS_LISTEN_SOCKET EQU 127
|
||||
SYS_ACCEPT_SOCKET EQU 128
|
||||
SYS_CONNECT_SOCKET EQU 129
|
||||
SYS_SEND_SOCKET EQU 130
|
||||
SYS_RECV_SOCKET EQU 131
|
||||
SYS_SHUTDOWN_SOCKET EQU 132
|
||||
SYS_SEND_TO_SOCKET EQU 133
|
||||
SYS_RECV_FROM_SOCKET EQU 134
|
||||
SYS_RESOLVE_HOST EQU 135
|
||||
SYS_SET_SOCKET_OPTION EQU 136
|
||||
SYS_GET_SOCKET_OPTION EQU 137
|
||||
SYS_POLL EQU 138
|
||||
SYS_UNPUBLISH_QUEUE EQU 139
|
||||
SYS_CREATE_SOCKET EQU 139
|
||||
SYS_SOCKET_IO_ASYNC EQU 140
|
||||
SYS_BIND_SOCKET EQU 141
|
||||
SYS_LISTEN_SOCKET EQU 142
|
||||
SYS_ACCEPT_SOCKET EQU 143
|
||||
SYS_CONNECT_SOCKET EQU 144
|
||||
SYS_SEND_SOCKET EQU 145
|
||||
SYS_RECV_SOCKET EQU 146
|
||||
SYS_SHUTDOWN_SOCKET EQU 147
|
||||
SYS_SEND_TO_SOCKET EQU 148
|
||||
SYS_RECV_FROM_SOCKET EQU 149
|
||||
SYS_RESOLVE_HOST EQU 150
|
||||
SYS_SET_SOCKET_OPTION EQU 151
|
||||
SYS_GET_SOCKET_OPTION EQU 152
|
||||
SYS_POLL EQU 153
|
||||
|
||||
SYS_MAX EQU 140
|
||||
SYS_MAX EQU 154
|
||||
+181
-190
@@ -1,20 +1,14 @@
|
||||
; syscall_stubs.asm - user-mode INT 0x40 stubs exported by syscall.dll (MASM64)
|
||||
; syscall_stubs.asm - user-mode INT 0x40 stubs for syscall.dll and syscall_s.lib (MASM64)
|
||||
;
|
||||
; Calling convention (kernel side):
|
||||
; RAX = syscall number
|
||||
; RCX = arg 0
|
||||
; RDX = arg 1
|
||||
; R8 = arg 2
|
||||
; R9 = arg 3
|
||||
; R10 = arg 4 (our extension - loaded from the stack by SYSCALL_STUB6)
|
||||
; R11 = arg 5 (our extension - loaded from the stack by SYSCALL_STUB6)
|
||||
; Calling convention:
|
||||
; RAX = syscall number.
|
||||
; RCX = arg 0.
|
||||
; RDX = arg 1.
|
||||
; R8 = arg 2.
|
||||
; R9 = arg 3.
|
||||
;
|
||||
; User-mode callers follow standard MS x64: args 1-4 in RCX/RDX/R8/R9,
|
||||
; args 5+ on the stack above the home area ([rsp+28h], [rsp+30h], ...).
|
||||
;
|
||||
; SYSCALL_STUB2/3 - 4 or fewer args: pure register passing, no stack read.
|
||||
; SYSCALL_STUB5 - exactly 5 args: loads [rsp+28h] -> R10 only.
|
||||
; SYSCALL_STUB6 - 5 or 6 args: loads [rsp+28h] -> R10 and [rsp+30h] -> R11.
|
||||
; Syscalls never consume stack arguments. Operations that need more than four
|
||||
; logical arguments use a request structure.
|
||||
|
||||
INCLUDE syscall_nr.inc
|
||||
|
||||
@@ -28,42 +22,13 @@ SYS_USER_APC_FRAME_SystemArgument2 EQU 40
|
||||
.CODE
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 MACRO name, nr
|
||||
SYSCALL_STUB MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
IFDEF NTL_SYSCALL_STATIC
|
||||
Sys&name PROC
|
||||
ELSE
|
||||
Sys&name PROC EXPORT
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB3 MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
Sys&name PROC EXPORT
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB5 MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
Sys&name PROC EXPORT
|
||||
mov r10, [rsp+28h] ; 5th argument
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB6 MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
Sys&name PROC EXPORT
|
||||
mov r10, [rsp+28h] ; 5th argument
|
||||
mov r11, [rsp+30h] ; 6th argument
|
||||
ENDIF
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
@@ -73,43 +38,45 @@ ENDM
|
||||
; ---------------------------------------------------------------------------
|
||||
; Process & thread lifecycle
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 ExitProcess, SYS_EXIT_PROCESS
|
||||
SYSCALL_STUB2 ExitThread, SYS_EXIT_THREAD
|
||||
SYSCALL_STUB2 CreateProcess, SYS_CREATE_PROCESS
|
||||
SYSCALL_STUB3 WaitSingle, SYS_WAIT_SINGLE
|
||||
SYSCALL_STUB2 WaitMany, SYS_WAIT_MANY
|
||||
SYSCALL_STUB2 OpenInputQueue, SYS_OPEN_INPUT_QUEUE
|
||||
SYSCALL_STUB2 GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
|
||||
SYSCALL_STUB2 OpenProcess, SYS_OPEN_PROCESS
|
||||
SYSCALL_STUB2 KillProcess, SYS_KILL_PROCESS
|
||||
SYSCALL_STUB2 GetProcessList, SYS_GET_PROCESS_LIST
|
||||
SYSCALL_STUB2 GetProcessPid, SYS_GET_PROCESS_PID
|
||||
SYSCALL_STUB2 GetThreadList, SYS_GET_THREAD_LIST
|
||||
SYSCALL_STUB2 LoadModule, SYS_LOAD_MODULE
|
||||
SYSCALL_STUB2 UnloadModule, SYS_UNLOAD_MODULE
|
||||
SYSCALL_STUB2 GetModuleInfo, SYS_GET_MODULE_INFO
|
||||
SYSCALL_STUB2 GetModuleExport, SYS_GET_MODULE_EXPORT
|
||||
SYSCALL_STUB2 GetModuleList, SYS_GET_MODULE_LIST
|
||||
SYSCALL_STUB ExitProcess, SYS_EXIT_PROCESS
|
||||
SYSCALL_STUB ExitThread, SYS_EXIT_THREAD
|
||||
SYSCALL_STUB CreateProcessObject, SYS_CREATE_PROCESS_OBJECT
|
||||
SYSCALL_STUB AllocProcessVm, SYS_ALLOC_PROCESS_VM
|
||||
SYSCALL_STUB ProtectProcessVm, SYS_PROTECT_PROCESS_VM
|
||||
SYSCALL_STUB StartProcess, SYS_START_PROCESS
|
||||
SYSCALL_STUB WaitSingle, SYS_WAIT_SINGLE
|
||||
SYSCALL_STUB WaitMany, SYS_WAIT_MANY
|
||||
SYSCALL_STUB OpenInputQueue, SYS_OPEN_INPUT_QUEUE
|
||||
SYSCALL_STUB GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
|
||||
SYSCALL_STUB OpenProcess, SYS_OPEN_PROCESS
|
||||
SYSCALL_STUB KillProcess, SYS_KILL_PROCESS
|
||||
SYSCALL_STUB GetProcessList, SYS_GET_PROCESS_LIST
|
||||
SYSCALL_STUB GetProcessPid, SYS_GET_PROCESS_PID
|
||||
SYSCALL_STUB GetThreadList, SYS_GET_THREAD_LIST
|
||||
|
||||
SYSCALL_STUB2 CreateThread, SYS_CREATE_THREAD
|
||||
SYSCALL_STUB2 ResumeThread, SYS_RESUME_THREAD
|
||||
SYSCALL_STUB2 GetCurrentTid, SYS_GET_CURRENT_TID
|
||||
SYSCALL_STUB2 GetCurrentPid, SYS_GET_CURRENT_PID
|
||||
SYSCALL_STUB2 SetThreadPriority, SYS_SET_THREAD_PRIORITY
|
||||
SYSCALL_STUB2 Yield, SYS_YIELD
|
||||
SYSCALL_STUB2 Sleep, SYS_SLEEP
|
||||
SYSCALL_STUB2 SetUserApcDispatcher, SYS_SET_USER_APC_DISPATCHER
|
||||
SYSCALL_STUB2 ApcReturn, SYS_APC_RETURN
|
||||
SYSCALL_STUB2 SetExceptionDispatcher, SYS_SET_EXCEPTION_DISPATCHER
|
||||
SYSCALL_STUB2 GetExceptionDispatcher, SYS_GET_EXCEPTION_DISPATCHER
|
||||
SYSCALL_STUB2 ContinueException, SYS_CONTINUE_EXCEPTION
|
||||
SYSCALL_STUB2 RaiseException, SYS_RAISE_EXCEPTION
|
||||
SYSCALL_STUB CreateThread, SYS_CREATE_THREAD
|
||||
SYSCALL_STUB ResumeThread, SYS_RESUME_THREAD
|
||||
SYSCALL_STUB GetCurrentTid, SYS_GET_CURRENT_TID
|
||||
SYSCALL_STUB GetCurrentPid, SYS_GET_CURRENT_PID
|
||||
SYSCALL_STUB SetThreadPriority, SYS_SET_THREAD_PRIORITY
|
||||
SYSCALL_STUB Yield, SYS_YIELD
|
||||
SYSCALL_STUB Sleep, SYS_SLEEP
|
||||
SYSCALL_STUB SetUserApcDispatcher, SYS_SET_USER_APC_DISPATCHER
|
||||
SYSCALL_STUB ApcReturn, SYS_APC_RETURN
|
||||
SYSCALL_STUB SetExceptionDispatcher, SYS_SET_EXCEPTION_DISPATCHER
|
||||
SYSCALL_STUB GetExceptionDispatcher, SYS_GET_EXCEPTION_DISPATCHER
|
||||
SYSCALL_STUB ContinueException, SYS_CONTINUE_EXCEPTION
|
||||
SYSCALL_STUB RaiseException, SYS_RAISE_EXCEPTION
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; User APC dispatcher
|
||||
; ---------------------------------------------------------------------------
|
||||
PUBLIC SysUserApcDispatcher
|
||||
IFDEF NTL_SYSCALL_STATIC
|
||||
SysUserApcDispatcher PROC
|
||||
ELSE
|
||||
SysUserApcDispatcher PROC EXPORT
|
||||
ENDIF
|
||||
; Kernel enters here with RSP pointing at SYS_USER_APC_FRAME. There is no
|
||||
; return address because this is an IRET redirection. Preserve RBX because
|
||||
; the original user context resumes after SysApcReturn.
|
||||
@@ -137,152 +104,176 @@ SysUserApcDispatcher ENDP
|
||||
; ---------------------------------------------------------------------------
|
||||
; Virtual memory and handles
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 AllocVm, SYS_ALLOC_VM
|
||||
SYSCALL_STUB2 FreeVm, SYS_FREE_VM
|
||||
SYSCALL_STUB2 ProtectVm, SYS_PROTECT_VM
|
||||
SYSCALL_STUB2 QueryVm, SYS_QUERY_VM
|
||||
SYSCALL_STUB2 CloseHandle, SYS_CLOSE_HANDLE
|
||||
SYSCALL_STUB2 DuplicateHandle, SYS_DUPLICATE_HANDLE
|
||||
SYSCALL_STUB AllocVm, SYS_ALLOC_VM
|
||||
SYSCALL_STUB FreeVm, SYS_FREE_VM
|
||||
SYSCALL_STUB ProtectVm, SYS_PROTECT_VM
|
||||
SYSCALL_STUB QueryVm, SYS_QUERY_VM
|
||||
SYSCALL_STUB CloseHandle, SYS_CLOSE_HANDLE
|
||||
SYSCALL_STUB DuplicateHandle, SYS_DUPLICATE_HANDLE
|
||||
SYSCALL_STUB DeriveHandle, SYS_DERIVE_HANDLE
|
||||
SYSCALL_STUB RevokeHandle, SYS_REVOKE_HANDLE
|
||||
SYSCALL_STUB DeriveSecurityContext, SYS_DERIVE_SECURITY_CONTEXT
|
||||
SYSCALL_STUB QuerySecurityContext, SYS_QUERY_SECURITY_CONTEXT
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Synchronization objects
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 CreateMutex, SYS_CREATE_MUTEX
|
||||
SYSCALL_STUB2 TryAcquireMutex, SYS_TRY_ACQUIRE_MUTEX
|
||||
SYSCALL_STUB2 ReleaseMutex, SYS_RELEASE_MUTEX
|
||||
SYSCALL_STUB CreateMutex, SYS_CREATE_MUTEX
|
||||
SYSCALL_STUB TryAcquireMutex, SYS_TRY_ACQUIRE_MUTEX
|
||||
SYSCALL_STUB ReleaseMutex, SYS_RELEASE_MUTEX
|
||||
|
||||
SYSCALL_STUB2 CreateSem, SYS_CREATE_SEM
|
||||
SYSCALL_STUB2 TryWaitSem, SYS_TRY_WAIT_SEM
|
||||
SYSCALL_STUB2 SignalSem, SYS_SIGNAL_SEM
|
||||
SYSCALL_STUB CreateSem, SYS_CREATE_SEM
|
||||
SYSCALL_STUB TryWaitSem, SYS_TRY_WAIT_SEM
|
||||
SYSCALL_STUB SignalSem, SYS_SIGNAL_SEM
|
||||
|
||||
SYSCALL_STUB2 CreateEvent, SYS_CREATE_EVENT
|
||||
SYSCALL_STUB2 SignalEvent, SYS_SIGNAL_EVENT
|
||||
SYSCALL_STUB2 ClearEvent, SYS_CLEAR_EVENT
|
||||
SYSCALL_STUB CreateEvent, SYS_CREATE_EVENT
|
||||
SYSCALL_STUB SignalEvent, SYS_SIGNAL_EVENT
|
||||
SYSCALL_STUB ClearEvent, SYS_CLEAR_EVENT
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Queues and named queues
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 CreateQueue, SYS_CREATE_QUEUE
|
||||
SYSCALL_STUB2 SendQueue, SYS_SEND_QUEUE
|
||||
SYSCALL_STUB2 RecvQueue, SYS_RECV_QUEUE
|
||||
SYSCALL_STUB2 PublishQueue, SYS_PUBLISH_QUEUE
|
||||
SYSCALL_STUB2 UnpublishQueue, SYS_UNPUBLISH_QUEUE
|
||||
SYSCALL_STUB2 OpenNamedQueue, SYS_OPEN_NAMED_QUEUE
|
||||
SYSCALL_STUB2 OpenNamedQueueOwnerProcess, SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS
|
||||
SYSCALL_STUB CreateQueue, SYS_CREATE_QUEUE
|
||||
SYSCALL_STUB SendQueue, SYS_SEND_QUEUE
|
||||
SYSCALL_STUB RecvQueue, SYS_RECV_QUEUE
|
||||
SYSCALL_STUB PublishQueue, SYS_PUBLISH_QUEUE
|
||||
SYSCALL_STUB UnpublishQueue, SYS_UNPUBLISH_QUEUE
|
||||
SYSCALL_STUB OpenNamedQueue, SYS_OPEN_NAMED_QUEUE
|
||||
SYSCALL_STUB OpenNamedQueueOwnerProcess, SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS
|
||||
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Files, directories, and current directory
|
||||
; LPC-like Ports and well-known system services
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 OpenFile, SYS_OPEN_FILE
|
||||
SYSCALL_STUB2 ReadFile, SYS_READ_FILE
|
||||
SYSCALL_STUB2 WriteFile, SYS_WRITE_FILE
|
||||
SYSCALL_STUB2 ReadFileAsync, SYS_READ_FILE_ASYNC
|
||||
SYSCALL_STUB2 WriteFileAsync, SYS_WRITE_FILE_ASYNC
|
||||
SYSCALL_STUB2 CancelIo, SYS_CANCEL_IO
|
||||
SYSCALL_STUB2 SeekFile, SYS_SEEK_FILE
|
||||
SYSCALL_STUB2 GetFilePos, SYS_GET_FILE_POS
|
||||
SYSCALL_STUB2 GetFileSize, SYS_GET_FILE_SIZE
|
||||
SYSCALL_STUB2 GetFileStat, SYS_GET_FILE_STAT
|
||||
SYSCALL_STUB2 StatFile, SYS_STAT_FILE
|
||||
SYSCALL_STUB2 TruncateFile, SYS_TRUNCATE_FILE
|
||||
SYSCALL_STUB2 FlushFile, SYS_FLUSH_FILE
|
||||
SYSCALL_STUB2 DeleteFile, SYS_DELETE_FILE
|
||||
SYSCALL_STUB2 RenameFile, SYS_RENAME_FILE
|
||||
SYSCALL_STUB2 LockFile, SYS_LOCK_FILE
|
||||
SYSCALL_STUB2 UnlockFile, SYS_UNLOCK_FILE
|
||||
SYSCALL_STUB CreatePort, SYS_CREATE_PORT
|
||||
SYSCALL_STUB ConnectPort, SYS_CONNECT_PORT
|
||||
SYSCALL_STUB AcceptPort, SYS_ACCEPT_PORT
|
||||
SYSCALL_STUB SendPort, SYS_SEND_PORT
|
||||
SYSCALL_STUB ReceivePort, SYS_RECEIVE_PORT
|
||||
SYSCALL_STUB CallPort, SYS_CALL_PORT
|
||||
SYSCALL_STUB ReplyPort, SYS_REPLY_PORT
|
||||
SYSCALL_STUB DisconnectPort, SYS_DISCONNECT_PORT
|
||||
SYSCALL_STUB RegisterSystemService, SYS_REGISTER_SYSTEM_SERVICE
|
||||
SYSCALL_STUB UnregisterSystemService, SYS_UNREGISTER_SYSTEM_SERVICE
|
||||
SYSCALL_STUB ConnectSystemService, SYS_CONNECT_SYSTEM_SERVICE
|
||||
|
||||
SYSCALL_STUB2 OpenDir, SYS_OPEN_DIR
|
||||
SYSCALL_STUB2 ReadDir, SYS_READ_DIR
|
||||
SYSCALL_STUB2 CreateDir, SYS_CREATE_DIR
|
||||
SYSCALL_STUB2 RemoveDir, SYS_REMOVE_DIR
|
||||
SYSCALL_STUB2 SetCwd, SYS_SET_CWD
|
||||
SYSCALL_STUB2 RealPath, SYS_REAL_PATH
|
||||
; ---------------------------------------------------------------------------
|
||||
; Mounted-volume manager control plane
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB QueryVolumes, SYS_QUERY_VOLUMES
|
||||
SYSCALL_STUB OpenVolume, SYS_OPEN_VOLUME
|
||||
SYSCALL_STUB WaitVolumeChange, SYS_WAIT_VOLUME_CHANGE
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Files and directories
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB OpenFileAt, SYS_OPEN_FILE_AT
|
||||
SYSCALL_STUB OpenDirAt, SYS_OPEN_DIR_AT
|
||||
SYSCALL_STUB StatAt, SYS_STAT_AT
|
||||
SYSCALL_STUB DeleteAt, SYS_DELETE_AT
|
||||
SYSCALL_STUB RenameAt, SYS_RENAME_AT
|
||||
SYSCALL_STUB CreateDirAt, SYS_CREATE_DIR_AT
|
||||
SYSCALL_STUB RemoveDirAt, SYS_REMOVE_DIR_AT
|
||||
SYSCALL_STUB ReadFile, SYS_READ_FILE
|
||||
SYSCALL_STUB WriteFile, SYS_WRITE_FILE
|
||||
SYSCALL_STUB ReadFileAsync, SYS_READ_FILE_ASYNC
|
||||
SYSCALL_STUB WriteFileAsync, SYS_WRITE_FILE_ASYNC
|
||||
SYSCALL_STUB CancelIo, SYS_CANCEL_IO
|
||||
SYSCALL_STUB SeekFile, SYS_SEEK_FILE
|
||||
SYSCALL_STUB GetFilePos, SYS_GET_FILE_POS
|
||||
SYSCALL_STUB GetFileSize, SYS_GET_FILE_SIZE
|
||||
SYSCALL_STUB GetFileStat, SYS_GET_FILE_STAT
|
||||
SYSCALL_STUB TruncateFile, SYS_TRUNCATE_FILE
|
||||
SYSCALL_STUB FlushFile, SYS_FLUSH_FILE
|
||||
SYSCALL_STUB LockFile, SYS_LOCK_FILE
|
||||
SYSCALL_STUB UnlockFile, SYS_UNLOCK_FILE
|
||||
|
||||
SYSCALL_STUB ReadDir, SYS_READ_DIR
|
||||
; ---------------------------------------------------------------------------
|
||||
; Console and graphics
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 WriteConsole, SYS_WRITE_CONSOLE
|
||||
SYSCALL_STUB2 GetConsoleInfo, SYS_GET_CONSOLE_INFO
|
||||
SYSCALL_STUB2 SetConsoleCursor, SYS_SET_CONSOLE_CURSOR
|
||||
SYSCALL_STUB6 FillConsoleCharacter, SYS_FILL_CONSOLE_CHARACTER
|
||||
SYSCALL_STUB6 FillConsoleAttribute, SYS_FILL_CONSOLE_ATTRIBUTE
|
||||
SYSCALL_STUB6 PutScreenChar, SYS_PUT_SCREEN_CHAR
|
||||
SYSCALL_STUB2 CreateSurface, SYS_CREATE_SURFACE
|
||||
SYSCALL_STUB2 MapSurface, SYS_MAP_SURFACE
|
||||
SYSCALL_STUB2 QuerySurface, SYS_QUERY_SURFACE
|
||||
SYSCALL_STUB2 PresentSurface, SYS_PRESENT_SURFACE
|
||||
SYSCALL_STUB2 ClearScreen, SYS_CLEAR_SCREEN
|
||||
SYSCALL_STUB2 GetScreenSize, SYS_GET_SCREEN_SIZE
|
||||
SYSCALL_STUB2 QueryDisplayInfo, SYS_QUERY_DISPLAY_INFO
|
||||
SYSCALL_STUB2 OpenDisplayClock, SYS_OPEN_DISPLAY_CLOCK
|
||||
SYSCALL_STUB WriteConsole, SYS_WRITE_CONSOLE
|
||||
SYSCALL_STUB GetConsoleInfo, SYS_GET_CONSOLE_INFO
|
||||
SYSCALL_STUB SetConsoleCursor, SYS_SET_CONSOLE_CURSOR
|
||||
SYSCALL_STUB FillConsoleCharacter, SYS_FILL_CONSOLE_CHARACTER
|
||||
SYSCALL_STUB FillConsoleAttribute, SYS_FILL_CONSOLE_ATTRIBUTE
|
||||
SYSCALL_STUB PutScreenChar, SYS_PUT_SCREEN_CHAR
|
||||
SYSCALL_STUB CreateSurface, SYS_CREATE_SURFACE
|
||||
SYSCALL_STUB MapSurface, SYS_MAP_SURFACE
|
||||
SYSCALL_STUB QuerySurface, SYS_QUERY_SURFACE
|
||||
SYSCALL_STUB PresentSurface, SYS_PRESENT_SURFACE
|
||||
SYSCALL_STUB ClearScreen, SYS_CLEAR_SCREEN
|
||||
SYSCALL_STUB GetScreenSize, SYS_GET_SCREEN_SIZE
|
||||
SYSCALL_STUB QueryDisplayInfo, SYS_QUERY_DISPLAY_INFO
|
||||
SYSCALL_STUB OpenDisplayClock, SYS_OPEN_DISPLAY_CLOCK
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Native render audio
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 AudioEnumerateEndpoints, SYS_AUDIO_ENUMERATE_ENDPOINTS
|
||||
SYSCALL_STUB2 AudioOpenStream, SYS_AUDIO_OPEN_STREAM
|
||||
SYSCALL_STUB2 AudioWrite, SYS_AUDIO_WRITE
|
||||
SYSCALL_STUB2 AudioArmWait, SYS_AUDIO_ARM_WAIT
|
||||
SYSCALL_STUB2 AudioQueryStream, SYS_AUDIO_QUERY_STREAM
|
||||
SYSCALL_STUB2 AudioStart, SYS_AUDIO_START
|
||||
SYSCALL_STUB2 AudioPause, SYS_AUDIO_PAUSE
|
||||
SYSCALL_STUB2 AudioResume, SYS_AUDIO_RESUME
|
||||
SYSCALL_STUB2 AudioDrain, SYS_AUDIO_DRAIN
|
||||
SYSCALL_STUB2 AudioStop, SYS_AUDIO_STOP
|
||||
SYSCALL_STUB2 AudioReset, SYS_AUDIO_RESET
|
||||
SYSCALL_STUB2 AudioSetVolume, SYS_AUDIO_SET_VOLUME
|
||||
SYSCALL_STUB2 AudioSetMasterVolume, SYS_AUDIO_SET_MASTER_VOLUME
|
||||
SYSCALL_STUB AudioEnumerateEndpoints, SYS_AUDIO_ENUMERATE_ENDPOINTS
|
||||
SYSCALL_STUB AudioOpenStream, SYS_AUDIO_OPEN_STREAM
|
||||
SYSCALL_STUB AudioWrite, SYS_AUDIO_WRITE
|
||||
SYSCALL_STUB AudioArmWait, SYS_AUDIO_ARM_WAIT
|
||||
SYSCALL_STUB AudioQueryStream, SYS_AUDIO_QUERY_STREAM
|
||||
SYSCALL_STUB AudioStart, SYS_AUDIO_START
|
||||
SYSCALL_STUB AudioPause, SYS_AUDIO_PAUSE
|
||||
SYSCALL_STUB AudioResume, SYS_AUDIO_RESUME
|
||||
SYSCALL_STUB AudioDrain, SYS_AUDIO_DRAIN
|
||||
SYSCALL_STUB AudioStop, SYS_AUDIO_STOP
|
||||
SYSCALL_STUB AudioReset, SYS_AUDIO_RESET
|
||||
SYSCALL_STUB AudioSetVolume, SYS_AUDIO_SET_VOLUME
|
||||
SYSCALL_STUB AudioSetMasterVolume, SYS_AUDIO_SET_MASTER_VOLUME
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Input
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 PollKeyboard, SYS_POLL_KEYBOARD
|
||||
SYSCALL_STUB2 IsKeyDown, SYS_IS_KEY_DOWN
|
||||
SYSCALL_STUB2 ReadKeyboardChar, SYS_READ_KEYBOARD_CHAR
|
||||
SYSCALL_STUB2 ReadKey, SYS_READ_KEY
|
||||
SYSCALL_STUB2 GetKeyModifiers, SYS_GET_KEY_MODIFIERS
|
||||
SYSCALL_STUB2 QueryGlobalInputState, SYS_QUERY_GLOBAL_INPUT_STATE
|
||||
SYSCALL_STUB2 ReadInputEvent, SYS_READ_INPUT_EVENT
|
||||
SYSCALL_STUB2 PeekInputEvent, SYS_PEEK_INPUT_EVENT
|
||||
SYSCALL_STUB PollKeyboard, SYS_POLL_KEYBOARD
|
||||
SYSCALL_STUB IsKeyDown, SYS_IS_KEY_DOWN
|
||||
SYSCALL_STUB ReadKeyboardChar, SYS_READ_KEYBOARD_CHAR
|
||||
SYSCALL_STUB ReadKey, SYS_READ_KEY
|
||||
SYSCALL_STUB GetKeyModifiers, SYS_GET_KEY_MODIFIERS
|
||||
SYSCALL_STUB QueryGlobalInputState, SYS_QUERY_GLOBAL_INPUT_STATE
|
||||
SYSCALL_STUB ReadInputEvent, SYS_READ_INPUT_EVENT
|
||||
SYSCALL_STUB PeekInputEvent, SYS_PEEK_INPUT_EVENT
|
||||
|
||||
SYSCALL_STUB2 PeekMouse, SYS_PEEK_MOUSE
|
||||
SYSCALL_STUB2 ReadMouse, SYS_READ_MOUSE
|
||||
SYSCALL_STUB2 GetMousePos, SYS_GET_MOUSE_POS
|
||||
SYSCALL_STUB PeekMouse, SYS_PEEK_MOUSE
|
||||
SYSCALL_STUB ReadMouse, SYS_READ_MOUSE
|
||||
SYSCALL_STUB GetMousePos, SYS_GET_MOUSE_POS
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Time and debug
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 CreateTimer, SYS_CREATE_TIMER
|
||||
SYSCALL_STUB2 SetTimer, SYS_SET_TIMER
|
||||
SYSCALL_STUB2 CancelTimer, SYS_CANCEL_TIMER
|
||||
SYSCALL_STUB2 GetTick, SYS_GET_TICK
|
||||
SYSCALL_STUB2 GetTickFrequency, SYS_GET_TICK_FREQUENCY
|
||||
SYSCALL_STUB2 PrintDebug, SYS_PRINT_DEBUG
|
||||
SYSCALL_STUB2 QueryTrapFrame, SYS_QUERY_TRAP_FRAME
|
||||
SYSCALL_STUB CreateTimer, SYS_CREATE_TIMER
|
||||
SYSCALL_STUB SetTimer, SYS_SET_TIMER
|
||||
SYSCALL_STUB CancelTimer, SYS_CANCEL_TIMER
|
||||
SYSCALL_STUB GetTick, SYS_GET_TICK
|
||||
SYSCALL_STUB GetTickFrequency, SYS_GET_TICK_FREQUENCY
|
||||
SYSCALL_STUB PrintDebug, SYS_PRINT_DEBUG
|
||||
SYSCALL_STUB QueryTrapFrame, SYS_QUERY_TRAP_FRAME
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Native sockets
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 CreateSocket, SYS_CREATE_SOCKET
|
||||
SYSCALL_STUB2 SocketIoAsync, SYS_SOCKET_IO_ASYNC
|
||||
SYSCALL_STUB2 BindSocket, SYS_BIND_SOCKET
|
||||
SYSCALL_STUB2 ListenSocket, SYS_LISTEN_SOCKET
|
||||
SYSCALL_STUB2 AcceptSocket, SYS_ACCEPT_SOCKET
|
||||
SYSCALL_STUB2 ConnectSocket, SYS_CONNECT_SOCKET
|
||||
SYSCALL_STUB6 SendSocket, SYS_SEND_SOCKET
|
||||
SYSCALL_STUB6 RecvSocket, SYS_RECV_SOCKET
|
||||
SYSCALL_STUB2 ShutdownSocket, SYS_SHUTDOWN_SOCKET
|
||||
SYSCALL_STUB2 SendToSocket, SYS_SEND_TO_SOCKET
|
||||
SYSCALL_STUB2 RecvFromSocket, SYS_RECV_FROM_SOCKET
|
||||
SYSCALL_STUB2 ResolveHost, SYS_RESOLVE_HOST
|
||||
SYSCALL_STUB2 SetSocketOption, SYS_SET_SOCKET_OPTION
|
||||
SYSCALL_STUB2 GetSocketOption, SYS_GET_SOCKET_OPTION
|
||||
SYSCALL_STUB2 Poll, SYS_POLL
|
||||
SYSCALL_STUB CreateSocket, SYS_CREATE_SOCKET
|
||||
SYSCALL_STUB SocketIoAsync, SYS_SOCKET_IO_ASYNC
|
||||
SYSCALL_STUB BindSocket, SYS_BIND_SOCKET
|
||||
SYSCALL_STUB ListenSocket, SYS_LISTEN_SOCKET
|
||||
SYSCALL_STUB AcceptSocket, SYS_ACCEPT_SOCKET
|
||||
SYSCALL_STUB ConnectSocket, SYS_CONNECT_SOCKET
|
||||
SYSCALL_STUB SendSocket, SYS_SEND_SOCKET
|
||||
SYSCALL_STUB RecvSocket, SYS_RECV_SOCKET
|
||||
SYSCALL_STUB ShutdownSocket, SYS_SHUTDOWN_SOCKET
|
||||
SYSCALL_STUB SendToSocket, SYS_SEND_TO_SOCKET
|
||||
SYSCALL_STUB RecvFromSocket, SYS_RECV_FROM_SOCKET
|
||||
SYSCALL_STUB ResolveHost, SYS_RESOLVE_HOST
|
||||
SYSCALL_STUB SetSocketOption, SYS_SET_SOCKET_OPTION
|
||||
SYSCALL_STUB GetSocketOption, SYS_GET_SOCKET_OPTION
|
||||
SYSCALL_STUB Poll, SYS_POLL
|
||||
|
||||
SYSCALL_STUB2 GetMemoryStatus, SYS_GET_MEMORY_STATUS
|
||||
SYSCALL_STUB2 QueryKernelStats, SYS_QUERY_KERNEL_STATS
|
||||
SYSCALL_STUB2 GetSystemTime, SYS_GET_SYSTEM_TIME
|
||||
SYSCALL_STUB5 CreateFileMapping, SYS_CREATE_FILE_MAPPING
|
||||
SYSCALL_STUB6 MapFile, SYS_MAP_FILE
|
||||
SYSCALL_STUB GetMemoryStatus, SYS_GET_MEMORY_STATUS
|
||||
SYSCALL_STUB QueryKernelStats, SYS_QUERY_KERNEL_STATS
|
||||
SYSCALL_STUB GetSystemTime, SYS_GET_SYSTEM_TIME
|
||||
SYSCALL_STUB CreateFileMapping, SYS_CREATE_FILE_MAPPING
|
||||
SYSCALL_STUB MapFile, SYS_MAP_FILE
|
||||
|
||||
END
|
||||
Reference in new issue
Block a user