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:
maksw committed 2026-08-20 12:33:17 +02:00
1 parent 4add6789ba
commit f882d339fb
232 files changed
+20934 -8898

No files matched your search

+796
View File
@@ -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 = &image;
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(&region, 0, sizeof(region));
status = SysQueryVm((void*)peb->LoaderData, &region);
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(&region, 0, sizeof(region));
KSTATUS status = SysQueryVm((void*)peb->LoaderData, &region);
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 = &image;
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);
+499
View File
@@ -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;
}
+408
View File
@@ -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;
}
+232
View File
@@ -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;
}
+308
View File
@@ -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
View File
@@ -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
View File
@@ -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