// Kernel syscall dispatcher. This file validates user requests and bridges user-mode ABI calls into kernel services. // KeSyscallEntry (ke_syscall.asm) handles the INT 0x40 ring-3 -> ring-0 // transition, re-orders the arguments to match MS x64, and calls // KeSyscallDispatch with the syscall number plus at most four register arguments. // Compound operations use size-tagged request structures instead of extending // the register ABI. // Return value (RAX) is a KSTATUS code *or* an arbitrary 64-bit datum // (pointer, size, tick count, etc.) depending on the call; conventions are // documented in syscall_nr.h. // Unified handle design // All kernel objects (FILE_OBJECT, MUTEX, SEMAPHORE, EVENT, QUEUE) are // reference-counted OBJECTs stored in the per-process handle table. // SYS_CLOSE_HANDLE is the single close path for every type: // KeCloseHandle(proc, handle) // -> ObDereferenceObject(obj) // -> obj->Destroy(obj) [type-specific cleanup, e.g. KiFileDestroy] // -> MmFreePool(obj) // There is no separate SyscallCloseFile. Removing that stub was the right // call: the object model already handled it; the dedicated syscall was only // adding a footgun where callers could bypass the unified refcount path. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PROCESS_LAUNCH_DEBUG #define PROCESS_LAUNCH_DEBUG 0 #endif #if PROCESS_LAUNCH_DEBUG #define ProcDbg(...) DbgPrint(__VA_ARGS__) #else #define ProcDbg(...) ((void)0) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // HAL pointer - set once at boot by KeSyscallSetHal(). static const HAL_API* gHal; typedef struct SYS_IO_STATUS_BLOCK_K { volatile KSTATUS Status; volatile uint64_t Information; } SYS_IO_STATUS_BLOCK_K; typedef void (*SYS_USER_APC_ROUTINE_K)(void* normalContext, SYS_IO_STATUS_BLOCK_K* iosb, uint64_t information); typedef struct SYS_ASYNC_FILE_REQUEST_K { uint32_t Size; SYS_HANDLE FileHandle; SYS_HANDLE EventHandle; uint32_t Flags; uint64_t Offset; void* Buffer; size_t Length; SYS_IO_STATUS_BLOCK_K* Iosb; SYS_USER_APC_ROUTINE_K ApcRoutine; void* ApcContext; SYS_HANDLE* RequestHandleOut; // optional cancellation/query handle output } SYS_ASYNC_FILE_REQUEST_K; _Static_assert(sizeof(SYS_ASYNC_FILE_REQUEST_K) == sizeof(SYS_ASYNC_FILE_REQUEST), "async file request ABI mismatch"); typedef struct SYS_USER_ASYNC_APC_CONTEXT { KAPC KernelApc; THREAD* TargetThread; SYS_USER_APC_ROUTINE_K UserRoutine; void* UserContext; } SYS_USER_ASYNC_APC_CONTEXT; typedef struct FILE_MAPPING_OBJECT { OBJECT Header; FILE_OBJECT* File; uint64_t MaximumSize; uint64_t PageFlags; uint32_t Protect; uint32_t Flags; } FILE_MAPPING_OBJECT; static void KiFileMappingDestroy(OBJECT* obj) { FILE_MAPPING_OBJECT* mapping = CONTAINING_RECORD(obj, FILE_MAPPING_OBJECT, Header); if (mapping->File) { ObDereferenceObject(&mapping->File->Header); mapping->File = (FILE_OBJECT*)0; } } // Implements the syscall Init operation. void KeSyscallInit(const HAL_API* hal) { gHal = hal; } // Requires one ambient privilege from the calling process's immutable security context. static KSTATUS KiRequirePrivilege(SYS_PRIVILEGES privilege) { PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; return SeProcessHasPrivilege(process, privilege) ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED; } // Resolves one current-process handle while preserving the exact lookup status. static OBJECT* KiHandleToObject(SYS_HANDLE handle, OBJECT_TYPE expected, SYS_RIGHTS requiredRights, KSTATUS* statusOut) { if (!statusOut) return (OBJECT*)0; *statusOut = NTLSTATUS_FAILURE; PROCESS* process = KeGetCurrentProcess(); if (!process) return (OBJECT*)0; OBJECT* object = (OBJECT*)0; *statusOut = KeReferenceHandle(process, handle, expected, requiredRights, &object); return KSUCCESS(*statusOut) ? object : (OBJECT*)0; } // Helper: allocate a handle in the current process's table for 'obj'. // Takes one reference on behalf of the handle slot. // On failure, drops the reference it would have stored. static KSTATUS KiAllocHandleForCurrentWithRights(OBJECT* obj, SYS_RIGHTS rights, SYS_HANDLE* handleOut) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) { ObDereferenceObject(obj); return NTLSTATUS_FAILURE; } KSTATUS s = KeCreateHandle(proc, obj, rights, handleOut); // Most syscall create/open paths receive a fresh creator reference from the // subsystem. The root capability grant takes its own reference; this helper always // drops the creator reference so the object is owned only by the returned // handle (and any subsystem-internal refs). ObDereferenceObject(obj); return s; } #define SYSCALL_STRING_MAX SYS_PATH_MAX #define SYSCALL_TEXT_MAX_BYTES (64ULL * 1024ULL) #define SYSCALL_IO_MAX_BYTES (16ULL * 1024ULL * 1024ULL) #define SYSCALL_FILE_IO_CHUNK_BYTES (64ULL * 1024ULL) #define SYSCALL_BLIT_MAX_BYTES (16ULL * 1024ULL * 1024ULL) // Copies a request only after its leading size field matches the exact ABI layout. static KSTATUS KiCopySizedUserRequest(const void* userRequest, void* dst, size_t size) { if (!userRequest || !dst || size < sizeof(uint32_t) || size > UINT32_MAX) return NTLSTATUS_INVALID_PARAMETER; uint32_t userSize = 0; KSTATUS status = CopyFromUser(&userSize, userRequest, sizeof(userSize)); if (!KSUCCESS(status)) return status; if (userSize != (uint32_t)size) return NTLSTATUS_INVALID_PARAMETER; return CopyFromUser(dst, userRequest, size); } // Copies one bounded Port capability-transfer array from user memory. static KSTATUS KiImportPortTransfers(const SYS_PORT_HANDLE_TRANSFER* userTransfers, uint32_t count, SYS_PORT_HANDLE_TRANSFER* transfers) { if (count == 0) return NTLSTATUS_SUCCESS; if (!userTransfers || !transfers || count > SYS_PORT_MAX_TRANSFER_HANDLES) return NTLSTATUS_INVALID_PARAMETER; return CopyFromUser(transfers, userTransfers, sizeof(*transfers) * count); } // Validates the leading Size of a caller-owned output structure before writing it. static KSTATUS KiProbeSizedUserOutput(void* userOutput, size_t size) { if (!userOutput || size < sizeof(uint32_t) || size > UINT32_MAX) return NTLSTATUS_INVALID_PARAMETER; uint32_t userSize = 0; KSTATUS status = CopyFromUser(&userSize, userOutput, sizeof(userSize)); if (!KSUCCESS(status)) return status; if (userSize != (uint32_t)size) return NTLSTATUS_INVALID_PARAMETER; return MmProbeUserWrite(userOutput, size); } static KSTATUS KiImportUserStringView(SYS_STRING_VIEW userView, char* dst, size_t dstSize, SYS_STRING_VIEW* out, bool nulTerminate) { if (!out) return NTLSTATUS_INVALID_PARAMETER; *out = SysSvFromParts((const char*)0, 0); if (userView.Length == 0) { if (nulTerminate) { if (!dst || dstSize == 0) return NTLSTATUS_BUFFER_TOO_SMALL; dst[0] = '\0'; } return NTLSTATUS_SUCCESS; } if (!userView.Data || !dst) return NTLSTATUS_INVALID_PARAMETER; if (userView.Length > SYSCALL_STRING_MAX) return NTLSTATUS_BUFFER_TOO_SMALL; size_t needed = userView.Length + (nulTerminate ? 1 : 0); if (needed < userView.Length || needed > dstSize) return NTLSTATUS_BUFFER_TOO_SMALL; KSTATUS s = CopyFromUser(dst, userView.Data, userView.Length); if (!KSUCCESS(s)) return s; if (nulTerminate) dst[userView.Length] = '\0'; *out = SysSvFromParts(dst, userView.Length); return NTLSTATUS_SUCCESS; } static KSTATUS KiImportUserTextView(SYS_STRING_VIEW userView, char* dst, size_t dstSize, SYS_STRING_VIEW* out, bool nulTerminate) { KSTATUS s = KiImportUserStringView(userView, dst, dstSize, out, nulTerminate); if (!KSUCCESS(s)) return s; if (!out || !SysSvIsUtf8Text(*out)) return NTLSTATUS_INVALID_PARAMETER; return NTLSTATUS_SUCCESS; } // Copies handle To User. static KSTATUS KiCopyHandleToUser(SYS_HANDLE* handleOut, SYS_HANDLE value) { if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; return CopyToUser(handleOut, &value, sizeof(value)); } // Copies u32 To User. static KSTATUS KiCopyU32ToUser(uint32_t* valueOut, uint32_t value) { if (!valueOut) return NTLSTATUS_INVALID_PARAMETER; return CopyToUser(valueOut, &value, sizeof(value)); } // Copies size To User. static KSTATUS KiCopySizeToUser(size_t* valueOut, size_t value) { if (!valueOut) return NTLSTATUS_INVALID_PARAMETER; return CopyToUser(valueOut, &value, sizeof(value)); } // Copies u64 To User. static KSTATUS KiCopyU64ToUser(uint64_t* valueOut, uint64_t value) { if (!valueOut) return NTLSTATUS_INVALID_PARAMETER; return CopyToUser(valueOut, &value, sizeof(value)); } // Copies int To User. static KSTATUS KiCopyIntToUser(int* valueOut, int value) { if (!valueOut) return NTLSTATUS_INVALID_PARAMETER; return CopyToUser(valueOut, &value, sizeof(value)); } // Copies one signed 32-bit integer to user memory. static KSTATUS KiCopyInt32ToUser(int32_t* valueOut, int32_t value) { if (!valueOut) return NTLSTATUS_INVALID_PARAMETER; return CopyToUser(valueOut, &value, sizeof(value)); } // Implements the mul Size operation. static bool KiMulSize(size_t a, size_t b, size_t* out) { if (!out) return false; if (a && b > ((size_t)-1) / a) return false; *out = a * b; return true; } static size_t KiAlignUpSize(size_t value, size_t alignment) { if (!alignment) return value; size_t mask = alignment - 1; return (value + mask) & ~mask; } // Individual syscall handlers. static uint64_t SyscallOpenPowerEvent(SYS_HANDLE* handleOut) { if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS st = MmProbeUserWrite(handleOut, sizeof(*handleOut)); if (!KSUCCESS(st)) return st; PROCESS* caller = KeGetCurrentProcess(); EVENT* event = KePowerRequestEvent(caller); if (!event) return NTLSTATUS_ACCESS_DENIED; SYS_HANDLE handle = SYS_HANDLE_INVALID; st = KiAllocHandleForCurrentWithRights(&event->Header, SYS_RIGHT_WAIT | SYS_RIGHT_QUERY, &handle); if (!KSUCCESS(st)) return st; st = KiCopyHandleToUser(handleOut, handle); if (!KSUCCESS(st)) (void)KeCloseHandle(caller, handle); return st; } static uint64_t SyscallRequestSystemPower(uint32_t action) { KSTATUS st = KiRequirePrivilege(SYS_PRIV_SYSTEM_POWER); return KSUCCESS(st) ? KePowerRequest(action) : st; } static uint64_t SyscallQuerySystemPower(uint32_t* actionOut) { if (!actionOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS st = MmProbeUserWrite(actionOut, sizeof(*actionOut)); if (!KSUCCESS(st)) return st; uint32_t action = 0; st = KePowerGetRequest(KeGetCurrentProcess(), &action); return KSUCCESS(st) ? KiCopyU32ToUser(actionOut, action) : st; } static uint64_t SyscallCompleteSystemPower(uint32_t action) { return KePowerComplete(KeGetCurrentProcess(), action); } // Implements SYS_EXIT_PROCESS. static uint64_t SyscallExitProcess(int32_t exitCode) { THREAD* t = KeGetCurrentThread(); PROCESS* proc = t ? t->Owner : NULL; if (!proc || !t) return NTLSTATUS_FAILURE; KSTATUS s = KeExitProcess(proc, exitCode); if (!KSUCCESS(s)) return s; KeExitThread(t, exitCode); while (true) ; // unreachable - KeExitThread does not return } // Implements SYS_EXIT_THREAD. static uint64_t SyscallExitThread(int32_t exitCode) { THREAD* t = KeGetCurrentThread(); if (t) KeExitThread(t, (int)exitCode); while (true); } // Implements SYS_CREATE_THREAD. // Creates a user thread and optionally returns both its waitable handle and // diagnostic TID. Suspended creation leaves the thread off every ready queue // until SYS_RESUME_THREAD decrements its initial suspend count. static uint64_t SyscallCreateThread(const SYS_CREATE_THREAD_REQUEST* request) { SYS_CREATE_THREAD_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; if (!req.Entry) return NTLSTATUS_INVALID_PARAMETER; if (req.Priority >= PriorityCount) return NTLSTATUS_INVALID_PARAMETER; if (req.Flags & ~SYS_CREATE_THREAD_FLAG_SUSPENDED) return NTLSTATUS_INVALID_PARAMETER; // Running threads may be detached: callers that do not need a handle or TID // can leave both outputs null. A suspended thread still requires a handle so // there is a capability with which to resume or cancel it. if ((req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED) && !req.ThreadHandleOut) return NTLSTATUS_INVALID_PARAMETER; s = MmProbeUserRead((const void*)req.Entry, 1); if (!KSUCCESS(s)) return s; if (req.ThreadHandleOut) { s = MmProbeUserWrite(req.ThreadHandleOut, sizeof(*req.ThreadHandleOut)); if (!KSUCCESS(s)) return s; } if (req.ThreadIdOut) { s = MmProbeUserWrite(req.ThreadIdOut, sizeof(*req.ThreadIdOut)); if (!KSUCCESS(s)) return s; } THREAD* newt = NULL; KE_THREAD_CREATE_PARAMETERS threadParameters = KE_THREAD_CREATE_NORMAL(req.Priority); if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED) threadParameters.Flags |= KE_THREAD_CREATE_FLAG_SUSPENDED; s = KeCreateThread(proc, req.Entry, req.Argument, &threadParameters, &newt); if (!KSUCCESS(s)) return s; SYS_HANDLE threadHandle = SYS_HANDLE_INVALID; if (req.ThreadHandleOut) { s = KeCreateHandle(proc, &newt->Header, KeGetValidHandleRights(ObjectTypeThread), &threadHandle); if (!KSUCCESS(s)) { if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED) (void)KeCancelSuspendedThread(newt); ObDereferenceObject(&newt->Header); return s; } } if (req.ThreadHandleOut) { s = KiCopyHandleToUser(req.ThreadHandleOut, threadHandle); if (!KSUCCESS(s)) { KeCloseHandle(proc, threadHandle); if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED) (void)KeCancelSuspendedThread(newt); ObDereferenceObject(&newt->Header); return s; } } if (req.ThreadIdOut) { s = KiCopyU32ToUser(req.ThreadIdOut, newt->ThreadId); if (!KSUCCESS(s)) { if (threadHandle) KeCloseHandle(proc, threadHandle); if (req.Flags & SYS_CREATE_THREAD_FLAG_SUSPENDED) (void)KeCancelSuspendedThread(newt); ObDereferenceObject(&newt->Header); return s; } } ObDereferenceObject(&newt->Header); return NTLSTATUS_SUCCESS; } // Implements SYS_OPEN_CURRENT_THREAD. static uint64_t SyscallOpenCurrentThread(SYS_RIGHTS desiredAccess, SYS_HANDLE* handleOut) { if (!handleOut || desiredAccess == 0) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS validAccess = KeGetValidHandleRights(ObjectTypeThread); if ((desiredAccess & ~validAccess) != 0) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(handleOut, sizeof(SYS_HANDLE)); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); THREAD* thread = KeGetCurrentThread(); if (!process || !thread) return NTLSTATUS_FAILURE; SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KeCreateHandle(process, &thread->Header, desiredAccess, &handle); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(handleOut, handle); if (!KSUCCESS(status)) { KeCloseHandle(process, handle); return status; } return NTLSTATUS_SUCCESS; } // Implements SYS_RESUME_THREAD. static uint64_t SyscallResumeThread(SYS_HANDLE threadHandle, uint32_t* previousSuspendCountOut) { if (!threadHandle || !previousSuspendCountOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(previousSuspendCountOut, sizeof(uint32_t)); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; OBJECT* object = (OBJECT*)0; KSTATUS handleStatus = KeReferenceHandle(process, threadHandle, ObjectTypeThread, SYS_THREAD_RIGHT_RESUME, &object); if (!KSUCCESS(handleStatus)) return handleStatus; THREAD* thread = CONTAINING_RECORD(object, THREAD, Header); uint32_t previousSuspendCount = 0; status = KeResumeThread(thread, &previousSuspendCount); ObDereferenceObject(object); if (!KSUCCESS(status)) return status; return KiCopyU32ToUser(previousSuspendCountOut, previousSuspendCount); } // Implements SYS_RESUME_THREAD_AND_WAIT. // Resumes one suspended thread and blocks until that same thread exits without // returning to userspace between the resume and wait operations. static uint64_t SyscallResumeThreadAndWait(SYS_HANDLE threadHandle, uint32_t* previousSuspendCountOut) { if (!threadHandle || !previousSuspendCountOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(previousSuspendCountOut, sizeof(uint32_t)); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); THREAD* currentThread = KeGetCurrentThread(); if (!process || !currentThread) return NTLSTATUS_FAILURE; OBJECT* object = (OBJECT*)0; status = KeReferenceHandle(process, threadHandle, ObjectTypeThread, SYS_THREAD_RIGHT_RESUME | SYS_RIGHT_WAIT, &object); if (!KSUCCESS(status)) return status; THREAD* thread = CONTAINING_RECORD(object, THREAD, Header); if (thread == currentThread) { ObDereferenceObject(object); return NTLSTATUS_INVALID_PARAMETER; } uint32_t previousSuspendCount = 0; status = KeResumeThread(thread, &previousSuspendCount); if (KSUCCESS(status)) status = KeWaitForEvent(thread->ExitEvent, 0); ObDereferenceObject(object); if (!KSUCCESS(status)) return status; return KiCopyU32ToUser(previousSuspendCountOut, previousSuspendCount); } // Implements SYS_GET_THREAD_TID. static uint64_t SyscallGetThreadTid(SYS_HANDLE threadHandle, uint32_t* tidOut) { if (!threadHandle || !tidOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(tidOut, sizeof(uint32_t)); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; OBJECT* object = (OBJECT*)0; status = KeReferenceHandle(process, threadHandle, ObjectTypeThread, SYS_RIGHT_QUERY, &object); if (!KSUCCESS(status)) return status; THREAD* thread = CONTAINING_RECORD(object, THREAD, Header); uint32_t tid = thread->ThreadId; ObDereferenceObject(object); return KiCopyU32ToUser(tidOut, tid); } static void KiFinishAlertableUserWait(uint8_t alertable) { if (!alertable) return; THREAD* thread = KeGetCurrentThread(); if (thread && thread->UserApcPending) thread->UserApcDeliverable = 1; } // Maps a waitable handle to its passive event backing object. static KSTATUS KiHandleToWaitEvent(PROCESS* proc, SYS_HANDLE handle, EVENT** eventOut, OBJECT** objectOut) { if (!proc || !eventOut || !objectOut) return NTLSTATUS_INVALID_PARAMETER; *eventOut = (EVENT*)0; *objectOut = (OBJECT*)0; OBJECT* obj = (OBJECT*)0; KSTATUS handleStatus = KeReferenceHandle(proc, handle, 0, SYS_RIGHT_WAIT, &obj); if (!KSUCCESS(handleStatus)) return handleStatus; EVENT* event = (EVENT*)0; switch (obj->Type) { case ObjectTypeProcess: event = CONTAINING_RECORD(obj, PROCESS, Header)->ExitEvent; break; case ObjectTypeThread: event = CONTAINING_RECORD(obj, THREAD, Header)->ExitEvent; break; case ObjectTypeEvent: event = CONTAINING_RECORD(obj, EVENT, Header); break; case ObjectTypeAsyncIo: event = IoGetAsyncRequestWaitEvent((IO_ASYNC_REQUEST_OBJECT*)obj); break; case ObjectTypeTimer: event = KeTimerWaitEvent(CONTAINING_RECORD(obj, KTIMER, Header)); break; case ObjectTypeSurface: event = KeSurfaceWaitEvent(obj); break; case ObjectTypeDisplayClock: event = KeDisplayClockWaitEvent((DISPLAY_CLOCK_OBJECT*)obj); break; case ObjectTypeAudioStream: event = KeAudioStreamWaitEvent((KE_AUDIO_STREAM*)obj); break; case ObjectTypePort: event = KePortWaitEvent((PORT*)obj); break; case ObjectTypePipeEndpoint: event = IoPipeWaitEvent((IO_PIPE_ENDPOINT*)obj); break; default: ObDereferenceObject(obj); return NTLSTATUS_NOT_SUPPORTED; } if (!event) { ObDereferenceObject(obj); return NTLSTATUS_OBJECT_DEAD; } *eventOut = event; *objectOut = obj; return NTLSTATUS_SUCCESS; } // Implements SYS_WAIT_SINGLE. // Waits on one handle. Passive objects use the wait-many engine; mutexes and // semaphores keep their native consuming semantics for single-object waits. static uint64_t SyscallWaitSingle(SYS_HANDLE handle, uint32_t flags, uint64_t timeoutMilliseconds) { uint64_t timeoutMs = timeoutMilliseconds; if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return NTLSTATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; OBJECT* obj = (OBJECT*)0; KSTATUS handleStatus = KeReferenceHandle(proc, handle, 0, SYS_RIGHT_WAIT, &obj); if (!KSUCCESS(handleStatus)) return handleStatus; uint8_t alertable = (flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0; KSTATUS s = NTLSTATUS_NOT_SUPPORTED; if (obj->Type == ObjectTypeMutex) { if (alertable) s = NTLSTATUS_NOT_SUPPORTED; else s = KeAcquireMutex(CONTAINING_RECORD(obj, MUTEX, Header), timeoutMs); ObDereferenceObject(obj); return s; } if (obj->Type == ObjectTypeSemaphore) { if (alertable) s = NTLSTATUS_NOT_SUPPORTED; else s = KeWaitSemaphore(CONTAINING_RECORD(obj, SEMAPHORE, Header), timeoutMs); ObDereferenceObject(obj); return s; } ObDereferenceObject(obj); EVENT* event = (EVENT*)0; OBJECT* ref = (OBJECT*)0; s = KiHandleToWaitEvent(proc, handle, &event, &ref); if (!KSUCCESS(s)) return s; if (ref->Type == ObjectTypePipeEndpoint) IoPipeBeginWait((IO_PIPE_ENDPOINT*)ref); uint32_t selected = 0; s = KeWaitForMultipleEvents(&event, 1, alertable, timeoutMs, &selected); if (ref->Type == ObjectTypePipeEndpoint) IoPipeEndWait((IO_PIPE_ENDPOINT*)ref); ObDereferenceObject(ref); if (s != NTLSTATUS_TIMEOUT && s != NTLSTATUS_NOT_SUPPORTED) KiFinishAlertableUserWait(alertable); return s; } // Implements SYS_WAIT_MANY. // Waits until any passive waitable handle becomes signaled. static uint64_t SyscallWaitMany(const SYS_WAIT_MANY_REQUEST* request) { SYS_WAIT_MANY_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags & ~SYS_WAIT_FLAG_ALERTABLE) return NTLSTATUS_INVALID_PARAMETER; if (!req.Handles || req.Count == 0 || req.Count > SYS_WAIT_MANY_MAX_HANDLES) return NTLSTATUS_INVALID_PARAMETER; SYS_HANDLE handles[SYS_WAIT_MANY_MAX_HANDLES]; size_t handleBytes = (size_t)req.Count * sizeof(handles[0]); s = CopyFromUser(handles, req.Handles, handleBytes); if (!KSUCCESS(s)) return s; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; EVENT* events[SYS_WAIT_MANY_MAX_HANDLES]; OBJECT* refs[SYS_WAIT_MANY_MAX_HANDLES]; for (uint32_t i = 0; i < SYS_WAIT_MANY_MAX_HANDLES; i++) { events[i] = (EVENT*)0; refs[i] = (OBJECT*)0; } for (uint32_t i = 0; i < req.Count; i++) { s = KiHandleToWaitEvent(proc, handles[i], &events[i], &refs[i]); if (!KSUCCESS(s)) { for (uint32_t j = 0; j < i; j++) ObDereferenceObject(refs[j]); return s; } } for (uint32_t i = 0; i < req.Count; i++) if (refs[i]->Type == ObjectTypePipeEndpoint) IoPipeBeginWait((IO_PIPE_ENDPOINT*)refs[i]); uint8_t alertable = (req.Flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0; uint32_t selected = 0; s = KeWaitForMultipleEvents(events, req.Count, alertable, req.TimeoutMilliseconds, &selected); for (uint32_t i = 0; i < req.Count; i++) { if (refs[i]->Type == ObjectTypePipeEndpoint) IoPipeEndWait((IO_PIPE_ENDPOINT*)refs[i]); ObDereferenceObject(refs[i]); } if (KSUCCESS(s) && req.SignaledIndexOut) { KSTATUS cs = KiCopyU32ToUser(req.SignaledIndexOut, selected); if (!KSUCCESS(cs)) return cs; } if (s != NTLSTATUS_TIMEOUT && s != NTLSTATUS_NOT_SUPPORTED) KiFinishAlertableUserWait(alertable); return s; } // Returns the current thread ID. static uint64_t SyscallGetCurrentTid(void) { THREAD* t = KeGetCurrentThread(); return t ? t->ThreadId : 0; } // Returns the current process ID. static uint64_t SyscallGetCurrentPid(void) { PROCESS* p = KeGetCurrentProcess(); return p ? p->ProcessId : 0; } // Yields the current thread. static uint64_t SyscallYield(void) { KeYield(); return NTLSTATUS_SUCCESS; } // Implements SYS_CREATE_TIMER. static uint64_t SyscallCreateTimer(SYS_HANDLE* handleOut) { if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; KTIMER* timer = (KTIMER*)0; KSTATUS s = KeTimerCreate(&timer); if (!KSUCCESS(s)) return s; SYS_HANDLE handle = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&timer->Header, KeGetValidHandleRights(ObjectTypeTimer), &handle); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(handleOut, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); } return s; } // Implements SYS_SET_TIMER. static uint64_t SyscallSetTimer(const SYS_SET_TIMER_REQUEST* request) { SYS_SET_TIMER_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags != SYS_TIMER_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; KTIMER* timer = (KTIMER*)KiHandleToObject(req.TimerHandle, ObjectTypeTimer, SYS_TIMER_RIGHT_SET, &handleStatus); if (!timer) return handleStatus; s = KeTimerSet(timer, req.DueTimeMilliseconds, req.PeriodMilliseconds); ObDereferenceObject(&timer->Header); return s; } // Implements SYS_CANCEL_TIMER. static uint64_t SyscallCancelTimer(SYS_HANDLE timerHandle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KTIMER* timer = (KTIMER*)KiHandleToObject(timerHandle, ObjectTypeTimer, SYS_TIMER_RIGHT_SET, &handleStatus); if (!timer) return handleStatus; KSTATUS s = KeTimerCancel(timer); ObDereferenceObject(&timer->Header); return s; } // Implements SYS_SLEEP. // Sleep is a convenience wrapper over the same waitable timer backend exposed // by timer handles. A zero-length sleep remains a yield. static uint64_t SyscallSleep(uint64_t ms) { if (ms == 0) { KeYield(); return NTLSTATUS_SUCCESS; } KTIMER* timer = (KTIMER*)0; KSTATUS s = KeTimerCreate(&timer); if (!KSUCCESS(s)) return s; s = KeTimerSet(timer, ms, 0); if (KSUCCESS(s)) { EVENT* event = KeTimerWaitEvent(timer); uint32_t selected = 0; s = KeWaitForMultipleEvents(&event, 1, 0, SYS_WAIT_TIMEOUT_INFINITE, &selected); } ObDereferenceObject(&timer->Header); return s; } // Implements SYS_SET_USER_APC_DISPATCHER. static uint64_t SyscallSetUserApcDispatcher(void* dispatcher) { if (!dispatcher) return NTLSTATUS_INVALID_PARAMETER; KSTATUS s = MmProbeUserRead(dispatcher, 1); if (!KSUCCESS(s)) return s; return KeSetUserApcDispatcher(dispatcher); } // Implements SYS_QUEUE_USER_APC. static uint64_t SyscallQueueUserApc(const SYS_QUEUE_USER_APC_REQUEST* userRequest) { SYS_QUEUE_USER_APC_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (!request.ThreadHandle || !request.Routine || request.Reserved != 0 || (request.Flags & ~SYS_QUEUE_USER_APC_FLAG_INTERRUPT) != 0) return NTLSTATUS_INVALID_PARAMETER; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; OBJECT* object = (OBJECT*)0; status = KeReferenceHandle(process, request.ThreadHandle, ObjectTypeThread, SYS_THREAD_RIGHT_NOTIFY, &object); if (!KSUCCESS(status)) return status; THREAD* thread = CONTAINING_RECORD(object, THREAD, Header); status = KeQueueUserApc(thread, (void*)(uintptr_t)request.Routine, request.NormalContext, request.SystemArgument1, request.SystemArgument2, (request.Flags & SYS_QUEUE_USER_APC_FLAG_INTERRUPT) != 0 ? KE_USER_APC_FLAG_INTERRUPT : KE_USER_APC_FLAG_NONE); ObDereferenceObject(object); return status; } // Implements SYS_APC_RETURN. static uint64_t SyscallApcReturn(const SYS_USER_APC_FRAME* frame) { return KeUserApcReturn((const SYS_USER_APC_FRAME_K*)(uintptr_t)frame); } // Implements SYS_SET_EXCEPTION_DISPATCHER. static uint64_t SyscallSetExceptionDispatcher(const SYS_EXCEPTION_DISPATCHER_REQUEST* userRequest) { SYS_EXCEPTION_DISPATCHER_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; return KeSetExceptionDispatcher(process, &request); } // Implements SYS_GET_EXCEPTION_DISPATCHER. static uint64_t SyscallGetExceptionDispatcher(SYS_EXCEPTION_DISPATCHER_REQUEST* userRequest) { if (!userRequest) return NTLSTATUS_INVALID_PARAMETER; uint32_t userSize = 0; KSTATUS status = CopyFromUser(&userSize, userRequest, sizeof(userSize)); if (!KSUCCESS(status)) return status; if (userSize != sizeof(SYS_EXCEPTION_DISPATCHER_REQUEST)) return NTLSTATUS_INVALID_PARAMETER; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; SYS_EXCEPTION_DISPATCHER_REQUEST request; status = KeGetExceptionDispatcher(process, &request); if (!KSUCCESS(status)) return status; return CopyToUser(userRequest, &request, sizeof(request)); } // Implements SYS_CONTINUE_EXCEPTION. static uint64_t SyscallContinueException(const SYS_CONTINUE_EXCEPTION_REQUEST* userRequest) { SYS_CONTINUE_EXCEPTION_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; return KeContinueException(&request); } // Implements SYS_RAISE_EXCEPTION. static uint64_t SyscallRaiseException(const SYS_RAISE_EXCEPTION_REQUEST* userRequest) { SYS_RAISE_EXCEPTION_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.ParameterCount > SYS_EXCEPTION_MAXIMUM_PARAMETERS) return NTLSTATUS_INVALID_PARAMETER; uint64_t parameters[SYS_EXCEPTION_MAXIMUM_PARAMETERS]; memset(parameters, 0, sizeof(parameters)); if (request.ParameterCount) { if (!request.Parameters) return NTLSTATUS_INVALID_PARAMETER; status = CopyFromUser(parameters, request.Parameters, (size_t)request.ParameterCount * sizeof(parameters[0])); if (!KSUCCESS(status)) return status; } return KeRaiseUserException(&request, parameters); } // Implements SYS_SET_THREAD_PRIORITY. static uint64_t SyscallSetThreadPriority(uint32_t priority) { if (priority >= PriorityCount) return NTLSTATUS_INVALID_PARAMETER; THREAD* t = KeGetCurrentThread(); if (!t) return NTLSTATUS_FAILURE; return KiSchedulerSetNormalPriority(t, (THREAD_PRIORITY)priority); } // Implements SYS_SET_THREAD_NAME. static uint64_t SyscallSetThreadName(const SYS_SET_THREAD_NAME_REQUEST* userRequest) { SYS_SET_THREAD_NAME_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; char name[SYS_THREAD_NAME_MAX]; SYS_STRING_VIEW importedName; status = KiImportUserTextView(request.Name, name, sizeof(name), &importedName, true); if (!KSUCCESS(status)) return status; THREAD* thread = (THREAD*)0; OBJECT* referencedObject = (OBJECT*)0; if (request.ThreadHandle == SYS_HANDLE_INVALID) { thread = KeGetCurrentThread(); if (!thread) return NTLSTATUS_FAILURE; } else { PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; status = KeReferenceHandle(process, request.ThreadHandle, ObjectTypeThread, SYS_THREAD_RIGHT_SET_INFORMATION, &referencedObject); if (!KSUCCESS(status)) return status; thread = CONTAINING_RECORD(referencedObject, THREAD, Header); } status = KeSetThreadName(thread, importedName.Data, importedName.Length); if (referencedObject) ObDereferenceObject(referencedObject); return status; } // Implements SYS_QUERY_THREAD_NAME. static uint64_t SyscallQueryThreadName(SYS_HANDLE threadHandle, char* buffer, size_t bufferSize, size_t* lengthOut) { if (!lengthOut || bufferSize > SYS_THREAD_NAME_MAX || (bufferSize && !buffer)) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(lengthOut, sizeof(*lengthOut)); if (!KSUCCESS(status)) return status; if (bufferSize) { status = MmProbeUserWrite(buffer, bufferSize); if (!KSUCCESS(status)) return status; } THREAD* thread = (THREAD*)0; OBJECT* referencedObject = (OBJECT*)0; if (threadHandle == SYS_HANDLE_INVALID) { thread = KeGetCurrentThread(); if (!thread) return NTLSTATUS_FAILURE; } else { PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; status = KeReferenceHandle(process, threadHandle, ObjectTypeThread, SYS_RIGHT_QUERY, &referencedObject); if (!KSUCCESS(status)) return status; thread = CONTAINING_RECORD(referencedObject, THREAD, Header); } char name[SYS_THREAD_NAME_MAX]; size_t length = KeQueryThreadName(thread, name, sizeof(name)); if (referencedObject) ObDereferenceObject(referencedObject); status = KiCopySizeToUser(lengthOut, length); if (!KSUCCESS(status)) return status; if (bufferSize < length + 1u) return NTLSTATUS_BUFFER_TOO_SMALL; return CopyToUser(buffer, name, length + 1u); } // SYS_VM_* page-granular user virtual memory. static KSTATUS KiVmProtectToPageFlags(uint32_t protect, uint64_t* flagsOut) { if (!flagsOut) return NTLSTATUS_INVALID_PARAMETER; switch (protect) { case SYS_VM_NOACCESS: *flagsOut = 0; return NTLSTATUS_SUCCESS; case SYS_VM_READ: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_NX; return NTLSTATUS_SUCCESS; case SYS_VM_READWRITE: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_WRITE | PAGE_NX; return NTLSTATUS_SUCCESS; case SYS_VM_EXECUTE: case SYS_VM_EXECUTE_READ: *flagsOut = PAGE_PRESENT | PAGE_USER; return NTLSTATUS_SUCCESS; case SYS_VM_EXECUTE_READWRITE: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_WRITE; return NTLSTATUS_SUCCESS; default: return NTLSTATUS_INVALID_PARAMETER; } } // Implements the page Flags To Vm Protect operation. static uint32_t KiPageFlagsToVmProtect(uint64_t flags) { if (!(flags & PAGE_PRESENT)) return SYS_VM_NOACCESS; bool writable = (flags & PAGE_WRITE) != 0; bool executable = (flags & PAGE_NX) == 0; if (executable && writable) return SYS_VM_EXECUTE_READWRITE; if (executable) return SYS_VM_EXECUTE_READ; if (writable) return SYS_VM_READWRITE; return SYS_VM_READ; } // Implements SYS_ALLOC_VM. static uint64_t SyscallVmAlloc(const SYS_ALLOC_VM_REQUEST* request) { SYS_ALLOC_VM_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Reserved != 0) return NTLSTATUS_INVALID_PARAMETER; void* preferredBase = req.PreferredBase; size_t pageCount = req.PageCount; uint32_t protect = req.Protect; uint32_t flags = req.Flags; void** baseOut = req.BaseOut; if (!pageCount || !baseOut) return NTLSTATUS_INVALID_PARAMETER; if (pageCount > ((size_t)-1) / PAGE_SIZE) return NTLSTATUS_NO_MEMORY; if (preferredBase || (flags & SYS_VM_FLAG_FIXED)) return NTLSTATUS_NOT_SUPPORTED; uint64_t pageFlags = 0; s = KiVmProtectToPageFlags(protect, &pageFlags); if (!KSUCCESS(s)) return s; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; void* base = (void*)0; s = MmVadCreateCommitted(process, pageCount * PAGE_SIZE, PAGE_SIZE, pageFlags, MM_VAD_ZERO, MmVadGeneric, &base); if (!KSUCCESS(s)) return s; s = CopyToUser(baseOut, &base, sizeof(base)); if (!KSUCCESS(s)) { MmVadDestroy(process, base); return s; } return NTLSTATUS_SUCCESS; } // Releases one user virtual-memory allocation. static uint64_t SyscallVmFree(void* base) { PROCESS* process = KeGetCurrentProcess(); if (!process || !base) return NTLSTATUS_INVALID_PARAMETER; return MmVadDestroy(process, base); } // Changes page protection for one user virtual-memory range. static uint64_t SyscallVmProtect(void* base, size_t pageCount, uint32_t protect, uint32_t* oldProtectOut) { if (!base || !pageCount) return NTLSTATUS_INVALID_PARAMETER; if (pageCount > ((size_t)-1) / PAGE_SIZE) return NTLSTATUS_NO_MEMORY; uint64_t pageFlags = 0; KSTATUS s = KiVmProtectToPageFlags(protect, &pageFlags); if (!KSUCCESS(s)) return s; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; if (oldProtectOut) { void* regionBase = (void*)0; size_t regionSize = 0; uint64_t oldFlags = 0; MM_VAD_TYPE type = MmVadGeneric; uint32_t regionFlags = 0; s = MmVadQuery(process, base, ®ionBase, ®ionSize, &oldFlags, &type, ®ionFlags); if (!KSUCCESS(s)) return s; uint32_t oldProtect = KiPageFlagsToVmProtect(oldFlags); s = CopyToUser(oldProtectOut, &oldProtect, sizeof(oldProtect)); if (!KSUCCESS(s)) return s; } return MmVadProtect(process, base, pageCount * PAGE_SIZE, pageFlags); } // Queries the user virtual-memory region containing one address. static uint64_t SyscallVmQuery(void* address, SYS_VM_REGION_INFO* out) { if (!address || !out) return NTLSTATUS_INVALID_PARAMETER; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; void* base = (void*)0; size_t size = 0; uint64_t pageFlags = 0; MM_VAD_TYPE type = MmVadGeneric; uint32_t regionFlags = 0; KSTATUS s = MmVadQuery(process, address, &base, &size, &pageFlags, &type, ®ionFlags); if (!KSUCCESS(s)) return s; SYS_VM_REGION_INFO info; info.Base = base; info.PageCount = size / PAGE_SIZE; info.Protect = KiPageFlagsToVmProtect(pageFlags); info.Flags = regionFlags; info.Type = (uint32_t)type; info.OwnerPid = process->ProcessId; return CopyToUser(out, &info, sizeof(info)); } // Implements SYS_CREATE_MUTEX. static uint64_t SyscallMutexCreate(SYS_HANDLE* handleOut) { if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; MUTEX* mx = NULL; KSTATUS s = KeMutexCreate(&mx); if (!KSUCCESS(s)) return s; SYS_HANDLE h = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&mx->Header, KeGetValidHandleRights(ObjectTypeMutex), &h); // takes ownership of the ref if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(handleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // Implements SYS_TRY_ACQUIRE_MUTEX. static uint64_t SyscallMutexTryAcquire(SYS_HANDLE handle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; MUTEX* mx = (MUTEX*)KiHandleToObject(handle, ObjectTypeMutex, SYS_RIGHT_WAIT, &handleStatus); if (!mx) return handleStatus; KSTATUS s = KeAcquireMutex(mx, 0); if (s == NTLSTATUS_TIMEOUT) s = NTLSTATUS_FAILURE; ObDereferenceObject(&mx->Header); return s; } // Implements SYS_RELEASE_MUTEX. static uint64_t SyscallMutexRelease(SYS_HANDLE handle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; MUTEX* mx = (MUTEX*)KiHandleToObject(handle, ObjectTypeMutex, SYS_MUTEX_RIGHT_RELEASE, &handleStatus); if (!mx) return handleStatus; KeReleaseMutex(mx); ObDereferenceObject(&mx->Header); return NTLSTATUS_SUCCESS; } // Implements SYS_CREATE_SEM. static uint64_t SyscallSemCreate(int32_t initial, int32_t max, SYS_HANDLE* handleOut) { if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; SEMAPHORE* sem = NULL; KSTATUS s = KeSemaphoreCreate(initial, max, &sem); if (!KSUCCESS(s)) return s; SYS_HANDLE h = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&sem->Header, KeGetValidHandleRights(ObjectTypeSemaphore), &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(handleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // Implements SYS_TRY_WAIT_SEM. static uint64_t SyscallSemTryWait(SYS_HANDLE handle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject(handle, ObjectTypeSemaphore, SYS_RIGHT_WAIT, &handleStatus); if (!sem) return handleStatus; KSTATUS s = KeWaitSemaphore(sem, 0); if (s == NTLSTATUS_TIMEOUT) s = NTLSTATUS_FAILURE; ObDereferenceObject(&sem->Header); return s; } // Implements SYS_SIGNAL_SEM. static uint64_t SyscallSemSignal(SYS_HANDLE handle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject(handle, ObjectTypeSemaphore, SYS_SEMAPHORE_RIGHT_SIGNAL, &handleStatus); if (!sem) return handleStatus; KeSignalSemaphore(sem); ObDereferenceObject(&sem->Header); return NTLSTATUS_SUCCESS; } // Implements SYS_CREATE_EVENT. static uint64_t SyscallEventCreate(uint32_t type, uint32_t initialState, SYS_HANDLE* handleOut) { if (!handleOut || type > EventManualReset) return NTLSTATUS_INVALID_PARAMETER; EVENT* ev = NULL; KSTATUS s = KeEventCreate((EVENT_TYPE)type, initialState, &ev); if (!KSUCCESS(s)) return s; SYS_HANDLE h = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&ev->Header, KeGetValidHandleRights(ObjectTypeEvent), &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(handleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // Implements SYS_SIGNAL_EVENT. static uint64_t SyscallEventSignal(SYS_HANDLE handle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; EVENT* ev = (EVENT*)KiHandleToObject(handle, ObjectTypeEvent, SYS_EVENT_RIGHT_SIGNAL, &handleStatus); if (!ev) return handleStatus; KeSignalEvent(ev); ObDereferenceObject(&ev->Header); return NTLSTATUS_SUCCESS; } // Implements SYS_CLEAR_EVENT. static uint64_t SyscallEventClear(SYS_HANDLE handle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; EVENT* ev = (EVENT*)KiHandleToObject(handle, ObjectTypeEvent, SYS_EVENT_RIGHT_SIGNAL, &handleStatus); if (!ev) return handleStatus; KeClearEvent(ev); ObDereferenceObject(&ev->Header); return NTLSTATUS_SUCCESS; } // Implements SYS_CREATE_QUEUE. static uint64_t SyscallQueueCreate(SYS_HANDLE* handleOut) { if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; QUEUE* q = NULL; KSTATUS s = KeQueueCreate(&q); if (!KSUCCESS(s)) return s; SYS_HANDLE h = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&q->Header, KeGetValidHandleRights(ObjectTypeQueue), &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(handleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // Implements SYS_SEND_QUEUE. static uint64_t SyscallQueueSend(const SYS_QUEUE_SEND_REQUEST* request) { SYS_QUEUE_SEND_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags != SYS_QUEUE_FLAG_NONE || !req.Message) return NTLSTATUS_INVALID_PARAMETER; QUEUE_MESSAGE msg; s = CopyFromUser(&msg, req.Message, sizeof(msg)); if (!KSUCCESS(s)) return s; KSTATUS handleStatus = NTLSTATUS_SUCCESS; QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, SYS_QUEUE_RIGHT_SEND, &handleStatus); if (!q) return handleStatus; s = KeSendMessage(q, &msg, req.TimeoutMilliseconds); ObDereferenceObject(&q->Header); return s; } // Implements SYS_RECV_QUEUE. static uint64_t SyscallQueueRecv(const SYS_QUEUE_RECV_REQUEST* request) { SYS_QUEUE_RECV_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags != SYS_QUEUE_FLAG_NONE || !req.MessageOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, SYS_QUEUE_RIGHT_RECEIVE, &handleStatus); if (!q) return handleStatus; QUEUE_MESSAGE msg; s = KeReceiveMessage(q, &msg, req.TimeoutMilliseconds); ObDereferenceObject(&q->Header); if (!KSUCCESS(s)) return s; return CopyToUser(req.MessageOut, &msg, sizeof(msg)); } // Implements SYS_CLOSE_HANDLE. // The one and only close path. KeCloseHandle drops the handle-table reference // on 'obj'. If that was the last reference, ObDereferenceObject runs the // type-specific Destroy callback (KiFileDestroy, etc.) and frees the pool memory. // No per-type close syscall needed or wanted. static uint64_t SyscallCreateFileMapping(const SYS_CREATE_FILE_MAPPING_REQUEST* request) { SYS_CREATE_FILE_MAPPING_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.FileHandle || !req.MappingHandleOut) return NTLSTATUS_INVALID_PARAMETER; uint64_t pageFlags = 0; s = KiVmProtectToPageFlags(req.Protect, &pageFlags); if (!KSUCCESS(s)) return s; if (!pageFlags) return NTLSTATUS_ACCESS_DENIED; SYS_RIGHTS neededRights = SYS_FILE_RIGHT_MAP_READ | SYS_RIGHT_QUERY; if (pageFlags & PAGE_WRITE) neededRights |= SYS_FILE_RIGHT_MAP_WRITE; KSTATUS fileHandleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, neededRights, &fileHandleStatus); if (!file) return fileHandleStatus; uint64_t maximumSize = req.MaximumSize; if (maximumSize == 0) maximumSize = IoGetFileSize(file); FILE_MAPPING_OBJECT* mapping = (FILE_MAPPING_OBJECT*)ObAllocateObject(ObjectTypeFileMapping, sizeof(FILE_MAPPING_OBJECT)); if (!mapping) { ObDereferenceObject(&file->Header); return NTLSTATUS_NO_MEMORY; } mapping->Header.Destroy = KiFileMappingDestroy; mapping->File = file; mapping->MaximumSize = maximumSize; mapping->PageFlags = pageFlags; mapping->Protect = req.Protect; mapping->Flags = req.Flags; SYS_HANDLE handle = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&mapping->Header, KeGetValidHandleRights(ObjectTypeFileMapping), &handle); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(req.MappingHandleOut, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); } return s; } static KSTATUS KiMapAccessToPageFlags(const FILE_MAPPING_OBJECT* mapping, uint32_t access, uint64_t* pageFlagsOut) { if (!mapping || !pageFlagsOut) return NTLSTATUS_INVALID_PARAMETER; uint64_t mappingFlags = mapping->PageFlags; if (!mappingFlags) return NTLSTATUS_ACCESS_DENIED; if (access == 0) access = SYS_MAP_ACCESS_READ; if ((access & SYS_MAP_ACCESS_WRITE) && (access & SYS_MAP_ACCESS_COPY)) return NTLSTATUS_INVALID_PARAMETER; if (((access & SYS_MAP_ACCESS_COPY) || (access & SYS_MAP_ACCESS_WRITE)) && (mappingFlags & PAGE_WRITE) == 0) return NTLSTATUS_ACCESS_DENIED; uint64_t flags = PAGE_PRESENT | PAGE_USER; if ((access & SYS_MAP_ACCESS_COPY) || (access & SYS_MAP_ACCESS_WRITE)) flags |= PAGE_WRITE; if (access & SYS_MAP_ACCESS_EXECUTE) { if (mappingFlags & PAGE_NX) return NTLSTATUS_ACCESS_DENIED; } else { flags |= PAGE_NX; } *pageFlagsOut = flags; return NTLSTATUS_SUCCESS; } // Maps a file-mapping object into the current process. static uint64_t SyscallMapFile(const SYS_MAP_FILE_REQUEST* request) { SYS_MAP_FILE_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.MappingHandle || !req.BaseOut) return NTLSTATUS_INVALID_PARAMETER; if (req.Offset & (PAGE_SIZE - 1)) return NTLSTATUS_INVALID_PARAMETER; KSTATUS mappingHandleStatus = NTLSTATUS_SUCCESS; FILE_MAPPING_OBJECT* mapping = (FILE_MAPPING_OBJECT*)KiHandleToObject(req.MappingHandle, ObjectTypeFileMapping, SYS_FILE_MAPPING_RIGHT_MAP, &mappingHandleStatus); if (!mapping) return mappingHandleStatus; uint64_t maximumSize = mapping->MaximumSize; if (maximumSize == 0) maximumSize = IoGetFileSize(mapping->File); if (req.Offset >= maximumSize) { ObDereferenceObject(&mapping->Header); return NTLSTATUS_INVALID_PARAMETER; } size_t length = req.Length; if (length == 0) { uint64_t tail = maximumSize - req.Offset; if (tail > (uint64_t)((size_t)-1)) { ObDereferenceObject(&mapping->Header); return NTLSTATUS_NO_MEMORY; } length = (size_t)tail; } if (length == 0 || (uint64_t)length > maximumSize - req.Offset) { ObDereferenceObject(&mapping->Header); return NTLSTATUS_INVALID_PARAMETER; } uint64_t pageFlags = 0; s = KiMapAccessToPageFlags(mapping, req.Access, &pageFlags); if (!KSUCCESS(s)) { ObDereferenceObject(&mapping->Header); return s; } PROCESS* process = KeGetCurrentProcess(); if (!process) { ObDereferenceObject(&mapping->Header); return NTLSTATUS_FAILURE; } uint32_t vadFlags = req.Flags; if (req.Access & SYS_MAP_ACCESS_WRITE) vadFlags |= MM_VAD_SHARED_WRITE; void* base = (void*)0; s = MmVadCreateFileMapping(process, mapping->File, req.Offset, length, pageFlags, vadFlags, &base); ObDereferenceObject(&mapping->Header); if (!KSUCCESS(s)) return s; s = CopyToUser(req.BaseOut, &base, sizeof(base)); if (!KSUCCESS(s)) { MmVadDestroy(process, base); return s; } return NTLSTATUS_SUCCESS; } // Closes one handle through the unified object handle path. static uint64_t SyscallCloseHandle(SYS_HANDLE handle) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; return KeCloseHandle(proc, handle); } // Converts public file access flags to handle rights and file object flags. static KSTATUS KiFileAccessToHandleRights(uint32_t access, SYS_RIGHTS* rightsOut, uint32_t* fileFlagsOut) { if (!rightsOut || !fileFlagsOut) return NTLSTATUS_INVALID_PARAMETER; uint32_t valid = SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_APPEND | SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK; if ((access & ~valid) != 0 || access == 0) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS rights = SYS_RIGHT_MANAGE; uint32_t fileFlags = 0; if (access & SYS_FILE_ACCESS_READ) rights |= SYS_FILE_RIGHT_READ_DATA | SYS_FILE_RIGHT_MAP_READ; if (access & SYS_FILE_ACCESS_WRITE) rights |= SYS_FILE_RIGHT_WRITE_DATA | SYS_FILE_RIGHT_MAP_WRITE; if (access & SYS_FILE_ACCESS_APPEND) { rights |= SYS_FILE_RIGHT_WRITE_DATA | SYS_FILE_RIGHT_MAP_WRITE; fileFlags |= IO_FILE_FLAG_APPEND; } if (access & SYS_FILE_ACCESS_QUERY) rights |= SYS_RIGHT_QUERY; if (access & SYS_FILE_ACCESS_LOCK) rights |= SYS_FILE_RIGHT_LOCK; *rightsOut = rights; *fileFlagsOut = fileFlags; return NTLSTATUS_SUCCESS; } // Converts public directory access flags to directory capability rights. static KSTATUS KiDirAccessToHandleRights(uint32_t access, SYS_RIGHTS* rightsOut) { if (!rightsOut) return NTLSTATUS_INVALID_PARAMETER; uint32_t valid = SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN | SYS_DIR_ACCESS_MODIFY; if ((access & ~valid) != 0 || access == 0) return NTLSTATUS_INVALID_PARAMETER; if ((access & SYS_DIR_ACCESS_MODIFY) && !(access & SYS_DIR_ACCESS_OPEN)) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS rights = SYS_RIGHT_MANAGE | SYS_RIGHT_QUERY; if (access & SYS_DIR_ACCESS_ENUMERATE) rights |= SYS_DIR_RIGHT_ENUMERATE; if (access & SYS_DIR_ACCESS_OPEN) rights |= SYS_DIR_RIGHT_OPEN; if (access & SYS_DIR_ACCESS_MODIFY) rights |= SYS_DIR_RIGHT_MODIFY; *rightsOut = rights; return NTLSTATUS_SUCCESS; } // References one capability-relative path root with type-specific traversal authority. static OBJECT* KiReferencePathRoot(SYS_HANDLE handle, bool requireModify, KSTATUS* statusOut) { if (!statusOut) return (OBJECT*)0; KSTATUS status = NTLSTATUS_SUCCESS; OBJECT* probe = KiHandleToObject(handle, 0, 0, &status); if (!probe) { *statusOut = status; return (OBJECT*)0; } OBJECT_TYPE type = probe->Type; ObDereferenceObject(probe); SYS_RIGHTS required = 0; if (type == ObjectTypeVolume) required = SYS_VOLUME_RIGHT_OPEN | (requireModify ? SYS_VOLUME_RIGHT_MODIFY : 0); else if (type == ObjectTypeDir) required = SYS_DIR_RIGHT_OPEN | (requireModify ? SYS_DIR_RIGHT_MODIFY : 0); else { *statusOut = NTLSTATUS_INVALID_HANDLE; return (OBJECT*)0; } return KiHandleToObject(handle, type, required, statusOut); } // Opens or creates one file beneath a referenced volume/directory root. static KSTATUS KiOpenFileAtRoot(OBJECT* root, SYS_STRING_VIEW path, bool create, FILE_OBJECT** fileOut) { if (!root || !fileOut) return NTLSTATUS_INVALID_PARAMETER; if (root->Type == ObjectTypeVolume) { return create ? IoCreateFileAtVolume((IO_VOLUME*)root, path, fileOut) : IoOpenFileAtVolume((IO_VOLUME*)root, path, fileOut); } if (root->Type == ObjectTypeDir) { return create ? IoCreateFileAtDirectory((DIR_OBJECT*)root, path, fileOut) : IoOpenFileAtDirectory((DIR_OBJECT*)root, path, fileOut); } return NTLSTATUS_INVALID_HANDLE; } // Opens one directory beneath a referenced volume/directory root. static KSTATUS KiOpenDirAtRoot(OBJECT* root, SYS_STRING_VIEW path, DIR_OBJECT** dirOut) { if (!root || !dirOut) return NTLSTATUS_INVALID_PARAMETER; if (root->Type == ObjectTypeVolume) return IoOpenDirAtVolume((IO_VOLUME*)root, path, dirOut); if (root->Type == ObjectTypeDir) return IoOpenDirAtDirectory((DIR_OBJECT*)root, path, dirOut); return NTLSTATUS_INVALID_HANDLE; } // Opens a file beneath a capability root using the public disposition contract. static KSTATUS KiOpenFileAtRootByDisposition(OBJECT* root, SYS_STRING_VIEW path, uint32_t disposition, uint32_t access, FILE_OBJECT** fileOut) { if (!root || !fileOut) return NTLSTATUS_INVALID_PARAMETER; *fileOut = (FILE_OBJECT*)0; bool writes = (access & (SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_APPEND)) != 0; KSTATUS status; switch (disposition) { case SYS_FILE_OPEN_EXISTING: return KiOpenFileAtRoot(root, path, false, fileOut); case SYS_FILE_CREATE_NEW: if (!writes) return NTLSTATUS_ACCESS_DENIED; return KiOpenFileAtRoot(root, path, true, fileOut); case SYS_FILE_CREATE_ALWAYS: if (!writes) return NTLSTATUS_ACCESS_DENIED; status = KiOpenFileAtRoot(root, path, true, fileOut); if (status == NTLSTATUS_OBJECT_NAME_COLLISION) { status = KiOpenFileAtRoot(root, path, false, fileOut); if (KSUCCESS(status)) { KSTATUS truncateStatus = IoTruncateFile(*fileOut, 0); if (!KSUCCESS(truncateStatus)) { IoCloseFile(*fileOut); *fileOut = (FILE_OBJECT*)0; return truncateStatus; } } } return status; case SYS_FILE_OPEN_ALWAYS: status = KiOpenFileAtRoot(root, path, false, fileOut); if (status == NTLSTATUS_NOT_FOUND) { if (!writes) return NTLSTATUS_ACCESS_DENIED; status = KiOpenFileAtRoot(root, path, true, fileOut); } return status; case SYS_FILE_TRUNCATE_EXISTING: if (!writes) return NTLSTATUS_ACCESS_DENIED; status = KiOpenFileAtRoot(root, path, false, fileOut); if (KSUCCESS(status)) { KSTATUS truncateStatus = IoTruncateFile(*fileOut, 0); if (!KSUCCESS(truncateStatus)) { IoCloseFile(*fileOut); *fileOut = (FILE_OBJECT*)0; return truncateStatus; } } return status; default: return NTLSTATUS_INVALID_PARAMETER; } } // Implements SYS_OPEN_FILE_AT. Authority comes only from the supplied root capability. static uint64_t SyscallOpenFileAt(const SYS_OPEN_FILE_AT_REQUEST* request) { SYS_OPEN_FILE_AT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (!req.HandleOut) return NTLSTATUS_INVALID_PARAMETER; if (req.Flags != SYS_FILE_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS rights = 0; uint32_t fileFlags = 0; status = KiFileAccessToHandleRights(req.Access, &rights, &fileFlags); if (!KSUCCESS(status)) return status; char pathBuffer[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; status = KiImportUserTextView(req.Path, pathBuffer, sizeof(pathBuffer), &path, true); if (!KSUCCESS(status)) return status; bool requireModify = (req.Access & (SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_APPEND)) != 0 || req.Disposition != SYS_FILE_OPEN_EXISTING; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* root = KiReferencePathRoot(req.RootHandle, requireModify, &handleStatus); if (!root) return handleStatus; FILE_OBJECT* file = (FILE_OBJECT*)0; status = KiOpenFileAtRootByDisposition(root, path, req.Disposition, req.Access, &file); ObDereferenceObject(root); if (!KSUCCESS(status)) return status; if (fileFlags != 0) { uintptr_t flags = KeAcquireSpinlock(&file->Lock); file->Flags |= fileFlags; KeReleaseSpinlock(&file->Lock, flags); } SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KiAllocHandleForCurrentWithRights(&file->Header, rights, &handle); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.HandleOut, handle); if (!KSUCCESS(status)) { PROCESS* process = KeGetCurrentProcess(); if (process) KeCloseHandle(process, handle); } return status; } // Implements SYS_OPEN_DIR_AT. Authority comes only from the supplied root capability. static uint64_t SyscallOpenDirAt(const SYS_OPEN_DIR_AT_REQUEST* request) { SYS_OPEN_DIR_AT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (!req.HandleOut) return NTLSTATUS_INVALID_PARAMETER; if (req.Flags != SYS_DIR_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS dirRights = 0; status = KiDirAccessToHandleRights(req.Access, &dirRights); if (!KSUCCESS(status)) return status; char pathBuffer[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; status = KiImportUserTextView(req.Path, pathBuffer, sizeof(pathBuffer), &path, true); if (!KSUCCESS(status)) return status; bool requireModify = (req.Access & SYS_DIR_ACCESS_MODIFY) != 0; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* root = KiReferencePathRoot(req.RootHandle, requireModify, &handleStatus); if (!root) return handleStatus; DIR_OBJECT* dir = (DIR_OBJECT*)0; status = KiOpenDirAtRoot(root, path, &dir); ObDereferenceObject(root); if (!KSUCCESS(status)) return status; SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KiAllocHandleForCurrentWithRights(&dir->Header, dirRights, &handle); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.HandleOut, handle); if (!KSUCCESS(status)) { PROCESS* process = KeGetCurrentProcess(); if (process) KeCloseHandle(process, handle); } return status; } // Imports one capability-relative path from userspace. static KSTATUS KiImportAtPath(SYS_STRING_VIEW userPath, char* buffer, size_t bufferSize, bool allowEmpty, SYS_STRING_VIEW* pathOut) { KSTATUS status = KiImportUserTextView(userPath, buffer, bufferSize, pathOut, true); if (!KSUCCESS(status)) return status; return (allowEmpty || pathOut->Length) ? NTLSTATUS_SUCCESS : NTLSTATUS_INVALID_PARAMETER; } // Implements SYS_STAT_AT. Authority comes only from the supplied root capability. static uint64_t SyscallStatAt(const SYS_STAT_AT_REQUEST* request) { SYS_STAT_AT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (!req.StatOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.StatOut, sizeof(*req.StatOut)); if (!KSUCCESS(status)) return status; char pathBuffer[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; status = KiImportAtPath(req.Path, pathBuffer, sizeof(pathBuffer), true, &path); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* root = KiReferencePathRoot(req.RootHandle, false, &handleStatus); if (!root) return handleStatus; SYS_FILE_STAT stat; status = IoStatAtRoot(root, path, &stat); ObDereferenceObject(root); if (!KSUCCESS(status)) return status; return CopyToUser(req.StatOut, &stat, sizeof(stat)); } // Implements SYS_DELETE_AT. Authority comes only from the supplied root capability. static uint64_t SyscallDeleteAt(const SYS_PATH_AT_REQUEST* request) { SYS_PATH_AT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; char pathBuffer[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; status = KiImportAtPath(req.Path, pathBuffer, sizeof(pathBuffer), false, &path); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* root = KiReferencePathRoot(req.RootHandle, true, &handleStatus); if (!root) return handleStatus; status = IoDeleteFileAtRoot(root, path); ObDereferenceObject(root); return status; } // Implements SYS_RENAME_AT. Both roots must grant modify authority and resolve to the same volume. static uint64_t SyscallRenameAt(const SYS_RENAME_AT_REQUEST* request) { SYS_RENAME_AT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; char oldBuffer[SYSCALL_STRING_MAX]; char newBuffer[SYSCALL_STRING_MAX]; SYS_STRING_VIEW oldPath; SYS_STRING_VIEW newPath; status = KiImportAtPath(req.OldPath, oldBuffer, sizeof(oldBuffer), false, &oldPath); if (!KSUCCESS(status)) return status; status = KiImportAtPath(req.NewPath, newBuffer, sizeof(newBuffer), false, &newPath); if (!KSUCCESS(status)) return status; KSTATUS oldStatus = NTLSTATUS_SUCCESS; OBJECT* oldRoot = KiReferencePathRoot(req.OldRootHandle, true, &oldStatus); if (!oldRoot) return oldStatus; KSTATUS newStatus = NTLSTATUS_SUCCESS; OBJECT* newRoot = KiReferencePathRoot(req.NewRootHandle, true, &newStatus); if (!newRoot) { ObDereferenceObject(oldRoot); return newStatus; } status = IoRenameFileAtRoots(oldRoot, oldPath, newRoot, newPath); ObDereferenceObject(newRoot); ObDereferenceObject(oldRoot); return status; } // Implements SYS_CREATE_DIR_AT. Authority comes only from the supplied root capability. static uint64_t SyscallCreateDirAt(const SYS_PATH_AT_REQUEST* request) { SYS_PATH_AT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; char pathBuffer[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; status = KiImportAtPath(req.Path, pathBuffer, sizeof(pathBuffer), false, &path); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* root = KiReferencePathRoot(req.RootHandle, true, &handleStatus); if (!root) return handleStatus; status = IoCreateDirAtRoot(root, path); ObDereferenceObject(root); return status; } // Implements SYS_REMOVE_DIR_AT. Authority comes only from the supplied root capability. static uint64_t SyscallRemoveDirAt(const SYS_PATH_AT_REQUEST* request) { SYS_PATH_AT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; char pathBuffer[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; status = KiImportAtPath(req.Path, pathBuffer, sizeof(pathBuffer), false, &path); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* root = KiReferencePathRoot(req.RootHandle, true, &handleStatus); if (!root) return handleStatus; status = IoRemoveDirAtRoot(root, path); ObDereferenceObject(root); return status; } static void* KiAllocateSyscallIoBuffer(size_t requested, size_t* actualOut) { if (actualOut) *actualOut = 0; if (!requested) return NULL; size_t chunk = requested < SYSCALL_FILE_IO_CHUNK_BYTES ? requested : SYSCALL_FILE_IO_CHUNK_BYTES; while (true) { void* buf = MmAllocatePoolTag(chunk, MM_POOL_TAG('s', 'i', 'o', ' ')); if (buf) { if (actualOut) *actualOut = chunk; return buf; } if (chunk <= PAGE_SIZE) break; chunk >>= 1; if (chunk < PAGE_SIZE) chunk = PAGE_SIZE; } return NULL; } // Imports and validates a synchronous file I/O request. static KSTATUS KiImportSyncFileIoRequest(const SYS_FILE_IO_REQUEST* userRequest, SYS_FILE_IO_REQUEST* req) { KSTATUS s = KiCopySizedUserRequest(userRequest, req, sizeof(*req)); if (!KSUCCESS(s)) return s; if (req->Flags != 0) return NTLSTATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; if (req->Length && !req->Buffer) return NTLSTATUS_INVALID_PARAMETER; return NTLSTATUS_SUCCESS; } // Implements SYS_READ_FILE. static uint64_t SyscallReadFile(const SYS_FILE_IO_REQUEST* request) { SYS_FILE_IO_REQUEST req; KSTATUS s = KiImportSyncFileIoRequest(request, &req); if (!KSUCCESS(s)) return s; if (req.Length == 0) { if (req.TransferredOut) return KiCopySizeToUser(req.TransferredOut, 0); return NTLSTATUS_SUCCESS; } KSTATUS fileHandleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, SYS_FILE_RIGHT_READ_DATA, &fileHandleStatus); if (!file) return fileHandleStatus; size_t chunkSize = 0; void* kbuf = KiAllocateSyscallIoBuffer(req.Length, &chunkSize); if (!kbuf) { ObDereferenceObject(&file->Header); return NTLSTATUS_NO_MEMORY; } uintptr_t user = (uintptr_t)req.Buffer; size_t total = 0; s = NTLSTATUS_SUCCESS; while (total < req.Length) { size_t todo = req.Length - total; if (todo > chunkSize) todo = chunkSize; if (user > (uintptr_t)-1 - total) { s = NTLSTATUS_INVALID_PARAMETER; break; } void* userChunk = (void*)(user + total); s = MmProbeUserWrite(userChunk, todo); if (!KSUCCESS(s)) break; size_t got = 0; s = IoReadFile(file, kbuf, todo, &got); if (!KSUCCESS(s)) break; if (got) { s = CopyToUser(userChunk, kbuf, got); if (!KSUCCESS(s)) break; total += got; } if (got < todo) break; } MmFreePool(kbuf); ObDereferenceObject(&file->Header); if (req.TransferredOut) { KSTATUS cs = KiCopySizeToUser(req.TransferredOut, total); if (KSUCCESS(s)) s = cs; } return s; } // Implements SYS_WRITE_FILE. static uint64_t SyscallWriteFile(const SYS_FILE_IO_REQUEST* request) { SYS_FILE_IO_REQUEST req; KSTATUS s = KiImportSyncFileIoRequest(request, &req); if (!KSUCCESS(s)) return s; if (req.Length == 0) { if (req.TransferredOut) return KiCopySizeToUser(req.TransferredOut, 0); return NTLSTATUS_SUCCESS; } KSTATUS fileHandleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, SYS_FILE_RIGHT_WRITE_DATA, &fileHandleStatus); if (!file) return fileHandleStatus; size_t chunkSize = 0; void* kbuf = KiAllocateSyscallIoBuffer(req.Length, &chunkSize); if (!kbuf) { ObDereferenceObject(&file->Header); return NTLSTATUS_NO_MEMORY; } uintptr_t user = (uintptr_t)req.Buffer; size_t total = 0; s = NTLSTATUS_SUCCESS; while (total < req.Length) { size_t todo = req.Length - total; if (todo > chunkSize) todo = chunkSize; if (user > (uintptr_t)-1 - total) { s = NTLSTATUS_INVALID_PARAMETER; break; } s = CopyFromUser(kbuf, (const void*)(user + total), todo); if (!KSUCCESS(s)) break; size_t written = 0; s = IoWriteFile(file, kbuf, todo, &written); if (!KSUCCESS(s)) break; total += written; if (written < todo) break; } MmFreePool(kbuf); ObDereferenceObject(&file->Header); if (req.TransferredOut) { KSTATUS cs = KiCopySizeToUser(req.TransferredOut, total); if (KSUCCESS(s)) s = cs; } return s; } static void SyscallUserAsyncKernelRoutine(KAPC* apc, void** normalRoutine, void** normalContext, void** systemArgument1, void** systemArgument2) { (void)normalRoutine; (void)normalContext; (void)systemArgument1; (void)systemArgument2; SYS_USER_ASYNC_APC_CONTEXT* ctx = CONTAINING_RECORD(apc, SYS_USER_ASYNC_APC_CONTEXT, KernelApc); DbgPrint("USER/APC async: kernel routine ctx=0x%016llX iosb=0x%016llX status=%u info=%llu irql=%u\n", (uint64_t)ctx, (uint64_t)(uintptr_t)apc->SystemArgument1, (uint32_t)(uintptr_t)apc->SystemArgument2, apc->SystemArgument1 ? ((SYS_IO_STATUS_BLOCK_K*)apc->SystemArgument1)->Information : 0, KeGetCurrentIrql()); } // Implements the user Async Apc Context Free operation. static void SyscallUserAsyncApcContextFree(SYS_USER_ASYNC_APC_CONTEXT* ctx) { if (!ctx) return; if (ctx->TargetThread) { ObDereferenceObject(&ctx->TargetThread->Header); ctx->TargetThread = (THREAD*)0; } MmFreePool(ctx); } // Implements the user Async Apc Rundown operation. static void SyscallUserAsyncApcRundown(KAPC* apc) { if (!apc) return; SYS_USER_ASYNC_APC_CONTEXT* ctx = CONTAINING_RECORD(apc, SYS_USER_ASYNC_APC_CONTEXT, KernelApc); SyscallUserAsyncApcContextFree(ctx); } static void SyscallUserAsyncNormalRoutine(void* normalContext, void* systemArgument1, void* systemArgument2) { SYS_USER_ASYNC_APC_CONTEXT* ctx = (SYS_USER_ASYNC_APC_CONTEXT*)normalContext; SYS_IO_STATUS_BLOCK_K* iosb = (SYS_IO_STATUS_BLOCK_K*)systemArgument1; uint64_t information = iosb ? iosb->Information : 0; (void)systemArgument2; if (ctx && ctx->UserRoutine) { KSTATUS s = KeQueueUserApc(ctx->TargetThread, (void*)ctx->UserRoutine, ctx->UserContext, iosb, (void*)(uintptr_t)information, KE_USER_APC_FLAG_NONE); if (!KSUCCESS(s)) { DbgPrint("USER/APC async: KeQueueUserApc failed status=%u\n", s); } } SyscallUserAsyncApcContextFree(ctx); } // Implements the async File Common operation. static uint64_t SyscallAsyncFileCommon(const SYS_ASYNC_FILE_REQUEST* request, bool write) { if (!request) return NTLSTATUS_INVALID_PARAMETER; SYS_ASYNC_FILE_REQUEST_K req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags) return NTLSTATUS_INVALID_PARAMETER; if (!req.Iosb) return NTLSTATUS_INVALID_PARAMETER; if (!req.Buffer && req.Length) return NTLSTATUS_INVALID_PARAMETER; if (req.Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; if (req.RequestHandleOut) { s = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut)); if (!KSUCCESS(s)) return s; } KSTATUS fileHandleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject( req.FileHandle, ObjectTypeFile, write ? SYS_FILE_RIGHT_WRITE_DATA : SYS_FILE_RIGHT_READ_DATA, &fileHandleStatus); if (!file) return fileHandleStatus; EVENT* event = (EVENT*)0; if (req.EventHandle) { KSTATUS eventHandleStatus = NTLSTATUS_SUCCESS; event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, SYS_EVENT_RIGHT_SIGNAL, &eventHandleStatus); if (!event) { ObDereferenceObject(&file->Header); return eventHandleStatus; } } KAPC* completionApc = (KAPC*)0; SYS_USER_ASYNC_APC_CONTEXT* apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)0; if (req.ApcRoutine) { THREAD* thread = KeGetCurrentThread(); if (!thread || !thread->UserApcDispatcher) { if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return NTLSTATUS_INVALID_PARAMETER; } apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)MmAllocatePoolTag(sizeof(SYS_USER_ASYNC_APC_CONTEXT), MM_POOL_TAG('u', 'a', 'p', 'c')); if (!apcCtx) { if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return NTLSTATUS_NO_MEMORY; } memset(apcCtx, 0, sizeof(*apcCtx)); ObReferenceObject(&thread->Header); apcCtx->TargetThread = thread; apcCtx->UserRoutine = req.ApcRoutine; apcCtx->UserContext = req.ApcContext; KeInitializeApc(&apcCtx->KernelApc, thread, SyscallUserAsyncKernelRoutine, SyscallUserAsyncNormalRoutine, apcCtx); KeSetApcRundownRoutine(&apcCtx->KernelApc, SyscallUserAsyncApcRundown); completionApc = &apcCtx->KernelApc; } MM_LOCKED_USER_BUFFER* lockedBuffer = (MM_LOCKED_USER_BUFFER*)0; MM_LOCKED_USER_BUFFER* lockedIosb = (MM_LOCKED_USER_BUFFER*)0; s = MmLockUserBuffer(proc, req.Iosb, sizeof(*req.Iosb), 1, &lockedIosb); if (!KSUCCESS(s)) { if (apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return s; } if (req.Length) { s = MmLockUserBuffer(proc, req.Buffer, req.Length, write ? 0 : 1, &lockedBuffer); if (!KSUCCESS(s)) { MmUnlockUserBuffer(lockedIosb); if (apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return s; } } IO_ASYNC_FILE_REQUEST_OBJECT* asyncReq = (IO_ASYNC_FILE_REQUEST_OBJECT*)0; if (write) { s = IoWriteFileAsyncKernel(file, req.Buffer, req.Length, req.Offset, (IO_STATUS_BLOCK*)req.Iosb, event, completionApc, lockedBuffer, lockedIosb, &asyncReq); } else { s = IoReadFileAsyncKernel(file, req.Buffer, req.Length, req.Offset, (IO_STATUS_BLOCK*)req.Iosb, event, completionApc, lockedBuffer, lockedIosb, &asyncReq); } lockedBuffer = (MM_LOCKED_USER_BUFFER*)0; lockedIosb = (MM_LOCKED_USER_BUFFER*)0; if (s == NTLSTATUS_PENDING && req.RequestHandleOut && asyncReq) { SYS_HANDLE requestHandle = SYS_HANDLE_INVALID; KSTATUS hs = KeCreateHandle(proc, IoAsyncFileRequestObject(asyncReq), KeGetValidHandleRights(ObjectTypeAsyncIo), &requestHandle); if (!KSUCCESS(hs)) { (void)IoCancelAsyncFileRequest(asyncReq); ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return hs; } KSTATUS cs = CopyToUser(req.RequestHandleOut, &requestHandle, sizeof(requestHandle)); if (!KSUCCESS(cs)) { (void)IoCancelAsyncFileRequest(asyncReq); KeCloseHandle(proc, requestHandle); ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return cs; } } else if (req.RequestHandleOut) { SYS_HANDLE zeroHandle = SYS_HANDLE_INVALID; KSTATUS cs = CopyToUser(req.RequestHandleOut, &zeroHandle, sizeof(zeroHandle)); if (!KSUCCESS(cs)) { if (asyncReq) ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (s != NTLSTATUS_PENDING && apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return cs; } } if (asyncReq) ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); // drop creator reference if (s != NTLSTATUS_PENDING && apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return s; } // Implements SYS_READ_FILE_ASYNC. static uint64_t SyscallReadFileAsync(const SYS_ASYNC_FILE_REQUEST* request) { return SyscallAsyncFileCommon(request, false); } // Implements SYS_WRITE_FILE_ASYNC. static uint64_t SyscallWriteFileAsync(const SYS_ASYNC_FILE_REQUEST* request) { return SyscallAsyncFileCommon(request, true); } // Implements SYS_CANCEL_IO. static uint64_t SyscallCancelIo(SYS_HANDLE asyncRequestHandle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; IO_ASYNC_REQUEST_OBJECT* req = (IO_ASYNC_REQUEST_OBJECT*)KiHandleToObject( asyncRequestHandle, ObjectTypeAsyncIo, SYS_ASYNC_IO_RIGHT_CANCEL, &handleStatus); if (!req) return handleStatus; KSTATUS s = IoCancelAsyncRequest(req); ObDereferenceObject(IoAsyncRequestObject(req)); return s; } // Implements SYS_TRUNCATE_FILE. static uint64_t SyscallTruncateFile(SYS_HANDLE handle, uint64_t newSize) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(handle, ObjectTypeFile, SYS_FILE_RIGHT_WRITE_DATA, &handleStatus); if (!file) return handleStatus; KSTATUS s = IoTruncateFile(file, newSize); ObDereferenceObject(&file->Header); return s; } // Implements SYS_FLUSH_FILE. static uint64_t SyscallFlushFile(SYS_HANDLE handle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(handle, ObjectTypeFile, SYS_FILE_RIGHT_WRITE_DATA, &handleStatus); if (!file) return handleStatus; KSTATUS s = IoFlushFile(file); ObDereferenceObject(&file->Header); return s; } // Implements SYS_GET_FILE_POS. static uint64_t SyscallGetFilePos(SYS_HANDLE handle, uint64_t* positionOut) { if (!positionOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(handle, ObjectTypeFile, SYS_RIGHT_QUERY, &handleStatus); if (!file) return handleStatus; uintptr_t flags = KeAcquireSpinlock(&file->Lock); uint64_t pos = file->Offset; KeReleaseSpinlock(&file->Lock, flags); ObDereferenceObject(&file->Header); return KiCopyU64ToUser(positionOut, pos); } // Implements SYS_SEEK_FILE. static uint64_t SyscallSeekFile(SYS_HANDLE handle, uint64_t offset) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(handle, ObjectTypeFile, SYS_RIGHT_QUERY, &handleStatus); if (!file) return handleStatus; KSTATUS s = IoSeekFile(file, offset); ObDereferenceObject(&file->Header); return s; } // Implements SYS_GET_FILE_SIZE. static uint64_t SyscallGetFileSize(SYS_HANDLE handle, uint64_t* sizeOut) { if (!sizeOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(handle, ObjectTypeFile, SYS_RIGHT_QUERY, &handleStatus); if (!file) return handleStatus; uint64_t sz = IoGetFileSize(file); ObDereferenceObject(&file->Header); return KiCopyU64ToUser(sizeOut, sz); } // Implements SYS_GET_FILE_STAT. static uint64_t SyscallGetFileStat(SYS_HANDLE handle, SYS_FILE_STAT* statOut) { if (!statOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(handle, ObjectTypeFile, SYS_RIGHT_QUERY, &handleStatus); if (!file) return handleStatus; SYS_FILE_STAT stat; KSTATUS s = IoGetFileStat(file, &stat); ObDereferenceObject(&file->Header); if (!KSUCCESS(s)) return s; return CopyToUser(statOut, &stat, sizeof(stat)); } // Implements SYS_OPEN_DISPLAY_CLOCK. static uint64_t SyscallOpenDisplayClock(const SYS_OPEN_DISPLAY_CLOCK_REQUEST* request) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_DISPLAY_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; SYS_OPEN_DISPLAY_CLOCK_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags != SYS_DISPLAY_CLOCK_FLAG_NONE || !req.HandleOut) return NTLSTATUS_INVALID_PARAMETER; DISPLAY_CLOCK_OBJECT* clock = (DISPLAY_CLOCK_OBJECT*)0; s = KeDisplayOpenClock(req.PeriodMilliseconds, &clock); if (!KSUCCESS(s)) return s; SYS_HANDLE handle = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&clock->Header, KeGetValidHandleRights(ObjectTypeDisplayClock), &handle); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(req.HandleOut, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); } return s; } // Copies one optional surface rectangle list from user memory. A zero count means the whole surface. static KSTATUS KiCaptureUserSurfaceRects(const DISPLAY_RECT* userRects, uint32_t rectCount, DISPLAY_RECT* rectsOut) { if (rectCount > SYS_SURFACE_MAX_RECTS || (rectCount && (!userRects || !rectsOut))) return NTLSTATUS_INVALID_PARAMETER; if (!rectCount) return NTLSTATUS_SUCCESS; return CopyFromUser(rectsOut, userRects, (size_t)rectCount * sizeof(DISPLAY_RECT)); } // Implements SYS_CREATE_SURFACE by creating storage and returning its root capability. static uint64_t SyscallCreateSurface(const SYS_CREATE_SURFACE_REQUEST* request) { SYS_CREATE_SURFACE_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (!req.HandleOut) return NTLSTATUS_INVALID_PARAMETER; OBJECT* surface = (OBJECT*)0; status = KeSurfaceCreate(req.Width, req.Height, req.Format, req.Flags, &surface); if (!KSUCCESS(status)) return status; SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KiAllocHandleForCurrentWithRights(surface, KeGetValidHandleRights(ObjectTypeSurface), &handle); if (!KSUCCESS(status)) return status; status = CopyToUser(req.HandleOut, &handle, sizeof(handle)); if (!KSUCCESS(status)) KeCloseHandle(KeGetCurrentProcess(), handle); return status; } // Implements SYS_MAP_SURFACE. Each process receives its own VAD over shared backing frames. static uint64_t SyscallMapSurface(SYS_HANDLE surfaceHandle, uint32_t flags, void** baseOut, uint32_t* pitchBytesOut) { if (!baseOut || !pitchBytesOut || !(flags & SYS_SURFACE_MAP_READ) || (flags & ~(SYS_SURFACE_MAP_READ | SYS_SURFACE_MAP_WRITE))) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS rights = SYS_SURFACE_RIGHT_MAP_READ; if (flags & SYS_SURFACE_MAP_WRITE) rights |= SYS_SURFACE_RIGHT_MAP_WRITE; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* surface = KiHandleToObject(surfaceHandle, ObjectTypeSurface, rights, &handleStatus); if (!surface) return handleStatus; void* userBase = (void*)0; uint32_t pitchBytes = 0; KSTATUS status = KeSurfaceMap(KeGetCurrentProcess(), surface, flags, &userBase, &pitchBytes); if (KSUCCESS(status)) status = CopyToUser(pitchBytesOut, &pitchBytes, sizeof(pitchBytes)); if (KSUCCESS(status)) status = CopyToUser(baseOut, &userBase, sizeof(userBase)); if (!KSUCCESS(status) && userBase) (void)KeSurfaceUnmap(KeGetCurrentProcess(), userBase); ObDereferenceObject(surface); return status; } // Implements SYS_UNMAP_SURFACE without accepting arbitrary VADs as surface mappings. static uint64_t SyscallUnmapSurface(void* base) { return KeSurfaceUnmap(KeGetCurrentProcess(), base); } // Implements SYS_QUERY_SURFACE. static uint64_t SyscallQuerySurface(SYS_HANDLE surfaceHandle, SYS_SURFACE_DESC* descOut) { if (!descOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* surface = KiHandleToObject(surfaceHandle, ObjectTypeSurface, SYS_RIGHT_QUERY, &handleStatus); if (!surface) return handleStatus; SYS_SURFACE_DESC desc; KSTATUS status = KeSurfaceQuery(surface, &desc); ObDereferenceObject(surface); if (!KSUCCESS(status)) return status; return CopyToUser(descOut, &desc, sizeof(desc)); } // Implements SYS_COMMIT_SURFACE. Commit announces new pixels and never touches scanout. static uint64_t SyscallCommitSurface(SYS_HANDLE surfaceHandle, const DISPLAY_RECT* userRects, uint32_t rectCount, uint64_t* generationOut) { DISPLAY_RECT rects[SYS_SURFACE_MAX_RECTS]; KSTATUS status = KiCaptureUserSurfaceRects(userRects, rectCount, rects); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; OBJECT* surface = KiHandleToObject(surfaceHandle, ObjectTypeSurface, SYS_SURFACE_RIGHT_COMMIT, &handleStatus); if (!surface) return handleStatus; uint64_t generation = 0; status = KeSurfaceCommit(surface, rectCount ? rects : (DISPLAY_RECT*)0, rectCount, &generation); ObDereferenceObject(surface); if (KSUCCESS(status) && generationOut) status = CopyToUser(generationOut, &generation, sizeof(generation)); return status; } // Clears the legacy text-console display after its caller has already been authorized. static KSTATUS KiConsoleClear(uint32_t color) { return KeDisplayClear(color); } // Returns the current console geometry and cursor state after authorization. static KSTATUS KiConsoleQueryInfo(SYS_CONSOLE_INFO* infoOut) { if (!infoOut) return NTLSTATUS_INVALID_PARAMETER; KE_CONSOLE_INFO ki; KSTATUS status = KeDisplayGetConsoleInfo(&ki); if (!KSUCCESS(status)) return status; SYS_CONSOLE_INFO out; memset(&out, 0, sizeof(out)); out.Columns = ki.Columns; out.Rows = ki.Rows; out.CursorX = ki.CursorX; out.CursorY = ki.CursorY; out.Attribute = ki.Attribute; return CopyToUser(infoOut, &out, sizeof(out)); } // Updates the legacy text-console cursor after authorization. static KSTATUS KiConsoleSetCursor(int32_t x, int32_t y) { if (x < 0 || y < 0) return NTLSTATUS_INVALID_PARAMETER; return KeDisplaySetConsoleCursor(x, y); } // Draws one console glyph at explicit pixel coordinates after authorization. static KSTATUS KiConsolePutCharacter(const SYS_PUT_SCREEN_CHAR_REQUEST* request) { SYS_PUT_SCREEN_CHAR_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Reserved[0] || req.Reserved[1] || req.Reserved[2] || req.X < 0 || req.Y < 0) return NTLSTATUS_INVALID_PARAMETER; return KeDisplayPutChar(req.Character, req.X, req.Y, req.Foreground, req.Background); } // Fills console characters over one linear range after authorization. static KSTATUS KiConsoleFillCharacter(const SYS_FILL_CONSOLE_CHARACTER_REQUEST* request) { SYS_FILL_CONSOLE_CHARACTER_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Reserved[0] || req.Reserved[1] || req.Reserved[2] || req.X < 0 || req.Y < 0) return NTLSTATUS_INVALID_PARAMETER; size_t written = 0; status = KeDisplayFillConsoleChars(req.Character, req.X, req.Y, req.Length, &written); if (req.WrittenOut) { KSTATUS copyStatus = KiCopySizeToUser(req.WrittenOut, written); if (KSUCCESS(status) && !KSUCCESS(copyStatus)) status = copyStatus; } return status; } // Fills console attributes over one linear range after authorization. static KSTATUS KiConsoleFillAttribute(const SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST* request) { SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Reserved != 0 || req.X < 0 || req.Y < 0) return NTLSTATUS_INVALID_PARAMETER; size_t written = 0; status = KeDisplayFillConsoleAttrs(req.Attribute, req.X, req.Y, req.Length, &written); if (req.WrittenOut) { KSTATUS copyStatus = KiCopySizeToUser(req.WrittenOut, written); if (KSUCCESS(status) && !KSUCCESS(copyStatus)) status = copyStatus; } return status; } // Implements SYS_CLEAR_SCREEN through the legacy ambient console privilege. static uint64_t SyscallClearScreen(uint32_t color) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsoleClear(color); } // Implements SYS_GET_SCREEN_SIZE. static uint64_t SyscallGetScreenSize(int32_t* widthOut, int32_t* heightOut) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; int32_t w = 0, h = 0; KSTATUS gs = KeDisplayGetSize(&w, &h); if (!KSUCCESS(gs)) return gs; if (widthOut) { KSTATUS s = KiCopyInt32ToUser(widthOut, w); if (!KSUCCESS(s)) return s; } if (heightOut) { KSTATUS s = KiCopyInt32ToUser(heightOut, h); if (!KSUCCESS(s)) return s; } return NTLSTATUS_SUCCESS; } // Implements SYS_PUT_SCREEN_CHAR through the legacy ambient console privilege. static uint64_t SyscallPutScreenChar(const SYS_PUT_SCREEN_CHAR_REQUEST* request) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsolePutCharacter(request); } // Implements SYS_GET_CONSOLE_INFO through the legacy ambient console privilege. static uint64_t SyscallGetConsoleInfo(SYS_CONSOLE_INFO* infoOut) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsoleQueryInfo(infoOut); } // Implements SYS_SET_CONSOLE_CURSOR through the legacy ambient console privilege. static uint64_t SyscallSetConsoleCursor(int32_t x, int32_t y) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsoleSetCursor(x, y); } // Implements SYS_FILL_CONSOLE_CHARACTER through the legacy ambient console privilege. static uint64_t SyscallFillConsoleCharacter(const SYS_FILL_CONSOLE_CHARACTER_REQUEST* request) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsoleFillCharacter(request); } // Implements SYS_FILL_CONSOLE_ATTRIBUTE through the legacy ambient console privilege. static uint64_t SyscallFillConsoleAttribute(const SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST* request) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsoleFillAttribute(request); } // Implements the capability-scoped raw input-manager stream used by RSSRV. static uint64_t SyscallReadInputManagerEvent(const SYS_INPUT_MANAGER_READ_REQUEST* request) { SYS_INPUT_MANAGER_READ_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_INPUT_MANAGER_EVENT_FLAG_NONE || !req.EventOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_INPUT_MANAGER* manager = (KE_INPUT_MANAGER*)KiHandleToObject( req.ManagerHandle, ObjectTypeInputManager, SYS_INPUT_MANAGER_RIGHT_READ, &handleStatus); if (!manager) return handleStatus; SYS_INPUT_MANAGER_EVENT event; status = KeInputManagerReadEvent(manager, req.TimeoutMilliseconds, &event); ObDereferenceObject(&manager->Header); if (!KSUCCESS(status)) return status; return CopyToUser(req.EventOut, &event, sizeof(event)); } // Returns monotonic ticks since boot. static uint64_t SyscallGetTick(void) { return gHal ? gHal->Timer.QueryTicks() : 0; } // Copies the frequency used by SysGetTick. static uint64_t SyscallGetTickFrequency(uint64_t* frequencyOut) { if (!gHal || !gHal->Timer.QueryTickFrequency) return NTLSTATUS_DEVICE_ERROR; uint64_t frequency = gHal->Timer.QueryTickFrequency(); if (!frequency) return NTLSTATUS_DEVICE_ERROR; return KiCopyU64ToUser(frequencyOut, frequency); } // Implements SYS_GET_SYSTEM_TIME. static uint64_t SyscallGetSystemTime(SYS_SYSTEM_TIME* timeOut) { if (!timeOut) return NTLSTATUS_INVALID_PARAMETER; SYS_SYSTEM_TIME t; KSTATUS s = KeQuerySystemTime(&t); if (!KSUCCESS(s)) return s; return CopyToUser(timeOut, &t, sizeof(t)); } // Implements SYS_PRINT_DEBUG. static uint64_t SyscallDebugPrint(const SYS_PRINT_DEBUG_REQUEST* request) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_DEBUG_OUTPUT); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; SYS_PRINT_DEBUG_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Text.Length == 0) return NTLSTATUS_SUCCESS; if (!req.Text.Data) return NTLSTATUS_INVALID_PARAMETER; if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return NTLSTATUS_BUFFER_TOO_SMALL; char chunk[512]; size_t offset = 0; while (offset < req.Text.Length) { size_t todo = req.Text.Length - offset; if (todo > sizeof(chunk)) todo = sizeof(chunk); s = CopyFromUser(chunk, req.Text.Data + offset, todo); if (!KSUCCESS(s)) return s; DbgPrint("%v", SysSvFromParts(chunk, todo)); offset += todo; } return NTLSTATUS_SUCCESS; } // Implements SYS_QUERY_TRAP_FRAME. static uint64_t SyscallQueryTrapFrame(SYS_TRAP_FRAME_INFO* infoOut) { if (!infoOut) return NTLSTATUS_INVALID_PARAMETER; uint32_t userSize = 0; KSTATUS s = CopyFromUser(&userSize, infoOut, sizeof(userSize)); if (!KSUCCESS(s)) return s; SYS_TRAP_FRAME_INFO_K trapInfo; memset(&trapInfo, 0, sizeof(trapInfo)); trapInfo.Size = userSize; s = KeQueryCurrentTrapFrame(&trapInfo); size_t copySize = (s == NTLSTATUS_BUFFER_TOO_SMALL) ? sizeof(trapInfo.Size) : sizeof(trapInfo); KSTATUS cs = CopyToUser(infoOut, &trapInfo, copySize); if (!KSUCCESS(cs)) return cs; return s; } // Begins one process-construction memory operation without racing the start transition. static KSTATUS KiBeginProcessBuildOperation(PROCESS* process) { if (!process) return NTLSTATUS_INVALID_PARAMETER; for (;;) { if (NtlAtomicLoad32(&process->BuildState) != KE_PROCESS_BUILD_CONSTRUCTING) return NTLSTATUS_ACCESS_DENIED; NtlAtomicIncrement32(&process->BuildOperations); if (NtlAtomicLoad32(&process->BuildState) == KE_PROCESS_BUILD_CONSTRUCTING) return NTLSTATUS_SUCCESS; NtlAtomicDecrement32(&process->BuildOperations); } } // Ends one process-construction memory operation. static void KiEndProcessBuildOperation(PROCESS* process) { if (!process) return; int32_t remaining = NtlAtomicDecrement32(&process->BuildOperations); if (remaining == 0 && KeIsProcessTerminating(process)) (void)KeTerminateProcess(process, process->ExitCode); } // Copies the caller-selected security context for one child process. static KSTATUS KiReferenceChildSecurityContext(SYS_HANDLE contextHandle, SECURITY_CONTEXT** contextOut) { if (!contextOut) return NTLSTATUS_INVALID_PARAMETER; *contextOut = (SECURITY_CONTEXT*)0; PROCESS* parent = KeGetCurrentProcess(); if (!parent || !parent->SecurityContext) return NTLSTATUS_FAILURE; if (contextHandle == SYS_SECURITY_CONTEXT_CURRENT) { *contextOut = parent->SecurityContext; ObReferenceObject(&(*contextOut)->Header); return NTLSTATUS_SUCCESS; } KSTATUS status = NTLSTATUS_SUCCESS; SECURITY_CONTEXT* context = (SECURITY_CONTEXT*)KiHandleToObject(contextHandle, ObjectTypeSecurityContext, SYS_SECURITY_CONTEXT_RIGHT_ASSIGN, &status); if (!context) return status; *contextOut = context; return NTLSTATUS_SUCCESS; } // Implements SYS_CREATE_PROCESS_OBJECT. The returned process has no runnable thread. static uint64_t SyscallCreateProcessObject(const SYS_CREATE_PROCESS_OBJECT_REQUEST* request) { SYS_CREATE_PROCESS_OBJECT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_CREATE_PROCESS_OBJECT_FLAG_NONE || !req.ProcessHandleOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.ProcessHandleOut, sizeof(*req.ProcessHandleOut)); if (!KSUCCESS(status)) return status; char imagePathBuf[SYSCALL_STRING_MAX]; char commandLineBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW imagePath; SYS_STRING_VIEW commandLine; status = KiImportUserTextView(req.ImagePath, imagePathBuf, sizeof(imagePathBuf), &imagePath, true); if (!KSUCCESS(status)) return status; if (imagePath.Length == 0) return NTLSTATUS_INVALID_PARAMETER; status = KiImportUserTextView(req.CommandLine, commandLineBuf, sizeof(commandLineBuf), &commandLine, true); if (!KSUCCESS(status)) return status; SECURITY_CONTEXT* childContext = (SECURITY_CONTEXT*)0; status = KiReferenceChildSecurityContext(req.SecurityContext, &childContext); if (!KSUCCESS(status)) return status; PROCESS* child = (PROCESS*)0; status = KeCreateProcess(imagePathBuf, childContext, &child); ObDereferenceObject(&childContext->Header); if (!KSUCCESS(status)) return status; child->BuildState = KE_PROCESS_BUILD_CONSTRUCTING; child->BuildOperations = 0; PROCESS* parent = KeGetCurrentProcess(); status = KeSetupUserProcessEnvironment(child, imagePath, commandLine); if (!KSUCCESS(status)) { ObDereferenceObject(&child->Header); return status; } SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KeCreateHandle(parent, &child->Header, KeGetValidHandleRights(ObjectTypeProcess), &handle); if (KSUCCESS(status)) { status = KiCopyHandleToUser(req.ProcessHandleOut, handle); if (!KSUCCESS(status)) KeCloseHandle(parent, handle); } ObDereferenceObject(&child->Header); return status; } // Implements SYS_ALLOC_PROCESS_VM. The shared user VA is reachable by the builder but owned by the child VAD. static uint64_t SyscallAllocProcessVm(const SYS_ALLOC_PROCESS_VM_REQUEST* request) { SYS_ALLOC_PROCESS_VM_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (!req.PageCount || !req.BaseOut || req.Flags != SYS_VM_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; if (req.PageCount > ((size_t)-1) / PAGE_SIZE) return NTLSTATUS_NO_MEMORY; status = MmProbeUserWrite(req.BaseOut, sizeof(*req.BaseOut)); if (!KSUCCESS(status)) return status; uint64_t pageFlags = 0; status = KiVmProtectToPageFlags(req.Protect, &pageFlags); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PROCESS* child = (PROCESS*)KiHandleToObject(req.ProcessHandle, ObjectTypeProcess, SYS_PROCESS_RIGHT_BUILD, &handleStatus); if (!child) return handleStatus; status = KiBeginProcessBuildOperation(child); if (!KSUCCESS(status)) { ObDereferenceObject(&child->Header); return status; } void* base = (void*)0; status = MmVadCreateCommitted(child, req.PageCount * PAGE_SIZE, PAGE_SIZE, pageFlags, MM_VAD_ZERO, MmVadGeneric, &base); KiEndProcessBuildOperation(child); if (KSUCCESS(status)) { KSTATUS copyStatus = CopyToUser(req.BaseOut, &base, sizeof(base)); if (!KSUCCESS(copyStatus)) { (void)MmVadDestroy(child, base); status = copyStatus; } } ObDereferenceObject(&child->Header); return status; } // Implements SYS_PROTECT_PROCESS_VM. static uint64_t SyscallProtectProcessVm(SYS_HANDLE processHandle, void* base, size_t pageCount, uint32_t protect) { if (!base || ((uintptr_t)base & (PAGE_SIZE - 1u)) != 0 || !pageCount || pageCount > ((size_t)-1) / PAGE_SIZE) return NTLSTATUS_INVALID_PARAMETER; uint64_t pageFlags = 0; KSTATUS status = KiVmProtectToPageFlags(protect, &pageFlags); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PROCESS* child = (PROCESS*)KiHandleToObject(processHandle, ObjectTypeProcess, SYS_PROCESS_RIGHT_BUILD, &handleStatus); if (!child) return handleStatus; status = KiBeginProcessBuildOperation(child); if (KSUCCESS(status)) { status = MmVadProtect(child, base, pageCount * PAGE_SIZE, pageFlags); KiEndProcessBuildOperation(child); } ObDereferenceObject(&child->Header); return status; } // Implements SYS_START_PROCESS. Start is a one-way transition from Constructing to Started. static uint64_t SyscallStartProcess(const SYS_START_PROCESS_REQUEST* request) { SYS_START_PROCESS_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_START_PROCESS_FLAG_NONE || !req.MainImageBase || !req.MainImageSize || !req.EntryPoint || req.StartupCapabilityCount > SYS_STARTUP_CAPABILITY_MAX || (req.StartupCapabilityCount && !req.StartupCapabilities)) return NTLSTATUS_INVALID_PARAMETER; SYS_STARTUP_CAPABILITY capabilities[SYS_STARTUP_CAPABILITY_MAX]; memset(capabilities, 0, sizeof(capabilities)); if (req.StartupCapabilityCount) { status = CopyFromUser(capabilities, req.StartupCapabilities, (size_t)req.StartupCapabilityCount * sizeof(capabilities[0])); if (!KSUCCESS(status)) return status; } KSTATUS handleStatus = NTLSTATUS_SUCCESS; PROCESS* child = (PROCESS*)KiHandleToObject(req.ProcessHandle, ObjectTypeProcess, SYS_PROCESS_RIGHT_BUILD, &handleStatus); if (!child) return handleStatus; int buildActive = 0; status = KiBeginProcessBuildOperation(child); if (!KSUCCESS(status)) { bool terminating = KeIsProcessTerminating(child); ObDereferenceObject(&child->Header); return terminating ? NTLSTATUS_OBJECT_DEAD : status; } buildActive = 1; THREAD* thread = (THREAD*)0; if (NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_STARTING, KE_PROCESS_BUILD_CONSTRUCTING) != KE_PROCESS_BUILD_CONSTRUCTING) { status = KeIsProcessTerminating(child) ? NTLSTATUS_OBJECT_DEAD : NTLSTATUS_ACCESS_DENIED; goto done; } if (NtlAtomicLoad32(&child->BuildOperations) != 1) { (void)NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_CONSTRUCTING, KE_PROCESS_BUILD_STARTING); status = NTLSTATUS_WOULD_BLOCK; goto done; } for (uint32_t i = 0; i < req.StartupCapabilityCount && KSUCCESS(status); ++i) { if (!SysStartupCapabilityNameValid(capabilities[i].Name) || capabilities[i].Handle == SYS_HANDLE_INVALID) { status = NTLSTATUS_INVALID_PARAMETER; break; } for (uint32_t j = 0; j < i; ++j) { if (strcmp(capabilities[j].Name, capabilities[i].Name) == 0) { status = NTLSTATUS_OBJECT_NAME_COLLISION; break; } } if (!KSUCCESS(status)) break; OBJECT* object = (OBJECT*)0; status = KeReferenceHandle(child, capabilities[i].Handle, 0, 0, &object); if (KSUCCESS(status)) ObDereferenceObject(object); } void* regionBase = (void*)0; size_t regionSize = 0; uint64_t regionFlags = 0; MM_VAD_TYPE regionType = MmVadGeneric; uint32_t vadFlags = 0; if (KSUCCESS(status)) { size_t imageVadSize = MmVadGetSize(child, (const void*)req.MainImageBase); if (!imageVadSize || req.MainImageSize > imageVadSize || req.MainImageBase > UINTPTR_MAX - req.MainImageSize || req.EntryPoint < req.MainImageBase || req.EntryPoint >= req.MainImageBase + req.MainImageSize) status = NTLSTATUS_INVALID_PARAMETER; } if (KSUCCESS(status)) { status = MmVadQuery(child, (const void*)req.EntryPoint, ®ionBase, ®ionSize, ®ionFlags, ®ionType, &vadFlags); if (KSUCCESS(status) && (!(regionFlags & PAGE_PRESENT) || (regionFlags & PAGE_NX))) status = NTLSTATUS_ACCESS_DENIED; } if (KSUCCESS(status) && req.LoaderData) { status = MmVadQuery(child, req.LoaderData, ®ionBase, ®ionSize, ®ionFlags, ®ionType, &vadFlags); if (KSUCCESS(status) && (!(regionFlags & PAGE_PRESENT) || !(regionFlags & PAGE_USER))) status = NTLSTATUS_INVALID_PARAMETER; } if (KSUCCESS(status)) status = KeSetProcessStartupCapabilities(child, capabilities, req.StartupCapabilityCount); if (KSUCCESS(status)) status = KeSetProcessLoaderData(child, req.LoaderData); if (KSUCCESS(status) && !KeIsProcessTerminating(child)) { child->ImageBase = req.MainImageBase; child->ImageSize = req.MainImageSize; KE_THREAD_CREATE_PARAMETERS parameters = KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, child->Name); parameters.Flags = KE_THREAD_CREATE_FLAG_SUSPENDED; status = KeCreateThread(child, req.EntryPoint, 0, ¶meters, &thread); if (KSUCCESS(status)) { if (NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_STARTED, KE_PROCESS_BUILD_STARTING) != KE_PROCESS_BUILD_STARTING) { (void)KeCancelSuspendedThread(thread); status = NTLSTATUS_OBJECT_DEAD; } } } else if (KSUCCESS(status)) { status = NTLSTATUS_OBJECT_DEAD; } if (!KSUCCESS(status)) { child->ImageBase = 0; child->ImageSize = 0; (void)KeSetProcessStartupCapabilities(child, (const SYS_STARTUP_CAPABILITY*)0, 0); (void)KeSetProcessLoaderData(child, (const void*)0); (void)NtlAtomicCompareExchange32(&child->BuildState, KE_PROCESS_BUILD_CONSTRUCTING, KE_PROCESS_BUILD_STARTING); goto done; } KiEndProcessBuildOperation(child); buildActive = 0; if (KeIsProcessTerminating(child)) { status = NTLSTATUS_OBJECT_DEAD; } else { status = KeResumeThread(thread, (uint32_t*)0); if (!KSUCCESS(status) && !KeIsProcessTerminating(child)) (void)KeCancelSuspendedThread(thread); } ObDereferenceObject(&thread->Header); ObDereferenceObject(&child->Header); return status; done: if (thread) ObDereferenceObject(&thread->Header); if (buildActive) KiEndProcessBuildOperation(child); ObDereferenceObject(&child->Header); return status; } // Implements SYS_OPEN_PROCESS. static uint64_t SyscallOpenProcess(uint32_t pid, SYS_RIGHTS desiredAccess, SYS_HANDLE* handleOut) { if (!handleOut || desiredAccess == 0) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS validAccess = KeGetValidHandleRights(ObjectTypeProcess); if ((desiredAccess & ~validAccess) != 0) return NTLSTATUS_INVALID_PARAMETER; PROCESS* caller = KeGetCurrentProcess(); if (!caller) return NTLSTATUS_FAILURE; if (pid != caller->ProcessId || (desiredAccess & SYS_PROCESS_RIGHT_SECURITY_CONTEXT) != 0) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_PROCESS_OPEN_ANY); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; } KSTATUS s = MmProbeUserWrite(handleOut, sizeof(SYS_HANDLE)); if (!KSUCCESS(s)) return s; PROCESS* proc = KeGetProcessById(pid); if (!proc) return NTLSTATUS_NOT_FOUND; SYS_HANDLE handle = SYS_HANDLE_INVALID; s = KeCreateHandle(caller, &proc->Header, desiredAccess, &handle); ObDereferenceObject(&proc->Header); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(handleOut, handle); if (!KSUCCESS(s)) { KeCloseHandle(caller, handle); return s; } return NTLSTATUS_SUCCESS; } // Copies the process exit code. static uint64_t SyscallGetProcessExitCode(SYS_HANDLE handle, int32_t* exitCodeOut) { if (!exitCodeOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PROCESS* proc = (PROCESS*)KiHandleToObject(handle, ObjectTypeProcess, SYS_RIGHT_QUERY, &handleStatus); if (!proc) return handleStatus; int32_t exitCode = 0; KSTATUS s = KeGetProcessExitCode(proc, &exitCode); ObDereferenceObject(&proc->Header); if (!KSUCCESS(s)) return s; return KiCopyInt32ToUser(exitCodeOut, exitCode); } // Writes one user text span to the legacy kernel console after authorization. static KSTATUS KiConsoleWrite(const SYS_WRITE_CONSOLE_REQUEST* request) { SYS_WRITE_CONSOLE_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Text.Length == 0) return NTLSTATUS_SUCCESS; if (!req.Text.Data) return NTLSTATUS_INVALID_PARAMETER; if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return NTLSTATUS_BUFFER_TOO_SMALL; char chunk[512]; size_t offset = 0; while (offset < req.Text.Length) { size_t todo = req.Text.Length - offset; if (todo > sizeof(chunk)) todo = sizeof(chunk); status = CopyFromUser(chunk, req.Text.Data + offset, todo); if (!KSUCCESS(status)) return status; KePrintConsole(chunk, todo); offset += todo; } return KeDisplayFlushConsoleOutput(); } // Implements SYS_WRITE_CONSOLE through the legacy ambient console privilege. static uint64_t SyscallWriteConsole(const SYS_WRITE_CONSOLE_REQUEST* request) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_CONSOLE_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiConsoleWrite(request); } // Implements SYS_READ_DIR. // Returns NTLSTATUS_NO_MORE_FILES when the directory is exhausted. static uint64_t SyscallReadDir(SYS_HANDLE handle, SYS_DIR_ENTRY* entryOut) { if (!entryOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; DIR_OBJECT* dir = (DIR_OBJECT*)KiHandleToObject(handle, ObjectTypeDir, SYS_DIR_RIGHT_ENUMERATE, &handleStatus); if (!dir) return handleStatus; KDIR_ENTRY entry; KSTATUS s = IoReadDir(dir, &entry); ObDereferenceObject(&dir->Header); if (!KSUCCESS(s)) return s; return CopyToUser(entryOut, &entry, sizeof(entry)); } // Implements SYS_GET_PROCESS_PID. // Returns the numeric PID of a process identified by handle. // Used by posix_spawn to map the kernel handle back to a pid_t for waitpid. static uint64_t SyscallGetProcessPid(SYS_HANDLE handle, uint32_t* pidOut) { if (!pidOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PROCESS* proc = (PROCESS*)KiHandleToObject(handle, ObjectTypeProcess, SYS_RIGHT_QUERY, &handleStatus); if (!proc) return handleStatus; uint32_t pid = proc->ProcessId; ObDereferenceObject(&proc->Header); return KiCopyU32ToUser(pidOut, pid); } // Implements SYS_CREATE_PORT. static uint64_t SyscallCreatePort(const SYS_CREATE_PORT_REQUEST* request) { SYS_CREATE_PORT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_FLAG_NONE || req.Reserved != 0 || !req.HandleOut) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS validPeerRights = KeGetValidHandleRights(ObjectTypeProcess); if ((req.PeerProcessRights & ~validPeerRights) != 0) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.HandleOut, sizeof(*req.HandleOut)); if (!KSUCCESS(status)) return status; PORT* port = (PORT*)0; status = KePortCreate(req.Backlog, req.PeerProcessRights, &port); if (!KSUCCESS(status)) return status; SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KiAllocHandleForCurrentWithRights((OBJECT*)port, SYS_RIGHT_MANAGE | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_PORT_RIGHT_CONNECT | SYS_PORT_RIGHT_ACCEPT, &handle); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.HandleOut, handle); if (!KSUCCESS(status)) { PROCESS* process = KeGetCurrentProcess(); if (process) (void)KeCloseHandle(process, handle); } return status; } // Implements SYS_CONNECT_PORT. static uint64_t SyscallConnectPort(const SYS_CONNECT_PORT_REQUEST* request) { SYS_CONNECT_PORT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_CONNECT_FLAG_NONE || !req.EndpointHandleOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.EndpointHandleOut, sizeof(*req.EndpointHandleOut)); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* listener = (PORT*)KiHandleToObject(req.ListenerHandle, ObjectTypePort, SYS_PORT_RIGHT_CONNECT, &handleStatus); if (!listener) return handleStatus; PROCESS* process = KeGetCurrentProcess(); THREAD* thread = KeGetCurrentThread(); SYS_HANDLE endpoint = SYS_HANDLE_INVALID; status = process && thread ? KePortConnect(listener, process, thread, &endpoint) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)listener); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.EndpointHandleOut, endpoint); if (!KSUCCESS(status) && process) (void)KeCloseHandle(process, endpoint); return status; } // Implements SYS_ACCEPT_PORT. static uint64_t SyscallAcceptPort(const SYS_ACCEPT_PORT_REQUEST* request) { SYS_ACCEPT_PORT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_ACCEPT_FLAG_NONE || !req.EndpointHandleOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.EndpointHandleOut, sizeof(*req.EndpointHandleOut)); if (!KSUCCESS(status)) return status; if (req.ConnectionInfoOut) { status = KiProbeSizedUserOutput(req.ConnectionInfoOut, sizeof(*req.ConnectionInfoOut)); if (!KSUCCESS(status)) return status; } KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* listener = (PORT*)KiHandleToObject(req.ListenerHandle, ObjectTypePort, SYS_PORT_RIGHT_ACCEPT, &handleStatus); if (!listener) return handleStatus; PROCESS* process = KeGetCurrentProcess(); SYS_HANDLE endpoint = SYS_HANDLE_INVALID; SYS_PORT_CONNECTION_INFO info; memset(&info, 0, sizeof(info)); status = process ? KePortAccept(listener, process, req.TimeoutMilliseconds, &endpoint, &info) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)listener); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.EndpointHandleOut, endpoint); if (KSUCCESS(status) && req.ConnectionInfoOut) status = CopyToUser(req.ConnectionInfoOut, &info, sizeof(info)); if (!KSUCCESS(status) && process) (void)KeCloseHandle(process, endpoint); return status; } // Implements SYS_SEND_PORT. static uint64_t SyscallSendPort(const SYS_SEND_PORT_REQUEST* request) { SYS_SEND_PORT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_FLAG_NONE || req.PayloadLength > SYS_PORT_MAX_INLINE_BYTES || req.HandleCount > SYS_PORT_MAX_TRANSFER_HANDLES || (req.PayloadLength && !req.Payload)) return NTLSTATUS_INVALID_PARAMETER; SYS_PORT_HANDLE_TRANSFER transfers[SYS_PORT_MAX_TRANSFER_HANDLES]; status = KiImportPortTransfers(req.Handles, req.HandleCount, transfers); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* endpoint = (PORT*)KiHandleToObject(req.EndpointHandle, ObjectTypePort, SYS_PORT_RIGHT_SEND, &handleStatus); if (!endpoint) return handleStatus; PROCESS* process = KeGetCurrentProcess(); THREAD* thread = KeGetCurrentThread(); status = process && thread ? KePortSend(endpoint, process, thread, req.Payload, req.PayloadLength, transfers, req.HandleCount, req.TimeoutMilliseconds) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)endpoint); return status; } // Implements SYS_RECEIVE_PORT. static uint64_t SyscallReceivePort(const SYS_RECEIVE_PORT_REQUEST* request) { SYS_RECEIVE_PORT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_FLAG_NONE || !req.InfoOut || req.PayloadCapacity > SYS_PORT_MAX_INLINE_BYTES || req.HandleCapacity > SYS_PORT_MAX_TRANSFER_HANDLES || (req.PayloadCapacity && !req.Payload) || (req.HandleCapacity && !req.HandlesOut)) return NTLSTATUS_INVALID_PARAMETER; status = KiProbeSizedUserOutput(req.InfoOut, sizeof(*req.InfoOut)); if (!KSUCCESS(status)) return status; if (req.HandleCapacity) { status = MmProbeUserWrite(req.HandlesOut, sizeof(SYS_HANDLE) * req.HandleCapacity); if (!KSUCCESS(status)) return status; } KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* endpoint = (PORT*)KiHandleToObject(req.EndpointHandle, ObjectTypePort, SYS_PORT_RIGHT_RECEIVE, &handleStatus); if (!endpoint) return handleStatus; PROCESS* process = KeGetCurrentProcess(); SYS_PORT_MESSAGE_INFO info; SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES] = {0}; memset(&info, 0, sizeof(info)); status = process ? KePortReceive(endpoint, process, req.Payload, req.PayloadCapacity, handles, req.HandleCapacity, &info, req.TimeoutMilliseconds) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)endpoint); if (status == NTLSTATUS_BUFFER_TOO_SMALL) { KSTATUS copyStatus = CopyToUser(req.InfoOut, &info, sizeof(info)); return KSUCCESS(copyStatus) ? status : copyStatus; } if (!KSUCCESS(status)) return status; status = CopyToUser(req.InfoOut, &info, sizeof(info)); if (KSUCCESS(status) && info.HandleCount) status = CopyToUser(req.HandlesOut, handles, sizeof(SYS_HANDLE) * info.HandleCount); if (!KSUCCESS(status) && process) for (uint32_t i = 0; i < info.HandleCount; ++i) (void)KeCloseHandle(process, handles[i]); return status; } // Implements SYS_CALL_PORT. static uint64_t SyscallCallPort(const SYS_CALL_PORT_REQUEST* request) { SYS_CALL_PORT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_FLAG_NONE || req.RequestLength > SYS_PORT_MAX_INLINE_BYTES || req.ReplyCapacity > SYS_PORT_MAX_INLINE_BYTES || req.RequestHandleCount > SYS_PORT_MAX_TRANSFER_HANDLES || req.ReplyHandleCapacity > SYS_PORT_MAX_TRANSFER_HANDLES || (req.RequestLength && !req.RequestPayload) || (req.ReplyCapacity && !req.ReplyPayload) || (req.ReplyHandleCapacity && !req.ReplyHandlesOut)) return NTLSTATUS_INVALID_PARAMETER; if (req.ReplyInfoOut) { status = KiProbeSizedUserOutput(req.ReplyInfoOut, sizeof(*req.ReplyInfoOut)); if (!KSUCCESS(status)) return status; } if (req.ReplyHandleCapacity) { status = MmProbeUserWrite(req.ReplyHandlesOut, sizeof(SYS_HANDLE) * req.ReplyHandleCapacity); if (!KSUCCESS(status)) return status; } SYS_PORT_HANDLE_TRANSFER transfers[SYS_PORT_MAX_TRANSFER_HANDLES]; status = KiImportPortTransfers(req.RequestHandles, req.RequestHandleCount, transfers); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* endpoint = (PORT*)KiHandleToObject(req.EndpointHandle, ObjectTypePort, SYS_PORT_RIGHT_CALL, &handleStatus); if (!endpoint) return handleStatus; PROCESS* process = KeGetCurrentProcess(); THREAD* thread = KeGetCurrentThread(); SYS_PORT_MESSAGE_INFO info; SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES] = {0}; memset(&info, 0, sizeof(info)); status = process && thread ? KePortCall(endpoint, process, thread, req.RequestPayload, req.RequestLength, transfers, req.RequestHandleCount, req.ReplyPayload, req.ReplyCapacity, handles, req.ReplyHandleCapacity, &info, req.TimeoutMilliseconds) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)endpoint); if (!KSUCCESS(status)) return status; if (req.ReplyInfoOut) { status = CopyToUser(req.ReplyInfoOut, &info, sizeof(info)); if (!KSUCCESS(status)) goto cleanup_call_handles; } if (info.HandleCount) { status = CopyToUser(req.ReplyHandlesOut, handles, sizeof(SYS_HANDLE) * info.HandleCount); if (!KSUCCESS(status)) goto cleanup_call_handles; } return NTLSTATUS_SUCCESS; cleanup_call_handles: if (process) for (uint32_t i = 0; i < info.HandleCount; ++i) (void)KeCloseHandle(process, handles[i]); return status; } // Implements SYS_REPLY_PORT. static uint64_t SyscallReplyPort(const SYS_REPLY_PORT_REQUEST* request) { SYS_REPLY_PORT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_FLAG_NONE || req.MessageId == 0 || req.PayloadLength > SYS_PORT_MAX_INLINE_BYTES || req.HandleCount > SYS_PORT_MAX_TRANSFER_HANDLES || (req.PayloadLength && !req.Payload)) return NTLSTATUS_INVALID_PARAMETER; SYS_PORT_HANDLE_TRANSFER transfers[SYS_PORT_MAX_TRANSFER_HANDLES]; status = KiImportPortTransfers(req.Handles, req.HandleCount, transfers); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* endpoint = (PORT*)KiHandleToObject(req.EndpointHandle, ObjectTypePort, SYS_PORT_RIGHT_REPLY, &handleStatus); if (!endpoint) return handleStatus; PROCESS* process = KeGetCurrentProcess(); THREAD* thread = KeGetCurrentThread(); status = process && thread ? KePortReply(endpoint, process, thread, req.MessageId, req.Payload, req.PayloadLength, transfers, req.HandleCount) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)endpoint); return status; } // Implements SYS_DISCONNECT_PORT. static uint64_t SyscallDisconnectPort(SYS_HANDLE endpointHandle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* endpoint = (PORT*)KiHandleToObject(endpointHandle, ObjectTypePort, SYS_PORT_RIGHT_DISCONNECT, &handleStatus); if (!endpoint) return handleStatus; KSTATUS status = KePortDisconnect(endpoint); ObDereferenceObject((OBJECT*)endpoint); return status; } // Implements SYS_OPEN_PORT_PEER_PROCESS. static uint64_t SyscallOpenPortPeerProcess(SYS_HANDLE endpointHandle, SYS_RIGHTS desiredRights, SYS_HANDLE* processHandleOut) { if (!processHandleOut || desiredRights == 0) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS validRights = KeGetValidHandleRights(ObjectTypeProcess); if ((desiredRights & ~validRights) != 0) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(processHandleOut, sizeof(*processHandleOut)); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* endpoint = (PORT*)KiHandleToObject(endpointHandle, ObjectTypePort, SYS_PORT_RIGHT_OPEN_PEER_PROCESS, &handleStatus); if (!endpoint) return handleStatus; PROCESS* caller = KeGetCurrentProcess(); SYS_HANDLE processHandle = SYS_HANDLE_INVALID; status = caller ? KePortOpenPeerProcess(endpoint, caller, desiredRights, &processHandle) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)endpoint); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(processHandleOut, processHandle); if (!KSUCCESS(status) && caller) (void)KeCloseHandle(caller, processHandle); return status; } // Implements SYS_REGISTER_SYSTEM_SERVICE using a typed, attenuable registration authority. static uint64_t SyscallRegisterSystemService(SYS_HANDLE registrationHandle, SYS_HANDLE listenerHandle) { PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; OBJECT* authority = (OBJECT*)0; KSTATUS status = KeReferenceHandle(process, registrationHandle, ObjectTypeServiceRegistration, SYS_SERVICE_REGISTRATION_RIGHT_REGISTER, &authority); if (!KSUCCESS(status)) return status; SYS_SERVICE_ID serviceId = SYS_SERVICE_INVALID; status = KeSystemServiceRegistrationIdentity(authority, &serviceId); ObDereferenceObject(authority); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* listener = (PORT*)KiHandleToObject(listenerHandle, ObjectTypePort, SYS_PORT_RIGHT_ACCEPT, &handleStatus); if (!listener) return handleStatus; status = KeSystemServiceRegister(serviceId, listener); ObDereferenceObject((OBJECT*)listener); return status; } // Implements SYS_UNREGISTER_SYSTEM_SERVICE using the same typed authority. static uint64_t SyscallUnregisterSystemService(SYS_HANDLE registrationHandle, SYS_HANDLE listenerHandle) { PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; OBJECT* authority = (OBJECT*)0; KSTATUS status = KeReferenceHandle(process, registrationHandle, ObjectTypeServiceRegistration, SYS_SERVICE_REGISTRATION_RIGHT_UNREGISTER, &authority); if (!KSUCCESS(status)) return status; SYS_SERVICE_ID serviceId = SYS_SERVICE_INVALID; status = KeSystemServiceRegistrationIdentity(authority, &serviceId); ObDereferenceObject(authority); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; PORT* listener = (PORT*)KiHandleToObject(listenerHandle, ObjectTypePort, SYS_PORT_RIGHT_ACCEPT, &handleStatus); if (!listener) return handleStatus; status = KeSystemServiceUnregister(serviceId, listener); ObDereferenceObject((OBJECT*)listener); return status; } // Implements SYS_CONNECT_SYSTEM_SERVICE. static uint64_t SyscallConnectSystemService(const SYS_CONNECT_SYSTEM_SERVICE_REQUEST* request) { SYS_CONNECT_SYSTEM_SERVICE_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_PORT_CONNECT_FLAG_NONE || !req.EndpointHandleOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.EndpointHandleOut, sizeof(*req.EndpointHandleOut)); if (!KSUCCESS(status)) return status; PORT* listener = (PORT*)0; status = KeSystemServiceReference(req.ServiceId, &listener); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); THREAD* thread = KeGetCurrentThread(); SYS_HANDLE endpoint = SYS_HANDLE_INVALID; status = process && thread ? KePortConnect(listener, process, thread, &endpoint) : NTLSTATUS_FAILURE; ObDereferenceObject((OBJECT*)listener); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.EndpointHandleOut, endpoint); if (!KSUCCESS(status) && process) (void)KeCloseHandle(process, endpoint); return status; } // Implements SYS_QUERY_VOLUMES. The manager capability is the authority to observe mounted-volume lifecycle. static uint64_t SyscallQueryVolumes(const SYS_QUERY_VOLUMES_REQUEST* request) { SYS_QUERY_VOLUMES_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_VOLUME_QUERY_FLAG_NONE || !req.CountOut || !req.GenerationOut || (req.Capacity != 0 && !req.Volumes)) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.CountOut, sizeof(*req.CountOut)); if (!KSUCCESS(status)) return status; status = MmProbeUserWrite(req.GenerationOut, sizeof(*req.GenerationOut)); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; IO_VOLUME_MANAGER* manager = (IO_VOLUME_MANAGER*)KiHandleToObject( req.ManagerHandle, ObjectTypeVolumeManager, SYS_VOLUME_MANAGER_RIGHT_ENUMERATE, &handleStatus); if (!manager) return handleStatus; ObDereferenceObject(&manager->Header); IO_VOLUME_SNAPSHOT_ENTRY* snapshot = (IO_VOLUME_SNAPSHOT_ENTRY*)0; uint32_t count = 0; uint64_t generation = 0; status = IoSnapshotVolumes(&snapshot, &count, &generation); if (!KSUCCESS(status)) return status; KSTATUS countStatus = CopyToUser(req.CountOut, &count, sizeof(count)); KSTATUS generationStatus = CopyToUser(req.GenerationOut, &generation, sizeof(generation)); if (!KSUCCESS(countStatus) || !KSUCCESS(generationStatus)) { if (snapshot) MmFreePool(snapshot); return !KSUCCESS(countStatus) ? countStatus : generationStatus; } if (req.Capacity < count) { if (snapshot) MmFreePool(snapshot); return NTLSTATUS_BUFFER_TOO_SMALL; } if (count) { status = MmProbeUserWrite(req.Volumes, (size_t)count * sizeof(*req.Volumes)); if (!KSUCCESS(status)) { MmFreePool(snapshot); return status; } } for (uint32_t i = 0; i < count; ++i) { SYS_VOLUME_INFO info; memset(&info, 0, sizeof(info)); info.Size = sizeof(info); info.Version = SYS_VOLUME_ENUM_VERSION; info.InstanceId = snapshot[i].InstanceId; info.Flags = 0; status = CopyToUser(&req.Volumes[i], &info, sizeof(info)); if (!KSUCCESS(status)) { MmFreePool(snapshot); return status; } } if (snapshot) MmFreePool(snapshot); return NTLSTATUS_SUCCESS; } // Implements SYS_OPEN_VOLUME. The manager can mint only traversal/data authority, never capability-management authority. static uint64_t SyscallOpenVolume(SYS_HANDLE managerHandle, uint32_t instanceId, SYS_RIGHTS desiredRights, SYS_HANDLE* volumeHandleOut) { const SYS_RIGHTS exportable = SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER | SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY; if (!instanceId || !desiredRights || (desiredRights & ~exportable) != 0 || !volumeHandleOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(volumeHandleOut, sizeof(*volumeHandleOut)); if (!KSUCCESS(status)) return status; KSTATUS handleStatus = NTLSTATUS_SUCCESS; IO_VOLUME_MANAGER* manager = (IO_VOLUME_MANAGER*)KiHandleToObject( managerHandle, ObjectTypeVolumeManager, SYS_VOLUME_MANAGER_RIGHT_OPEN, &handleStatus); if (!manager) return handleStatus; ObDereferenceObject(&manager->Header); IO_VOLUME* volume = (IO_VOLUME*)0; status = IoReferenceVolumeByInstanceId(instanceId, &volume); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); SYS_HANDLE handle = SYS_HANDLE_INVALID; status = process ? KeCreateHandle(process, &volume->Header, desiredRights, &handle) : NTLSTATUS_FAILURE; IoReleaseVolume(volume); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(volumeHandleOut, handle); if (!KSUCCESS(status) && process) (void)KeCloseHandle(process, handle); return status; } // Implements SYS_WAIT_VOLUME_CHANGE. Generation comparison closes the query-to-wait race. static uint64_t SyscallWaitVolumeChange(SYS_HANDLE managerHandle, uint64_t observedGeneration, uint64_t timeoutMilliseconds) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; IO_VOLUME_MANAGER* manager = (IO_VOLUME_MANAGER*)KiHandleToObject( managerHandle, ObjectTypeVolumeManager, SYS_VOLUME_MANAGER_RIGHT_WAIT, &handleStatus); if (!manager) return handleStatus; ObDereferenceObject(&manager->Header); return IoWaitForVolumeChange(observedGeneration, timeoutMilliseconds); } // Implements SYS_DUPLICATE_HANDLE. // Duplicates a handle from the caller into the target process table. static uint64_t SyscallDuplicateHandle(const SYS_DUPLICATE_HANDLE_REQUEST* request) { SYS_DUPLICATE_HANDLE_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.TargetHandleOut) return NTLSTATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.TargetHandleOut, sizeof(SYS_HANDLE)); if (!KSUCCESS(s)) return s; PROCESS* caller = KeGetCurrentProcess(); if (!caller) return NTLSTATUS_FAILURE; KSTATUS targetStatus = NTLSTATUS_SUCCESS; PROCESS* target = (PROCESS*)KiHandleToObject(req.TargetProcessHandle, ObjectTypeProcess, SYS_PROCESS_RIGHT_INSERT_HANDLE, &targetStatus); if (!target) return targetStatus; SYS_HANDLE targetHandle = SYS_HANDLE_INVALID; s = KeDuplicateHandle(caller, req.SourceHandle, target, req.DesiredRights, req.Flags, &targetHandle); if (!KSUCCESS(s)) { ObDereferenceObject(&target->Header); return s; } s = KiCopyHandleToUser(req.TargetHandleOut, targetHandle); if (!KSUCCESS(s)) { KeCloseHandle(target, targetHandle); ObDereferenceObject(&target->Header); return s; } ObDereferenceObject(&target->Header); return NTLSTATUS_SUCCESS; } // Implements SYS_DERIVE_HANDLE. static uint64_t SyscallDeriveHandle(const SYS_DERIVE_HANDLE_REQUEST* request) { SYS_DERIVE_HANDLE_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (!req.DesiredRights || !req.DerivedHandleOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.DerivedHandleOut, sizeof(SYS_HANDLE)); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; SYS_HANDLE derived = SYS_HANDLE_INVALID; status = KeDeriveHandle(process, req.SourceHandle, req.DesiredRights, &derived); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.DerivedHandleOut, derived); if (!KSUCCESS(status)) (void)KeCloseHandle(process, derived); return status; } // Implements SYS_REVOKE_HANDLE. static uint64_t SyscallRevokeHandle(SYS_HANDLE handle) { PROCESS* process = KeGetCurrentProcess(); if (!process) return NTLSTATUS_FAILURE; return KeRevokeHandle(process, handle); } // Implements SYS_DERIVE_SECURITY_CONTEXT. static uint64_t SyscallDeriveSecurityContext(const SYS_DERIVE_SECURITY_CONTEXT_REQUEST* request) { SYS_DERIVE_SECURITY_CONTEXT_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_SECURITY_CONTEXT_FLAG_NONE || !req.ContextHandleOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.ContextHandleOut, sizeof(SYS_HANDLE)); if (!KSUCCESS(status)) return status; PROCESS* process = KeGetCurrentProcess(); if (!process || !process->SecurityContext) return NTLSTATUS_FAILURE; SECURITY_CONTEXT* source = (SECURITY_CONTEXT*)0; if (req.SourceContext == SYS_SECURITY_CONTEXT_CURRENT) { source = process->SecurityContext; ObReferenceObject(&source->Header); } else { source = (SECURITY_CONTEXT*)KiHandleToObject(req.SourceContext, ObjectTypeSecurityContext, SYS_SECURITY_CONTEXT_RIGHT_DERIVE, &status); if (!source) return status; } SECURITY_CONTEXT* derived = (SECURITY_CONTEXT*)0; status = SeDeriveSecurityContext(source, req.EffectivePrivileges, req.DelegablePrivileges, &derived); ObDereferenceObject(&source->Header); if (!KSUCCESS(status)) return status; SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KiAllocHandleForCurrentWithRights(&derived->Header, KeGetValidHandleRights(ObjectTypeSecurityContext), &handle); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(req.ContextHandleOut, handle); if (!KSUCCESS(status)) { KeCloseHandle(process, handle); return status; } return NTLSTATUS_SUCCESS; } // Implements SYS_QUERY_SECURITY_CONTEXT. static uint64_t SyscallQuerySecurityContext(SYS_HANDLE contextHandle, SYS_SECURITY_CONTEXT_INFO* infoOut) { if (!infoOut) return NTLSTATUS_INVALID_PARAMETER; uint32_t userSize = 0; KSTATUS status = CopyFromUser(&userSize, infoOut, sizeof(userSize)); if (!KSUCCESS(status)) return status; if (userSize != sizeof(SYS_SECURITY_CONTEXT_INFO)) return NTLSTATUS_INVALID_PARAMETER; PROCESS* process = KeGetCurrentProcess(); if (!process || !process->SecurityContext) return NTLSTATUS_FAILURE; SECURITY_CONTEXT* context = (SECURITY_CONTEXT*)0; if (contextHandle == SYS_SECURITY_CONTEXT_CURRENT) { context = process->SecurityContext; ObReferenceObject(&context->Header); } else { context = (SECURITY_CONTEXT*)KiHandleToObject(contextHandle, ObjectTypeSecurityContext, SYS_RIGHT_QUERY, &status); if (!context) return status; } SYS_SECURITY_CONTEXT_INFO info; SeQuerySecurityContext(context, &info); ObDereferenceObject(&context->Header); return CopyToUser(infoOut, &info, sizeof(info)); } // Implements SYS_OPEN_PROCESS_SECURITY_CONTEXT. static uint64_t SyscallOpenProcessSecurityContext(SYS_HANDLE processHandle, SYS_RIGHTS desiredRights, SYS_HANDLE* contextHandleOut) { if (!contextHandleOut || desiredRights == 0) return NTLSTATUS_INVALID_PARAMETER; SYS_RIGHTS validRights = KeGetValidHandleRights(ObjectTypeSecurityContext); if ((desiredRights & ~validRights) != 0) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = MmProbeUserWrite(contextHandleOut, sizeof(*contextHandleOut)); if (!KSUCCESS(status)) return status; PROCESS* target = (PROCESS*)KiHandleToObject(processHandle, ObjectTypeProcess, SYS_PROCESS_RIGHT_SECURITY_CONTEXT, &status); if (!target) return status; SECURITY_CONTEXT* context = target->SecurityContext; if (!context || !ObTryReferenceObject(&context->Header)) { ObDereferenceObject(&target->Header); return NTLSTATUS_OBJECT_DEAD; } ObDereferenceObject(&target->Header); SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KiAllocHandleForCurrentWithRights(&context->Header, desiredRights, &handle); if (!KSUCCESS(status)) return status; status = KiCopyHandleToUser(contextHandleOut, handle); if (!KSUCCESS(status)) { PROCESS* caller = KeGetCurrentProcess(); if (caller) (void)KeCloseHandle(caller, handle); } return status; } // Terminates the process referenced by one process handle. static uint64_t SyscallKillProcess(SYS_HANDLE processHandle, int32_t exitCode) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; PROCESS* proc = (PROCESS*)KiHandleToObject(processHandle, ObjectTypeProcess, SYS_PROCESS_RIGHT_TERMINATE, &handleStatus); if (!proc) return handleStatus; THREAD* self = KeGetCurrentThread(); if (self && self->Owner == proc) { KSTATUS s = KeExitProcess(proc, exitCode); ObDereferenceObject(&proc->Header); if (!KSUCCESS(s)) return s; KeExitThread(self, exitCode); while (true) ; } KSTATUS s = KeTerminateProcess(proc, exitCode); ObDereferenceObject(&proc->Header); return s; } // Implements SYS_GET_PROCESS_LIST. static uint64_t SyscallGetProcessList(SYS_PROCESS_INFO* processesOut, size_t maxCount, size_t* countOut) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_PROCESS_ENUMERATE); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; if (maxCount && !processesOut) return NTLSTATUS_INVALID_PARAMETER; if (countOut) { KSTATUS s = MmProbeUserWrite(countOut, sizeof(size_t)); if (!KSUCCESS(s)) return s; } size_t total = KeCopyProcessInfo(NULL, 0); size_t toCopy = total < maxCount ? total : maxCount; if (toCopy) { size_t bytes = 0; if (!KiMulSize(toCopy, sizeof(SYS_PROCESS_INFO), &bytes)) return NTLSTATUS_INVALID_PARAMETER; SYS_PROCESS_INFO* tmp = (SYS_PROCESS_INFO*)MmAllocatePoolTag(bytes, MM_POOL_TAG('s', 'y', 's', ' ')); if (!tmp) return NTLSTATUS_NO_MEMORY; size_t copied = KeCopyProcessInfo(tmp, toCopy); if (copied < toCopy) toCopy = copied; KSTATUS s = CopyToUser(processesOut, tmp, toCopy * sizeof(SYS_PROCESS_INFO)); MmFreePool(tmp); if (!KSUCCESS(s)) return s; } if (countOut) return KiCopySizeToUser(countOut, total); return NTLSTATUS_SUCCESS; } // Implements SYS_GET_THREAD_LIST. static uint64_t SyscallGetThreadList(uint32_t pid, SYS_THREAD_INFO* threadsOut, size_t maxCount, size_t* countOut) { PROCESS* caller = KeGetCurrentProcess(); if (!caller) return NTLSTATUS_FAILURE; if (pid != caller->ProcessId) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_THREAD_ENUMERATE_ANY); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; } if (maxCount && !threadsOut) return NTLSTATUS_INVALID_PARAMETER; if (countOut) { KSTATUS s = MmProbeUserWrite(countOut, sizeof(size_t)); if (!KSUCCESS(s)) return s; } size_t total = 0; KSTATUS s = KeCopyProcessThreadInfo(pid, NULL, 0, &total); if (!KSUCCESS(s)) return s; size_t toCopy = total < maxCount ? total : maxCount; if (toCopy) { size_t bytes = 0; if (!KiMulSize(toCopy, sizeof(SYS_THREAD_INFO), &bytes)) return NTLSTATUS_INVALID_PARAMETER; SYS_THREAD_INFO* tmp = (SYS_THREAD_INFO*)MmAllocatePoolTag(bytes, MM_POOL_TAG('s', 't', 'h', 'r')); if (!tmp) return NTLSTATUS_NO_MEMORY; size_t copied = 0; s = KeCopyProcessThreadInfo(pid, tmp, toCopy, &copied); if (KSUCCESS(s)) { if (copied < toCopy) toCopy = copied; s = CopyToUser(threadsOut, tmp, toCopy * sizeof(SYS_THREAD_INFO)); } MmFreePool(tmp); if (!KSUCCESS(s)) return s; } if (countOut) return KiCopySizeToUser(countOut, total); return NTLSTATUS_SUCCESS; } // Implements SYS_LOCK_FILE. static uint64_t SyscallLockFile(const SYS_LOCK_FILE_REQUEST* request) { SYS_LOCK_FILE_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags != SYS_FILE_LOCK_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; if (req.Mode != SYS_FILE_LOCK_SHARED && req.Mode != SYS_FILE_LOCK_EXCLUSIVE) return NTLSTATUS_INVALID_PARAMETER; if (req.Length == 0) return NTLSTATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; KSTATUS fileHandleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, SYS_FILE_RIGHT_LOCK, &fileHandleStatus); if (!file) return fileHandleStatus; s = IoLockFile(file, proc, req.Mode, req.Offset, req.Length, req.TimeoutMilliseconds); ObDereferenceObject(&file->Header); return s; } // Implements SYS_UNLOCK_FILE. static uint64_t SyscallUnlockFile(const SYS_UNLOCK_FILE_REQUEST* request) { SYS_UNLOCK_FILE_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags != SYS_FILE_LOCK_FLAG_NONE) return NTLSTATUS_INVALID_PARAMETER; if (req.Length == 0) return NTLSTATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; KSTATUS fileHandleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, SYS_FILE_RIGHT_LOCK, &fileHandleStatus); if (!file) return fileHandleStatus; s = IoUnlockFile(file, proc, req.Offset, req.Length); ObDereferenceObject(&file->Header); return s; } // Implements SYS_QUERY_FILE_LOCK. static uint64_t SyscallQueryFileLock(const SYS_QUERY_FILE_LOCK_REQUEST* request) { SYS_QUERY_FILE_LOCK_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_FILE_LOCK_FLAG_NONE || !req.InfoOut) return NTLSTATUS_INVALID_PARAMETER; if (req.Mode != SYS_FILE_LOCK_SHARED && req.Mode != SYS_FILE_LOCK_EXCLUSIVE) return NTLSTATUS_INVALID_PARAMETER; if (req.Length == 0) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.InfoOut, sizeof(*req.InfoOut)); if (!KSUCCESS(status)) return status; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, SYS_FILE_RIGHT_LOCK, &handleStatus); if (!file) return handleStatus; SYS_FILE_LOCK_INFO info; status = IoQueryFileLock(file, proc, req.Mode, req.Offset, req.Length, &info); ObDereferenceObject(&file->Header); if (!KSUCCESS(status)) return status; return CopyToUser(req.InfoOut, &info, sizeof(info)); } // Implements SYS_SET_FILE_STATUS. static uint64_t SyscallSetFileStatus(const SYS_SET_FILE_STATUS_REQUEST* request) { SYS_SET_FILE_STATUS_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Mask & ~SYS_FILE_STATUS_APPEND) return NTLSTATUS_INVALID_PARAMETER; if (req.Flags & ~req.Mask) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, SYS_RIGHT_QUERY, &handleStatus); if (!file) return handleStatus; status = IoSetFileStatus(file, req.Mask, req.Flags); ObDereferenceObject(&file->Header); return status; } // Implements SYS_CREATE_PIPE. The syscall creates endpoint capabilities and one // process-owned shared data-plane view; ordinary reads and writes stay in user mode. static uint64_t SyscallCreatePipe(const SYS_CREATE_PIPE_REQUEST* request) { SYS_CREATE_PIPE_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != 0 || !req.ReadHandleOut || !req.WriteHandleOut || !req.SharedStateOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(req.ReadHandleOut, sizeof(*req.ReadHandleOut)); if (!KSUCCESS(status)) return status; status = MmProbeUserWrite(req.WriteHandleOut, sizeof(*req.WriteHandleOut)); if (!KSUCCESS(status)) return status; status = MmProbeUserWrite(req.SharedStateOut, sizeof(*req.SharedStateOut)); if (!KSUCCESS(status)) return status; IO_PIPE_ENDPOINT* readEnd = (IO_PIPE_ENDPOINT*)0; IO_PIPE_ENDPOINT* writeEnd = (IO_PIPE_ENDPOINT*)0; status = IoCreatePipe(&readEnd, &writeEnd); if (!KSUCCESS(status)) return status; PROCESS* proc = KeGetCurrentProcess(); void* sharedState = (void*)0; status = IoMapPipe(readEnd, proc, &sharedState); if (!KSUCCESS(status)) { ObDereferenceObject((OBJECT*)readEnd); ObDereferenceObject((OBJECT*)writeEnd); return status; } SYS_HANDLE readHandle = SYS_HANDLE_INVALID; SYS_HANDLE writeHandle = SYS_HANDLE_INVALID; SYS_RIGHTS readRights = SYS_RIGHT_MANAGE | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_PIPE_RIGHT_READ | SYS_PIPE_RIGHT_POLL; SYS_RIGHTS writeRights = SYS_RIGHT_MANAGE | SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_PIPE_RIGHT_WRITE | SYS_PIPE_RIGHT_POLL; status = KiAllocHandleForCurrentWithRights((OBJECT*)readEnd, readRights, &readHandle); if (!KSUCCESS(status)) { ObDereferenceObject((OBJECT*)writeEnd); (void)MmVadDestroy(proc, sharedState); return status; } status = KiAllocHandleForCurrentWithRights((OBJECT*)writeEnd, writeRights, &writeHandle); if (!KSUCCESS(status)) { if (proc) KeCloseHandle(proc, readHandle); (void)MmVadDestroy(proc, sharedState); return status; } status = KiCopyHandleToUser(req.ReadHandleOut, readHandle); if (KSUCCESS(status)) status = KiCopyHandleToUser(req.WriteHandleOut, writeHandle); if (KSUCCESS(status)) status = CopyToUser(req.SharedStateOut, &sharedState, sizeof(sharedState)); if (!KSUCCESS(status)) { if (sharedState && proc) (void)MmVadDestroy(proc, sharedState); if (proc) { KeCloseHandle(proc, readHandle); KeCloseHandle(proc, writeHandle); } } return status; } // Implements SYS_NOTIFY_PIPE. User mode calls this only when a shared waiter // count says a data-plane transition may need scheduler attention. static uint64_t SyscallNotifyPipe(SYS_HANDLE pipeHandle) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; IO_PIPE_ENDPOINT* endpoint = (IO_PIPE_ENDPOINT*)KiHandleToObject(pipeHandle, ObjectTypePipeEndpoint, SYS_RIGHT_WAIT, &handleStatus); if (!endpoint) return handleStatus; KSTATUS status = IoNotifyPipe(endpoint); ObDereferenceObject((OBJECT*)endpoint); return status; } typedef struct SYS_SOCKET_SEND_TO_REQUEST_K { uint32_t Size; const void* Buffer; size_t Length; uint32_t Flags; const SYS_SOCKET_ADDRESS_K* RemoteAddress; size_t RemoteAddressSize; size_t* SentOut; } SYS_SOCKET_SEND_TO_REQUEST_K; typedef struct SYS_SOCKET_RECV_FROM_REQUEST_K { uint32_t Size; void* Buffer; size_t Length; uint32_t Flags; SYS_SOCKET_ADDRESS_K* RemoteAddressOut; size_t* RemoteAddressSizeInOut; size_t* ReceivedOut; } SYS_SOCKET_RECV_FROM_REQUEST_K; typedef struct SYS_RESOLVE_HOST_REQUEST_K { uint32_t Size; SYS_STRING_VIEW Name; uint32_t Family; uint32_t Flags; SYS_SOCKET_ADDRESS_K* AddressOut; size_t* AddressSizeInOut; } SYS_RESOLVE_HOST_REQUEST_K; typedef struct SYS_SOCKET_OPTION_REQUEST_K { uint32_t Size; uint32_t Option; void* Value; size_t ValueSize; size_t* ActualSizeOut; } SYS_SOCKET_OPTION_REQUEST_K; typedef struct SYS_ASYNC_SOCKET_REQUEST_K { uint32_t Size; SYS_HANDLE SocketHandle; SYS_HANDLE EventHandle; uint32_t Operation; uint32_t Flags; uint32_t SocketFlags; uint32_t Backlog; uint32_t ShutdownHow; uint32_t Option; uint32_t PollEvents; void* Buffer; size_t Length; const SYS_SOCKET_ADDRESS_K* Address; size_t AddressSize; SYS_SOCKET_ADDRESS_K* AddressOut; size_t* AddressSizeInOut; void* OptionValue; size_t OptionValueSize; size_t* OptionActualSizeOut; SYS_HANDLE* HandleOut; uint32_t* PollReturnedEventsOut; SYS_IO_STATUS_BLOCK_K* Iosb; SYS_USER_APC_ROUTINE_K ApcRoutine; void* ApcContext; SYS_HANDLE* RequestHandleOut; } SYS_ASYNC_SOCKET_REQUEST_K; _Static_assert(sizeof(SYS_SOCKET_SEND_TO_REQUEST_K) == sizeof(SYS_SOCKET_SEND_TO_REQUEST), "send-to request ABI mismatch"); _Static_assert(sizeof(SYS_SOCKET_RECV_FROM_REQUEST_K) == sizeof(SYS_SOCKET_RECV_FROM_REQUEST), "recv-from request ABI mismatch"); _Static_assert(sizeof(SYS_RESOLVE_HOST_REQUEST_K) == sizeof(SYS_RESOLVE_HOST_REQUEST), "resolve-host request ABI mismatch"); _Static_assert(sizeof(SYS_SOCKET_OPTION_REQUEST_K) == sizeof(SYS_SOCKET_OPTION_REQUEST), "socket option request ABI mismatch"); _Static_assert(sizeof(SYS_ASYNC_SOCKET_REQUEST_K) == sizeof(SYS_ASYNC_SOCKET_REQUEST), "async socket request ABI mismatch"); typedef struct SYS_MEMORY_STATUS_K { uint64_t FreePhysicalBytes; uint64_t KernelPoolBytes; uint64_t UserHeapBytes; uint64_t ReclaimablePhysicalBytes; uint64_t AvailablePhysicalBytes; uint64_t PinnedPhysicalBytes; uint64_t NonEvictablePhysicalBytes; uint64_t UserPhysicalBytes; uint64_t PageTablePhysicalBytes; } SYS_MEMORY_STATUS_K; #define SYS_POLL_MAX_FDS 64u // Implements SYS_CREATE_SOCKET. static uint64_t SyscallSocket(uint32_t domain, uint32_t type, uint32_t protocol, SYS_HANDLE* handleOut) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_NETWORK_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; if (!handleOut) return NTLSTATUS_INVALID_PARAMETER; SOCKET_OBJECT* socket = NULL; KSTATUS s = NetSocketCreate(domain, type, protocol, &socket); if (!KSUCCESS(s)) return s; SYS_HANDLE h = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&socket->Header, KeGetValidHandleRights(ObjectTypeSocket), &h); if (!KSUCCESS(s)) return s; // KiAllocHandleForCurrentWithRights consumed NetSocketCreate's creator // reference; the returned process handle now owns the socket object. s = KiCopyHandleToUser(handleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // Validates the user-supplied socket address buffer size before copying. static KSTATUS SyscallValidateSocketAddressBufferSize(size_t addressSize) { if (addressSize < sizeof(SYS_SOCKET_ADDRESS_K)) return NTLSTATUS_BUFFER_TOO_SMALL; if (addressSize != sizeof(SYS_SOCKET_ADDRESS_K)) return NTLSTATUS_INVALID_PARAMETER; return NTLSTATUS_SUCCESS; } // Validates the fixed-size socket address ABI after a bounded copy. static KSTATUS SyscallValidateSocketAddressInput(const SYS_SOCKET_ADDRESS_K* address) { if (!address) return NTLSTATUS_INVALID_PARAMETER; if (address->Size != sizeof(SYS_SOCKET_ADDRESS_K)) return NTLSTATUS_INVALID_PARAMETER; return NTLSTATUS_SUCCESS; } // Implements SYS_BIND_SOCKET. static uint64_t SyscallBind(SYS_HANDLE socket, const SYS_SOCKET_ADDRESS* userAddress, size_t addressSize) { if (!userAddress) return NTLSTATUS_INVALID_PARAMETER; KSTATUS s = SyscallValidateSocketAddressBufferSize(addressSize); if (!KSUCCESS(s)) return s; SYS_SOCKET_ADDRESS_K address; s = CopyFromUser(&address, userAddress, sizeof(address)); if (!KSUCCESS(s)) return s; s = SyscallValidateSocketAddressInput(&address); if (!KSUCCESS(s)) return s; KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_BIND, &handleStatus); if (!socketObject) return handleStatus; s = NetSocketBind(socketObject, &address, addressSize); ObDereferenceObject(&socketObject->Header); return s; } // Implements SYS_LISTEN_SOCKET. static uint64_t SyscallListen(SYS_HANDLE socket, uint32_t backlog) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_LISTEN, &handleStatus); if (!socketObject) return handleStatus; KSTATUS s = NetSocketListen(socketObject, backlog); ObDereferenceObject(&socketObject->Header); return s; } // Implements SYS_ACCEPT_SOCKET. // a2=*remoteAddressSizeInOut(optional) a3=*acceptedHandleOut. static uint64_t SyscallAccept(SYS_HANDLE socket, SYS_SOCKET_ADDRESS* remoteAddressOut, size_t* remoteAddressSizeInOut, SYS_HANDLE* acceptedHandleOut) { if (!acceptedHandleOut) return NTLSTATUS_INVALID_PARAMETER; bool wantRemote = (remoteAddressOut != 0); size_t remoteCapacity = 0; if (wantRemote) { if (!remoteAddressSizeInOut) return NTLSTATUS_INVALID_PARAMETER; KSTATUS cs = CopyFromUser(&remoteCapacity, remoteAddressSizeInOut, sizeof(remoteCapacity)); if (!KSUCCESS(cs)) return cs; if (remoteCapacity < sizeof(SYS_SOCKET_ADDRESS_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_K); (void)CopyToUser(remoteAddressSizeInOut, &required, sizeof(required)); return NTLSTATUS_BUFFER_TOO_SMALL; } cs = MmProbeUserWrite(remoteAddressOut, sizeof(SYS_SOCKET_ADDRESS_K)); if (!KSUCCESS(cs)) return cs; } KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* listener = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_ACCEPT, &handleStatus); if (!listener) return handleStatus; SOCKET_OBJECT* accepted = NULL; SYS_SOCKET_ADDRESS_K remote; KSTATUS s = NetSocketAccept(listener, &accepted, wantRemote ? &remote : NULL, (IO_ASYNC_REQUEST_OBJECT*)0); ObDereferenceObject(&listener->Header); if (!KSUCCESS(s)) return s; SYS_HANDLE h = SYS_HANDLE_INVALID; s = KiAllocHandleForCurrentWithRights(&accepted->Header, KeGetValidHandleRights(ObjectTypeSocket), &h); if (!KSUCCESS(s)) return s; // KiAllocHandleForCurrentWithRights consumed the accept creator reference; // the returned process handle owns the accepted socket from this point. if (wantRemote) { size_t actual = sizeof(SYS_SOCKET_ADDRESS_K); s = CopyToUser(remoteAddressOut, &remote, sizeof(remote)); if (KSUCCESS(s)) s = CopyToUser(remoteAddressSizeInOut, &actual, sizeof(actual)); } if (KSUCCESS(s)) s = KiCopyHandleToUser(acceptedHandleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // Implements SYS_CONNECT_SOCKET. static uint64_t SyscallConnect(SYS_HANDLE socket, const SYS_SOCKET_ADDRESS* userAddress, size_t addressSize) { if (!userAddress) return NTLSTATUS_INVALID_PARAMETER; KSTATUS s = SyscallValidateSocketAddressBufferSize(addressSize); if (!KSUCCESS(s)) return s; SYS_SOCKET_ADDRESS_K address; s = CopyFromUser(&address, userAddress, sizeof(address)); if (!KSUCCESS(s)) return s; s = SyscallValidateSocketAddressInput(&address); if (!KSUCCESS(s)) return s; KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_CONNECT, &handleStatus); if (!socketObject) return handleStatus; s = NetSocketConnect(socketObject, &address, addressSize, (IO_ASYNC_REQUEST_OBJECT*)0); ObDereferenceObject(&socketObject->Header); return s; } // Implements SYS_SEND_SOCKET. static uint64_t SyscallSend(const SYS_SOCKET_SEND_REQUEST* request) { SYS_SOCKET_SEND_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return NTLSTATUS_INVALID_PARAMETER; if (req.Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; if (req.Length == 0) { if (req.SentOut) return KiCopySizeToUser(req.SentOut, 0); return NTLSTATUS_SUCCESS; } void* kbuf = MmAllocatePoolTag(req.Length, MM_POOL_TAG('s', 'y', 's', ' ')); if (!kbuf) return NTLSTATUS_NO_MEMORY; s = CopyFromUser(kbuf, req.Buffer, req.Length); if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; } KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(req.Socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_SEND, &handleStatus); if (!socket) { MmFreePool(kbuf); return handleStatus; } size_t sent = 0; s = NetSocketSend(socket, kbuf, req.Length, req.Flags, &sent); ObDereferenceObject(&socket->Header); MmFreePool(kbuf); if (req.SentOut) { KSTATUS copyStatus = KiCopySizeToUser(req.SentOut, sent); if (KSUCCESS(s)) s = copyStatus; } return s; } // Receives bytes from a connected socket. static uint64_t SyscallRecv(const SYS_SOCKET_RECV_REQUEST* request) { SYS_SOCKET_RECV_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return NTLSTATUS_INVALID_PARAMETER; if (req.Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; if (req.Length == 0) { if (req.ReceivedOut) return KiCopySizeToUser(req.ReceivedOut, 0); return NTLSTATUS_SUCCESS; } s = MmProbeUserWrite(req.Buffer, req.Length); if (!KSUCCESS(s)) return s; void* kbuf = MmAllocatePoolTag(req.Length, MM_POOL_TAG('s', 'y', 's', ' ')); if (!kbuf) return NTLSTATUS_NO_MEMORY; KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(req.Socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_RECEIVE, &handleStatus); if (!socket) { MmFreePool(kbuf); return handleStatus; } size_t received = 0; s = NetSocketReceive(socket, kbuf, req.Length, req.Flags, &received, (IO_ASYNC_REQUEST_OBJECT*)0); ObDereferenceObject(&socket->Header); if (KSUCCESS(s) && received) s = CopyToUser(req.Buffer, kbuf, received); MmFreePool(kbuf); if (KSUCCESS(s) && req.ReceivedOut) s = KiCopySizeToUser(req.ReceivedOut, received); return s; } // Shuts down one or both directions of a socket. static uint64_t SyscallShutdown(SYS_HANDLE socket, uint32_t how) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_SHUTDOWN, &handleStatus); if (!socketObject) return handleStatus; KSTATUS s = NetSocketShutdown(socketObject, how); ObDereferenceObject(&socketObject->Header); return s; } // Implements SYS_SEND_TO_SOCKET. static uint64_t SyscallSendTo(SYS_HANDLE socket, const SYS_SOCKET_SEND_TO_REQUEST* request) { if (!request) return NTLSTATUS_INVALID_PARAMETER; SYS_SOCKET_SEND_TO_REQUEST_K req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return NTLSTATUS_INVALID_PARAMETER; if (!req.RemoteAddress) return NTLSTATUS_INVALID_PARAMETER; if (req.Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; s = SyscallValidateSocketAddressBufferSize(req.RemoteAddressSize); if (!KSUCCESS(s)) return s; SYS_SOCKET_ADDRESS_K remote; s = CopyFromUser(&remote, req.RemoteAddress, sizeof(remote)); if (!KSUCCESS(s)) return s; s = SyscallValidateSocketAddressInput(&remote); if (!KSUCCESS(s)) return s; void* kbuf = NULL; if (req.Length) { kbuf = MmAllocatePoolTag(req.Length, MM_POOL_TAG('s', 'y', 's', ' ')); if (!kbuf) return NTLSTATUS_NO_MEMORY; s = CopyFromUser(kbuf, req.Buffer, req.Length); if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; } } KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_SEND, &handleStatus); if (!socketObject) { if (kbuf) MmFreePool(kbuf); return handleStatus; } size_t sent = 0; s = NetSocketSendTo(socketObject, kbuf, req.Length, req.Flags, &remote, req.RemoteAddressSize, &sent); ObDereferenceObject(&socketObject->Header); if (kbuf) MmFreePool(kbuf); if (req.SentOut) { KSTATUS cs = CopyToUser(req.SentOut, &sent, sizeof(sent)); if (KSUCCESS(s)) s = cs; } return s; } // Implements SYS_RECV_FROM_SOCKET. static uint64_t SyscallRecvFrom(SYS_HANDLE socket, const SYS_SOCKET_RECV_FROM_REQUEST* request) { if (!request) return NTLSTATUS_INVALID_PARAMETER; SYS_SOCKET_RECV_FROM_REQUEST_K req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return NTLSTATUS_INVALID_PARAMETER; if (req.Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; bool wantRemote = (req.RemoteAddressOut != NULL); size_t remoteCapacity = 0; if (wantRemote) { if (!req.RemoteAddressSizeInOut) return NTLSTATUS_INVALID_PARAMETER; s = CopyFromUser(&remoteCapacity, req.RemoteAddressSizeInOut, sizeof(remoteCapacity)); if (!KSUCCESS(s)) return s; if (remoteCapacity < sizeof(SYS_SOCKET_ADDRESS_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_K); (void)CopyToUser(req.RemoteAddressSizeInOut, &required, sizeof(required)); return NTLSTATUS_BUFFER_TOO_SMALL; } s = MmProbeUserWrite(req.RemoteAddressOut, sizeof(SYS_SOCKET_ADDRESS_K)); if (!KSUCCESS(s)) return s; } if (req.Length) { s = MmProbeUserWrite(req.Buffer, req.Length); if (!KSUCCESS(s)) return s; } void* kbuf = NULL; if (req.Length) { kbuf = MmAllocatePoolTag(req.Length, MM_POOL_TAG('s', 'y', 's', ' ')); if (!kbuf) return NTLSTATUS_NO_MEMORY; } KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_RECEIVE, &handleStatus); if (!socketObject) { if (kbuf) MmFreePool(kbuf); return handleStatus; } SYS_SOCKET_ADDRESS_K remote; memset(&remote, 0, sizeof(remote)); size_t received = 0; s = NetSocketReceiveFrom(socketObject, kbuf, req.Length, req.Flags, wantRemote ? &remote : NULL, &received, (IO_ASYNC_REQUEST_OBJECT*)0); ObDereferenceObject(&socketObject->Header); if (KSUCCESS(s) && received) s = CopyToUser(req.Buffer, kbuf, received); if (kbuf) MmFreePool(kbuf); if (KSUCCESS(s) && wantRemote) { size_t actual = sizeof(SYS_SOCKET_ADDRESS_K); s = CopyToUser(req.RemoteAddressOut, &remote, sizeof(remote)); if (KSUCCESS(s)) s = CopyToUser(req.RemoteAddressSizeInOut, &actual, sizeof(actual)); } if (KSUCCESS(s) && req.ReceivedOut) s = CopyToUser(req.ReceivedOut, &received, sizeof(received)); return s; } // Implements SYS_RESOLVE_HOST. static uint64_t SyscallResolveHost(const SYS_RESOLVE_HOST_REQUEST* request) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_NETWORK_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; if (!request) return NTLSTATUS_INVALID_PARAMETER; SYS_RESOLVE_HOST_REQUEST_K req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Name.Data || req.Name.Length == 0 || req.Name.Length > SYS_RESOLVE_HOST_NAME_MAX) return NTLSTATUS_INVALID_PARAMETER; if (req.Family != SYS_AF_INET && req.Family != SYS_AF_INET6) return NTLSTATUS_NOT_SUPPORTED; if (req.Flags != 0) return NTLSTATUS_NOT_SUPPORTED; if (!req.AddressOut || !req.AddressSizeInOut) return NTLSTATUS_INVALID_PARAMETER; size_t capacity = 0; s = CopyFromUser(&capacity, req.AddressSizeInOut, sizeof(capacity)); if (!KSUCCESS(s)) return s; if (capacity < sizeof(SYS_SOCKET_ADDRESS_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_K); (void)CopyToUser(req.AddressSizeInOut, &required, sizeof(required)); return NTLSTATUS_BUFFER_TOO_SMALL; } char name[SYS_RESOLVE_HOST_NAME_MAX + 1]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, true); if (!KSUCCESS(s)) return s; SYS_SOCKET_ADDRESS_K address; s = NetResolveHost(name, req.Family, &address); if (!KSUCCESS(s)) return s; size_t actual = sizeof(SYS_SOCKET_ADDRESS_K); s = CopyToUser(req.AddressOut, &address, sizeof(address)); if (!KSUCCESS(s)) return s; return CopyToUser(req.AddressSizeInOut, &actual, sizeof(actual)); } // Implements SYS_SET_SOCKET_OPTION. static uint64_t SyscallSetSocketOption(SYS_HANDLE socket, const SYS_SOCKET_OPTION_REQUEST* request) { if (!request) return NTLSTATUS_INVALID_PARAMETER; SYS_SOCKET_OPTION_REQUEST_K req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Value) return NTLSTATUS_INVALID_PARAMETER; uint8_t value[sizeof(uint64_t)]; if (req.ValueSize > sizeof(value)) return NTLSTATUS_BUFFER_TOO_SMALL; s = CopyFromUser(value, req.Value, req.ValueSize); if (!KSUCCESS(s)) return s; KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_SOCKET_RIGHT_SET_OPTION, &handleStatus); if (!socketObject) return handleStatus; s = NetSocketSetOption(socketObject, req.Option, value, req.ValueSize); ObDereferenceObject(&socketObject->Header); return s; } // Implements SYS_GET_SOCKET_OPTION. static uint64_t SyscallGetSocketOption(SYS_HANDLE socket, const SYS_SOCKET_OPTION_REQUEST* request) { if (!request) return NTLSTATUS_INVALID_PARAMETER; SYS_SOCKET_OPTION_REQUEST_K req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Value) return NTLSTATUS_INVALID_PARAMETER; uint8_t value[sizeof(uint64_t)]; if (req.ValueSize > sizeof(value)) return NTLSTATUS_BUFFER_TOO_SMALL; KSTATUS handleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socketObject = (SOCKET_OBJECT*)KiHandleToObject(socket, ObjectTypeSocket, SYS_RIGHT_QUERY, &handleStatus); if (!socketObject) return handleStatus; size_t actual = 0; s = NetSocketGetOption(socketObject, req.Option, value, req.ValueSize, &actual); ObDereferenceObject(&socketObject->Header); if (!KSUCCESS(s)) { if (s == NTLSTATUS_BUFFER_TOO_SMALL && req.ActualSizeOut) (void)CopyToUser(req.ActualSizeOut, &actual, sizeof(actual)); return s; } s = CopyToUser(req.Value, value, actual); if (KSUCCESS(s) && req.ActualSizeOut) s = CopyToUser(req.ActualSizeOut, &actual, sizeof(actual)); return s; } // Polls deadline Expired. static bool KiPollDeadlineExpired(uint64_t start, uint32_t timeoutMs) { if (timeoutMs == 0) return true; if (timeoutMs == SYS_POLL_TIMEOUT_INFINITE) return false; if (!gHal) return true; return (gHal->Timer.QueryTicks() - start) >= ((uint64_t)timeoutMs * 1000u); } // Implements SYS_POLL. static uint64_t SyscallPoll(const SYS_POLL_REQUEST* request) { SYS_POLL_REQUEST req; KSTATUS s = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Fds) return NTLSTATUS_INVALID_PARAMETER; size_t count = req.Count; if (count == 0 || count > SYS_POLL_MAX_FDS) return NTLSTATUS_INVALID_PARAMETER; size_t bytes = count * sizeof(SYS_POLLFD_K); SYS_POLLFD_K* fds = (SYS_POLLFD_K*)MmAllocatePoolTag(bytes, MM_POOL_TAG('s', 'y', 's', ' ')); if (!fds) return NTLSTATUS_NO_MEMORY; s = CopyFromUser(fds, req.Fds, bytes); if (!KSUCCESS(s)) { MmFreePool(fds); return s; } uint32_t timeoutMs = req.TimeoutMs; uint64_t start = gHal ? gHal->Timer.QueryTicks() : 0; size_t ready = 0; while (true) { ready = 0; for (size_t i = 0; i < count; i++) { fds[i].ReturnedEvents = 0; KSTATUS handleStatus = NTLSTATUS_SUCCESS; uint32_t revents = 0; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject( fds[i].Handle, ObjectTypeSocket, SYS_SOCKET_RIGHT_POLL, &handleStatus); if (socket) { s = NetSocketPoll(socket, fds[i].Events, &revents); ObDereferenceObject(&socket->Header); } else { IO_PIPE_ENDPOINT* pipe = (IO_PIPE_ENDPOINT*)KiHandleToObject( fds[i].Handle, ObjectTypePipeEndpoint, SYS_PIPE_RIGHT_POLL, &handleStatus); if (!pipe) { fds[i].ReturnedEvents = SYS_POLL_INVALID; ready++; continue; } s = IoPollPipe(pipe, fds[i].Events, &revents); ObDereferenceObject((OBJECT*)pipe); } if (!KSUCCESS(s)) { fds[i].ReturnedEvents = SYS_POLL_ERROR; ready++; continue; } fds[i].ReturnedEvents = revents; if (revents != 0) ready++; } if (ready != 0 || KiPollDeadlineExpired(start, timeoutMs)) break; KeYield(); } s = CopyToUser(req.Fds, fds, bytes); if (KSUCCESS(s) && req.ReadyOut) s = CopyToUser(req.ReadyOut, &ready, sizeof(ready)); MmFreePool(fds); return s; } // Releases resources prepared for an async socket request before queue ownership starts. static void SyscallAsyncSocketReleaseSubmitResources(NET_ASYNC_SOCKET_SUBMIT* submit) { if (!submit) return; if (submit->KernelBuffer) { MmFreePool(submit->KernelBuffer); submit->KernelBuffer = (void*)0; } if (submit->OptionValue) { MmFreePool(submit->OptionValue); submit->OptionValue = (void*)0; } if (submit->LockedBuffer) { MmUnlockUserBuffer(submit->LockedBuffer); submit->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0; } if (submit->LockedIosb) { MmUnlockUserBuffer(submit->LockedIosb); submit->LockedIosb = (MM_LOCKED_USER_BUFFER*)0; } if (submit->LockedAddressOut) { MmUnlockUserBuffer(submit->LockedAddressOut); submit->LockedAddressOut = (MM_LOCKED_USER_BUFFER*)0; } if (submit->LockedAddressSize) { MmUnlockUserBuffer(submit->LockedAddressSize); submit->LockedAddressSize = (MM_LOCKED_USER_BUFFER*)0; } if (submit->LockedOptionValue) { MmUnlockUserBuffer(submit->LockedOptionValue); submit->LockedOptionValue = (MM_LOCKED_USER_BUFFER*)0; } if (submit->LockedOptionActualSize) { MmUnlockUserBuffer(submit->LockedOptionActualSize); submit->LockedOptionActualSize = (MM_LOCKED_USER_BUFFER*)0; } if (submit->LockedHandleOut) { MmUnlockUserBuffer(submit->LockedHandleOut); submit->LockedHandleOut = (MM_LOCKED_USER_BUFFER*)0; } if (submit->LockedPollReturnedEvents) { MmUnlockUserBuffer(submit->LockedPollReturnedEvents); submit->LockedPollReturnedEvents = (MM_LOCKED_USER_BUFFER*)0; } } // Locks an optional async socket output pointer for worker-thread completion. static KSTATUS SyscallAsyncSocketLockOptional(PROCESS* proc, void* ptr, size_t size, MM_LOCKED_USER_BUFFER** out) { if (out) *out = (MM_LOCKED_USER_BUFFER*)0; if (!ptr || size == 0) return NTLSTATUS_SUCCESS; return MmLockUserBuffer(proc, ptr, size, 1, out); } // Copies an input address into a kernel-owned async socket request. static KSTATUS SyscallAsyncSocketCopyInputAddress(const SYS_ASYNC_SOCKET_REQUEST_K* req, NET_ASYNC_SOCKET_SUBMIT* submit) { if (!req || !submit || !req->Address) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = SyscallValidateSocketAddressBufferSize(req->AddressSize); if (!KSUCCESS(status)) return status; status = CopyFromUser(&submit->Address, req->Address, sizeof(submit->Address)); if (!KSUCCESS(status)) return status; status = SyscallValidateSocketAddressInput(&submit->Address); if (!KSUCCESS(status)) return status; submit->AddressSize = req->AddressSize; return NTLSTATUS_SUCCESS; } // Validates and locks optional remote-address outputs for async accept/recvfrom. static KSTATUS SyscallAsyncSocketPrepareAddressOutput(PROCESS* proc, const SYS_ASYNC_SOCKET_REQUEST_K* req, NET_ASYNC_SOCKET_SUBMIT* submit) { if (!req || !submit) return NTLSTATUS_INVALID_PARAMETER; if (!req->AddressOut) return NTLSTATUS_SUCCESS; if (!req->AddressSizeInOut) return NTLSTATUS_INVALID_PARAMETER; size_t capacity = 0; KSTATUS status = CopyFromUser(&capacity, req->AddressSizeInOut, sizeof(capacity)); if (!KSUCCESS(status)) return status; if (capacity < sizeof(SYS_SOCKET_ADDRESS_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_K); (void)CopyToUser(req->AddressSizeInOut, &required, sizeof(required)); return NTLSTATUS_BUFFER_TOO_SMALL; } status = MmLockUserBuffer(proc, req->AddressOut, sizeof(SYS_SOCKET_ADDRESS_K), 1, &submit->LockedAddressOut); if (!KSUCCESS(status)) return status; status = MmLockUserBuffer(proc, req->AddressSizeInOut, sizeof(*req->AddressSizeInOut), 1, &submit->LockedAddressSize); if (!KSUCCESS(status)) return status; submit->AddressOut = req->AddressOut; submit->AddressSizeInOut = req->AddressSizeInOut; return NTLSTATUS_SUCCESS; } // Prepares kernel-owned buffers and locked outputs for one async socket operation. static KSTATUS SyscallAsyncSocketPrepareOperation(PROCESS* proc, const SYS_ASYNC_SOCKET_REQUEST_K* req, NET_ASYNC_SOCKET_SUBMIT* submit) { if (!proc || !req || !submit) return NTLSTATUS_INVALID_PARAMETER; submit->Operation = req->Operation; submit->SocketFlags = req->SocketFlags; submit->Backlog = req->Backlog; submit->ShutdownHow = req->ShutdownHow; submit->Option = req->Option; submit->PollEvents = req->PollEvents; submit->Length = req->Length; switch (req->Operation) { case SYS_ASYNC_SOCKET_OP_BIND: case SYS_ASYNC_SOCKET_OP_CONNECT: return SyscallAsyncSocketCopyInputAddress(req, submit); case SYS_ASYNC_SOCKET_OP_LISTEN: case SYS_ASYNC_SOCKET_OP_SHUTDOWN: return NTLSTATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_ACCEPT: { if (!req->HandleOut) return NTLSTATUS_INVALID_PARAMETER; SYS_HANDLE zero = SYS_HANDLE_INVALID; KSTATUS status = MmLockUserBuffer(proc, req->HandleOut, sizeof(*req->HandleOut), 1, &submit->LockedHandleOut); if (!KSUCCESS(status)) return status; *req->HandleOut = zero; submit->HandleOut = req->HandleOut; return SyscallAsyncSocketPrepareAddressOutput(proc, req, submit); } case SYS_ASYNC_SOCKET_OP_SEND: if (req->Length && !req->Buffer) return NTLSTATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; if (req->Length) { submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 's', 'n')); if (!submit->KernelBuffer) return NTLSTATUS_NO_MEMORY; return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length); } return NTLSTATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_RECV: if (req->Length && !req->Buffer) return NTLSTATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; if (req->Length) { KSTATUS status = MmLockUserBuffer(proc, req->Buffer, req->Length, 1, &submit->LockedBuffer); if (!KSUCCESS(status)) return status; submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 'r', 'n')); if (!submit->KernelBuffer) return NTLSTATUS_NO_MEMORY; } submit->UserBuffer = req->Buffer; return NTLSTATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_SEND_TO: { if (req->Length && !req->Buffer) return NTLSTATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = SyscallAsyncSocketCopyInputAddress(req, submit); if (!KSUCCESS(status)) return status; if (req->Length) { submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 's', 'd')); if (!submit->KernelBuffer) return NTLSTATUS_NO_MEMORY; return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length); } return NTLSTATUS_SUCCESS; } case SYS_ASYNC_SOCKET_OP_RECV_FROM: { if (req->Length && !req->Buffer) return NTLSTATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = SyscallAsyncSocketPrepareAddressOutput(proc, req, submit); if (!KSUCCESS(status)) return status; if (req->Length) { status = MmLockUserBuffer(proc, req->Buffer, req->Length, 1, &submit->LockedBuffer); if (!KSUCCESS(status)) return status; submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 'r', 'd')); if (!submit->KernelBuffer) return NTLSTATUS_NO_MEMORY; } submit->UserBuffer = req->Buffer; return NTLSTATUS_SUCCESS; } case SYS_ASYNC_SOCKET_OP_SET_OPT: if (!req->OptionValue) return NTLSTATUS_INVALID_PARAMETER; if (req->OptionValueSize > sizeof(uint64_t)) return NTLSTATUS_BUFFER_TOO_SMALL; submit->OptionValueSize = req->OptionValueSize; if (req->OptionValueSize) { submit->OptionValue = MmAllocatePoolTag(req->OptionValueSize, MM_POOL_TAG('s', 'a', 'o', 's')); if (!submit->OptionValue) return NTLSTATUS_NO_MEMORY; return CopyFromUser(submit->OptionValue, req->OptionValue, req->OptionValueSize); } return NTLSTATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_GET_OPT: { if (!req->OptionValue) return NTLSTATUS_INVALID_PARAMETER; if (req->OptionValueSize > sizeof(uint64_t)) return NTLSTATUS_BUFFER_TOO_SMALL; submit->OptionValueSize = req->OptionValueSize; submit->UserOptionValue = req->OptionValue; KSTATUS status = MmLockUserBuffer(proc, req->OptionValue, req->OptionValueSize, 1, &submit->LockedOptionValue); if (!KSUCCESS(status)) return status; if (req->OptionActualSizeOut) { status = MmLockUserBuffer(proc, req->OptionActualSizeOut, sizeof(*req->OptionActualSizeOut), 1, &submit->LockedOptionActualSize); if (!KSUCCESS(status)) return status; submit->OptionActualSizeOut = req->OptionActualSizeOut; } if (req->OptionValueSize) { submit->OptionValue = MmAllocatePoolTag(req->OptionValueSize, MM_POOL_TAG('s', 'a', 'o', 'g')); if (!submit->OptionValue) return NTLSTATUS_NO_MEMORY; } return NTLSTATUS_SUCCESS; } case SYS_ASYNC_SOCKET_OP_POLL: { if (req->PollReturnedEventsOut) { KSTATUS status = MmLockUserBuffer(proc, req->PollReturnedEventsOut, sizeof(*req->PollReturnedEventsOut), 1, &submit->LockedPollReturnedEvents); if (!KSUCCESS(status)) return status; *req->PollReturnedEventsOut = 0; submit->PollReturnedEventsOut = req->PollReturnedEventsOut; } return NTLSTATUS_SUCCESS; } default: return NTLSTATUS_INVALID_PARAMETER; } } // Returns the capability right required by one asynchronous socket operation. static SYS_RIGHTS KiAsyncSocketRequiredRights(uint32_t operation) { switch (operation) { case SYS_ASYNC_SOCKET_OP_BIND: return SYS_SOCKET_RIGHT_BIND; case SYS_ASYNC_SOCKET_OP_LISTEN: return SYS_SOCKET_RIGHT_LISTEN; case SYS_ASYNC_SOCKET_OP_ACCEPT: return SYS_SOCKET_RIGHT_ACCEPT; case SYS_ASYNC_SOCKET_OP_CONNECT: return SYS_SOCKET_RIGHT_CONNECT; case SYS_ASYNC_SOCKET_OP_SEND: case SYS_ASYNC_SOCKET_OP_SEND_TO: return SYS_SOCKET_RIGHT_SEND; case SYS_ASYNC_SOCKET_OP_RECV: case SYS_ASYNC_SOCKET_OP_RECV_FROM: return SYS_SOCKET_RIGHT_RECEIVE; case SYS_ASYNC_SOCKET_OP_SHUTDOWN: return SYS_SOCKET_RIGHT_SHUTDOWN; case SYS_ASYNC_SOCKET_OP_SET_OPT: return SYS_SOCKET_RIGHT_SET_OPTION; case SYS_ASYNC_SOCKET_OP_GET_OPT: return SYS_RIGHT_QUERY; case SYS_ASYNC_SOCKET_OP_POLL: return SYS_SOCKET_RIGHT_POLL; default: return 0; } } // Implements SYS_SOCKET_IO_ASYNC. static uint64_t SyscallSocketIoAsync(const SYS_ASYNC_SOCKET_REQUEST* request) { if (!request) return NTLSTATUS_INVALID_PARAMETER; SYS_ASYNC_SOCKET_REQUEST_K req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags != SYS_ASYNC_SOCKET_FLAG_NONE || !req.Iosb) return NTLSTATUS_INVALID_PARAMETER; if (req.RequestHandleOut) { status = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut)); if (!KSUCCESS(status)) return status; } PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NTLSTATUS_FAILURE; SYS_RIGHTS socketRights = KiAsyncSocketRequiredRights(req.Operation); if (!socketRights) return NTLSTATUS_INVALID_PARAMETER; KSTATUS socketHandleStatus = NTLSTATUS_SUCCESS; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(req.SocketHandle, ObjectTypeSocket, socketRights, &socketHandleStatus); if (!socket) return socketHandleStatus; EVENT* event = (EVENT*)0; if (req.EventHandle) { KSTATUS eventHandleStatus = NTLSTATUS_SUCCESS; event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, SYS_EVENT_RIGHT_SIGNAL, &eventHandleStatus); if (!event) { ObDereferenceObject(&socket->Header); return eventHandleStatus; } } KAPC* completionApc = (KAPC*)0; SYS_USER_ASYNC_APC_CONTEXT* apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)0; if (req.ApcRoutine) { THREAD* thread = KeGetCurrentThread(); if (!thread || !thread->UserApcDispatcher) { if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&socket->Header); return NTLSTATUS_INVALID_PARAMETER; } apcCtx = (SYS_USER_ASYNC_APC_CONTEXT*)MmAllocatePoolTag(sizeof(SYS_USER_ASYNC_APC_CONTEXT), MM_POOL_TAG('u', 'a', 'p', 'c')); if (!apcCtx) { if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&socket->Header); return NTLSTATUS_NO_MEMORY; } memset(apcCtx, 0, sizeof(*apcCtx)); ObReferenceObject(&thread->Header); apcCtx->TargetThread = thread; apcCtx->UserRoutine = req.ApcRoutine; apcCtx->UserContext = req.ApcContext; KeInitializeApc(&apcCtx->KernelApc, thread, SyscallUserAsyncKernelRoutine, SyscallUserAsyncNormalRoutine, apcCtx); KeSetApcRundownRoutine(&apcCtx->KernelApc, SyscallUserAsyncApcRundown); completionApc = &apcCtx->KernelApc; } NET_ASYNC_SOCKET_SUBMIT submit; memset(&submit, 0, sizeof(submit)); submit.OwnerProcess = proc; submit.Socket = socket; submit.CompletionEvent = event; submit.CompletionApc = completionApc; submit.Iosb = (IO_STATUS_BLOCK*)req.Iosb; status = MmLockUserBuffer(proc, req.Iosb, sizeof(*req.Iosb), 1, &submit.LockedIosb); if (!KSUCCESS(status)) goto fail; status = SyscallAsyncSocketPrepareOperation(proc, &req, &submit); if (!KSUCCESS(status)) goto fail; NET_ASYNC_SOCKET_REQUEST_OBJECT* asyncReq = (NET_ASYNC_SOCKET_REQUEST_OBJECT*)0; status = NetSocketSubmitAsyncKernel(&submit, &asyncReq); if (status == NTLSTATUS_PENDING) { memset(&submit, 0, sizeof(submit)); } if (!KSUCCESS(status) && status != NTLSTATUS_PENDING) goto fail; if (req.RequestHandleOut && asyncReq) { SYS_HANDLE requestHandle = SYS_HANDLE_INVALID; KSTATUS hs = KeCreateHandle(proc, NetAsyncSocketRequestObject(asyncReq), KeGetValidHandleRights(ObjectTypeAsyncIo), &requestHandle); if (!KSUCCESS(hs)) { (void)IoCancelAsyncRequest((IO_ASYNC_REQUEST_OBJECT*)asyncReq); ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq)); status = hs; goto fail_after_submit; } KSTATUS cs = CopyToUser(req.RequestHandleOut, &requestHandle, sizeof(requestHandle)); if (!KSUCCESS(cs)) { (void)IoCancelAsyncRequest((IO_ASYNC_REQUEST_OBJECT*)asyncReq); KeCloseHandle(proc, requestHandle); ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq)); status = cs; goto fail_after_submit; } } else if (req.RequestHandleOut) { SYS_HANDLE zeroHandle = SYS_HANDLE_INVALID; KSTATUS cs = CopyToUser(req.RequestHandleOut, &zeroHandle, sizeof(zeroHandle)); if (!KSUCCESS(cs)) { status = cs; goto fail_after_submit; } } if (asyncReq) ObDereferenceObject(NetAsyncSocketRequestObject(asyncReq)); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&socket->Header); return status; fail: SyscallAsyncSocketReleaseSubmitResources(&submit); if (apcCtx) SyscallUserAsyncApcContextFree(apcCtx); fail_after_submit: if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&socket->Header); return status; } // Implements SYS_GET_MEMORY_STATUS. static uint64_t SyscallGetMemoryStatus(SYS_MEMORY_STATUS* statusOut) { if (!statusOut) return NTLSTATUS_INVALID_PARAMETER; MM_PFN_STATS pfn; MmPfnQueryStats(&pfn); SYS_MEMORY_STATUS_K status; memset(&status, 0, sizeof(status)); status.FreePhysicalBytes = pfn.FreeFrames * PAGE_SIZE; status.KernelPoolBytes = MmGetKernelPoolUsage(); status.UserHeapBytes = MmGetUserHeapUsage(); status.ReclaimablePhysicalBytes = pfn.ReclaimableFrames * PAGE_SIZE; status.AvailablePhysicalBytes = pfn.AvailableFrames * PAGE_SIZE; status.PinnedPhysicalBytes = pfn.PinnedFrames * PAGE_SIZE; status.NonEvictablePhysicalBytes = pfn.NonEvictableFrames * PAGE_SIZE; status.UserPhysicalBytes = pfn.UserFrames * PAGE_SIZE; status.PageTablePhysicalBytes = pfn.PageTableFrames * PAGE_SIZE; return CopyToUser(statusOut, &status, sizeof(status)); } // Implements SYS_QUERY_KERNEL_STATS. static KSTATUS KiReturnKernelStats(void* buffer, size_t bufferSize, size_t* requiredSizeOut, const void* src, size_t size) { if (requiredSizeOut) { KSTATUS s = KiCopySizeToUser(requiredSizeOut, size); if (!KSUCCESS(s)) return s; } if (!buffer) return NTLSTATUS_SUCCESS; if (bufferSize < size) return NTLSTATUS_BUFFER_TOO_SMALL; return CopyToUser(buffer, src, size); } // Copies the current primary-display state through either native authority path. static KSTATUS KiCopyDisplayInfoToUser(SYS_DISPLAY_INFO* infoOut) { if (!infoOut) return NTLSTATUS_INVALID_PARAMETER; KE_DISPLAY_INFO ki; KSTATUS status = KeDisplayQueryInfo(&ki); if (!KSUCCESS(status)) return status; SYS_DISPLAY_INFO out; memset(&out, 0, sizeof(out)); memcpy(out.PrimaryName, ki.PrimaryName, sizeof(out.PrimaryName)); out.Mode = ki.Mode; out.Caps = ki.Caps; out.DeviceCount = ki.DeviceCount; out.Flags = ki.Flags; out.ManagerStats = ki.ManagerStats; out.DeviceStats = ki.DeviceStats; return CopyToUser(infoOut, &out, sizeof(out)); } // Returns display metadata through the legacy ambient display authority. static uint64_t SyscallQueryDisplayInfo(SYS_DISPLAY_INFO* infoOut) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_DISPLAY_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; return KiCopyDisplayInfoToUser(infoOut); } // Returns display metadata through an explicitly delegated display-manager capability. static uint64_t SyscallQueryDisplayManagerInfo(SYS_HANDLE managerHandle, SYS_DISPLAY_INFO* infoOut) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = (KE_DISPLAY_MANAGER*)KiHandleToObject( managerHandle, ObjectTypeDisplayManager, SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiCopyDisplayInfoToUser(infoOut); ObDereferenceObject(&manager->Header); return status; } // Presents one surface through explicitly delegated physical-display authority. static uint64_t SyscallPresentDisplayManagerSurface(SYS_HANDLE managerHandle, SYS_HANDLE surfaceHandle, const DISPLAY_RECT* userRects, uint32_t rectCount) { KSTATUS managerStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = (KE_DISPLAY_MANAGER*)KiHandleToObject( managerHandle, ObjectTypeDisplayManager, SYS_DISPLAY_MANAGER_RIGHT_PRESENT, &managerStatus); if (!manager) return managerStatus; DISPLAY_RECT rects[SYS_SURFACE_MAX_RECTS]; KSTATUS status = KiCaptureUserSurfaceRects(userRects, rectCount, rects); if (!KSUCCESS(status)) { ObDereferenceObject(&manager->Header); return status; } KSTATUS surfaceStatus = NTLSTATUS_SUCCESS; OBJECT* surface = KiHandleToObject(surfaceHandle, ObjectTypeSurface, SYS_SURFACE_RIGHT_MAP_READ, &surfaceStatus); if (!surface) { ObDereferenceObject(&manager->Header); return surfaceStatus; } uint64_t bytesCopied = 0; status = KeSurfacePresent(surface, rectCount ? rects : (DISPLAY_RECT*)0, rectCount, &bytesCopied); ObDereferenceObject(surface); ObDereferenceObject(&manager->Header); return status; } // Updates the hardware cursor through an explicitly delegated display-manager capability. static uint64_t SyscallSetDisplayManagerCursor(const SYS_DISPLAY_MANAGER_CURSOR_REQUEST* request) { SYS_DISPLAY_MANAGER_CURSOR_REQUEST req; KSTATUS status = KiCopySizedUserRequest(request, &req, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Flags & ~(SYS_DISPLAY_MANAGER_CURSOR_FLAG_SHOW | SYS_DISPLAY_MANAGER_CURSOR_FLAG_HIDE)) return NTLSTATUS_INVALID_PARAMETER; if ((req.Flags & SYS_DISPLAY_MANAGER_CURSOR_FLAG_SHOW) && (req.Flags & SYS_DISPLAY_MANAGER_CURSOR_FLAG_HIDE)) return NTLSTATUS_INVALID_PARAMETER; KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = (KE_DISPLAY_MANAGER*)KiHandleToObject( req.ManagerHandle, ObjectTypeDisplayManager, SYS_DISPLAY_MANAGER_RIGHT_CURSOR, &handleStatus); if (!manager) return handleStatus; status = KeDisplaySetCursorPosition(req.X, req.Y); if (KSUCCESS(status) && (req.Flags & SYS_DISPLAY_MANAGER_CURSOR_FLAG_SHOW)) status = KeDisplayShowCursor(true); if (KSUCCESS(status) && (req.Flags & SYS_DISPLAY_MANAGER_CURSOR_FLAG_HIDE)) status = KeDisplayShowCursor(false); ObDereferenceObject(&manager->Header); return status; } // Returns a referenced display manager carrying RSSRV's console authority. static KE_DISPLAY_MANAGER* KiReferenceDisplayManagerConsole(SYS_HANDLE managerHandle, KSTATUS* statusOut) { return (KE_DISPLAY_MANAGER*)KiHandleToObject(managerHandle, ObjectTypeDisplayManager, SYS_DISPLAY_MANAGER_RIGHT_CONSOLE, statusOut); } // Writes text through an explicitly delegated display-manager console capability. static uint64_t SyscallWriteDisplayManagerConsole(SYS_HANDLE managerHandle, const SYS_WRITE_CONSOLE_REQUEST* request) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiConsoleWrite(request); ObDereferenceObject(&manager->Header); return status; } // Queries console state through an explicitly delegated display-manager capability. static uint64_t SyscallQueryDisplayManagerConsoleInfo(SYS_HANDLE managerHandle, SYS_CONSOLE_INFO* infoOut) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiConsoleQueryInfo(infoOut); ObDereferenceObject(&manager->Header); return status; } // Updates the console cursor through an explicitly delegated display-manager capability. static uint64_t SyscallSetDisplayManagerConsoleCursor(SYS_HANDLE managerHandle, int32_t x, int32_t y) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiConsoleSetCursor(x, y); ObDereferenceObject(&manager->Header); return status; } // Fills console characters through an explicitly delegated display-manager capability. static uint64_t SyscallFillDisplayManagerConsoleCharacter( SYS_HANDLE managerHandle, const SYS_FILL_CONSOLE_CHARACTER_REQUEST* request) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiConsoleFillCharacter(request); ObDereferenceObject(&manager->Header); return status; } // Fills console attributes through an explicitly delegated display-manager capability. static uint64_t SyscallFillDisplayManagerConsoleAttribute( SYS_HANDLE managerHandle, const SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST* request) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiConsoleFillAttribute(request); ObDereferenceObject(&manager->Header); return status; } // Draws one explicit console glyph through delegated display-manager authority. static uint64_t SyscallPutDisplayManagerConsoleCharacter( SYS_HANDLE managerHandle, const SYS_PUT_SCREEN_CHAR_REQUEST* request) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiConsolePutCharacter(request); ObDereferenceObject(&manager->Header); return status; } // Clears the console target through delegated display-manager authority. static uint64_t SyscallClearDisplayManagerConsole(SYS_HANDLE managerHandle, uint32_t color) { KSTATUS handleStatus = NTLSTATUS_SUCCESS; KE_DISPLAY_MANAGER* manager = KiReferenceDisplayManagerConsole(managerHandle, &handleStatus); if (!manager) return handleStatus; KSTATUS status = KiConsoleClear(color); ObDereferenceObject(&manager->Header); return status; } // Queries one class of kernel diagnostic statistics. static uint64_t SyscallQueryKernelStats(uint32_t classId, void* buffer, size_t bufferSize, size_t* requiredSizeOut) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_KERNEL_DIAGNOSTICS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; switch (classId) { case SYS_KERNEL_STATS_SCHEDULER: { KE_SCHED_STATS ks; SYS_SCHEDULER_STATS us; KeGetSchedulerStats(&ks); memset(&us, 0, sizeof(us)); us.Size = sizeof(us); us.Version = SYS_STATS_VERSION; us.Ticks = ks.Ticks; us.ContextSwitches = ks.ContextSwitches; us.KeepCurrentDecisions = ks.KeepCurrentDecisions; us.IdleSelections = ks.IdleSelections; us.Demotions = ks.Demotions; us.AgingPromotions = ks.AgingPromotions; us.QuantumExpirations = ks.QuantumExpirations; us.PriorityPreemptions = ks.PriorityPreemptions; us.VoluntaryBlocks = ks.VoluntaryBlocks; us.CpuBurstDemotions = ks.CpuBurstDemotions; us.SmtSiblingDeferrals = ks.SmtSiblingDeferrals; us.SmtPrimaryPokes = ks.SmtPrimaryPokes; us.RealtimeDispatches = ks.RealtimeDispatches; us.RealtimePreemptions = ks.RealtimePreemptions; us.PriorityInheritanceBoosts = ks.PriorityInheritanceBoosts; us.PriorityInheritanceDrops = ks.PriorityInheritanceDrops; us.PriorityInheritancePropagations = ks.PriorityInheritancePropagations; us.PriorityInheritanceMaxDepth = ks.PriorityInheritanceMaxDepth; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &us, sizeof(us)); } case SYS_KERNEL_STATS_PMM: { MM_PHYSICAL_STATS km; SYS_PMM_STATS um; MmQueryPhysicalStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.TotalFrames = km.TotalFrames; um.FreeFrames = km.FreeFrames; um.UsedFrames = km.UsedFrames; um.LargestFreeRun = km.LargestFreeRun; um.FreeRunCount = km.FreeRunCount; for (size_t i = 0; i < 8; i++) um.FreeRunHistogram[i] = km.FreeRunHistogram[i]; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_PFN: { MM_PFN_STATS km; SYS_PFN_STATS um; MmPfnQueryStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.TotalFrames = km.TotalFrames; um.FreeFrames = km.FreeFrames; um.UsedFrames = km.UsedFrames; um.ReservedFrames = km.ReservedFrames; um.ZeroedFrames = km.ZeroedFrames; um.ActiveFrames = km.ActiveFrames; um.StandbyFrames = km.StandbyFrames; um.ModifiedFrames = km.ModifiedFrames; um.PinnedFrames = km.PinnedFrames; um.EvictableFrames = km.EvictableFrames; um.NonEvictableFrames = km.NonEvictableFrames; um.ReclaimableFrames = km.ReclaimableFrames; um.AvailableFrames = km.AvailableFrames; um.MappedFrames = km.MappedFrames; um.KernelFrames = km.KernelFrames; um.PageTableFrames = km.PageTableFrames; um.UserFrames = km.UserFrames; um.CleanFileCacheFrames = km.CleanFileCacheFrames; um.ActiveCleanFileCacheFrames = km.ActiveCleanFileCacheFrames; um.StandbyCleanFileCacheFrames = km.StandbyCleanFileCacheFrames; um.ImageFrames = km.ImageFrames; um.ActiveImageFrames = km.ActiveImageFrames; um.StandbyImageFrames = km.StandbyImageFrames; um.MappedFileFrames = km.MappedFileFrames; um.AnonymousFrames = km.AnonymousFrames; um.CowFrames = km.CowFrames; um.SurfaceFrames = km.SurfaceFrames; um.DriverFrames = km.DriverFrames; um.DmaFrames = km.DmaFrames; um.BootloaderFrames = km.BootloaderFrames; um.PfnDatabaseFrames = km.PfnDatabaseFrames; um.MmioFrames = km.MmioFrames; um.AllocationCount = km.AllocationCount; um.FreeCount = km.FreeCount; um.RetainCount = km.RetainCount; um.ReleaseCount = km.ReleaseCount; um.PinCount = km.PinCount; um.UnpinCount = km.UnpinCount; um.FailedAllocations = km.FailedAllocations; um.FailedContiguousAllocations = km.FailedContiguousAllocations; um.FailedInvalidRange = km.FailedInvalidRange; um.FailedEmptyRange = km.FailedEmptyRange; um.FailedReserveDenied = km.FailedReserveDenied; um.FailedNoFrame = km.FailedNoFrame; um.FailedContiguousInvalidRequest = km.FailedContiguousInvalidRequest; um.FailedContiguousReserveDenied = km.FailedContiguousReserveDenied; um.FailedContiguousNoRun = km.FailedContiguousNoRun; um.ReclaimAttempts = km.ReclaimAttempts; um.ReclaimPagesRequested = km.ReclaimPagesRequested; um.ReclaimPagesSucceeded = km.ReclaimPagesSucceeded; um.ReclaimPagesFailed = km.ReclaimPagesFailed; um.ReclaimReentrantSkips = km.ReclaimReentrantSkips; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_PTE: { MM_PTE_STATS km; SYS_PTE_STATS um; MmPteQueryStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.MapCalls = km.MapCalls; um.MapFailures = km.MapFailures; um.UnmapCalls = km.UnmapCalls; um.UnmapFailures = km.UnmapFailures; um.ProtectCalls = km.ProtectCalls; um.ProtectFailures = km.ProtectFailures; um.QueryCalls = km.QueryCalls; um.QueryFailures = km.QueryFailures; for (size_t i = 0; i < SYS_PTE_QUERY_PURPOSE_COUNT && i < MmPteQueryPurposeCount; i++) { um.QueryByPurpose[i] = km.QueryByPurpose[i]; um.QueryFailuresByPurpose[i] = km.QueryFailuresByPurpose[i]; } um.TranslateCalls = km.TranslateCalls; um.LocalFlushes = km.LocalFlushes; um.GlobalFlushes = km.GlobalFlushes; um.ShootdownRequests = km.ShootdownRequests; um.ShootdownTargetCpus = km.ShootdownTargetCpus; um.ShootdownSkippedNoTargets = km.ShootdownSkippedNoTargets; um.ShootdownAcks = km.ShootdownAcks; um.ShootdownTimeouts = km.ShootdownTimeouts; um.NewMappingNoFlushes = km.NewMappingNoFlushes; um.ReplacedMappingFlushes = km.ReplacedMappingFlushes; um.PageTablePagesAllocated = km.PageTablePagesAllocated; um.CurrentLeafMappings = km.CurrentLeafMappings; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_VAD: { MM_VAD_STATS km; SYS_VAD_STATS um; MmVadQueryStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.ResolveFaultCalls = km.ResolveFaultCalls; um.ResolvedFaults = km.ResolvedFaults; um.FailedFaults = km.FailedFaults; um.ProtectionFaults = km.ProtectionFaults; um.NoVadFaults = km.NoVadFaults; um.UncommittedFaults = km.UncommittedFaults; um.NoAccessFaults = km.NoAccessFaults; um.DemandZeroPages = km.DemandZeroPages; um.ZeroPageMaps = km.ZeroPageMaps; um.ZeroPageCowSplits = km.ZeroPageCowSplits; um.CowFaults = km.CowFaults; um.CowPagesCopied = km.CowPagesCopied; um.CowViewsCreated = km.CowViewsCreated; um.CowPagesShared = km.CowPagesShared; um.EnsureResidentCalls = km.EnsureResidentCalls; um.EnsureResidentPages = km.EnsureResidentPages; um.AlreadyResidentPages = km.AlreadyResidentPages; um.FileBackedFaults = km.FileBackedFaults; um.FileBackedReads = km.FileBackedReads; um.FileBackedReadFailures = km.FileBackedReadFailures; um.FileBackedDiscards = km.FileBackedDiscards; um.FileBackedSharedWritableFaults = km.FileBackedSharedWritableFaults; um.FileBackedDirtyFaults = km.FileBackedDirtyFaults; um.FileBackedDirtySyncs = km.FileBackedDirtySyncs; um.ImageBackedFaults = km.ImageBackedFaults; um.ImageBackedZeroFaults = km.ImageBackedZeroFaults; um.ImageBackedPrivateFaults = km.ImageBackedPrivateFaults; um.ImageFixupPrivateCalls = km.ImageFixupPrivateCalls; um.ImageFixupPrivatePages = km.ImageFixupPrivatePages; um.SwapPageOuts = km.SwapPageOuts; um.SwapPageIns = km.SwapPageIns; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_FILE_CACHE: { MM_FILE_CACHE_STATS km; SYS_FILE_CACHE_STATS um; MmFileCacheQueryStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.Lookups = km.Lookups; um.Hits = km.Hits; um.Misses = km.Misses; um.Loads = km.Loads; um.LoadFailures = km.LoadFailures; um.RaceDiscards = km.RaceDiscards; um.InflightWaits = km.InflightWaits; um.Invalidations = km.Invalidations; um.DirtyPages = km.DirtyPages; um.MaxDirtyPages = km.MaxDirtyPages; um.DirtyMarks = km.DirtyMarks; um.WritebackRequests = km.WritebackRequests; um.WritebackPages = km.WritebackPages; um.WritebackBytes = km.WritebackBytes; um.WritebackFailures = km.WritebackFailures; um.FlushFileCalls = km.FlushFileCalls; um.FlushFileFailures = km.FlushFileFailures; um.MappedDirtySyncs = km.MappedDirtySyncs; um.MappedDirtyPages = km.MappedDirtyPages; um.Drops = km.Drops; um.DropFailures = km.DropFailures; um.StandbyTransitions = km.StandbyTransitions; um.StandbyHits = km.StandbyHits; um.ResidentPages = km.ResidentPages; um.ActivePages = km.ActivePages; um.StandbyPages = km.StandbyPages; um.MaxResidentPages = km.MaxResidentPages; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_SURFACE: { SYS_SURFACE_STATS stats; memset(&stats, 0, sizeof(stats)); KeSurfaceQueryStats(&stats); return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &stats, sizeof(stats)); } case SYS_KERNEL_STATS_SWAP: { MM_SWAP_STATS km; SYS_SWAP_STATS um; MmSwapQueryStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.Enabled = km.Enabled; um.TotalSlots = km.TotalSlots; um.UsedSlots = km.UsedSlots; um.FreeSlots = km.FreeSlots; um.HighWaterSlots = km.HighWaterSlots; um.PageOuts = km.PageOuts; um.PageIns = km.PageIns; um.PageOutFailures = km.PageOutFailures; um.PageInFailures = km.PageInFailures; um.SlotAllocations = km.SlotAllocations; um.SlotFrees = km.SlotFrees; um.SlotAllocationFailures = km.SlotAllocationFailures; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_FILESYSTEM: { IO_FILESYSTEM_STATS km; SYS_FILESYSTEM_STATS um; IoQueryFilesystemStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.PathLookups = km.PathLookups; um.PathCacheHits = km.PathCacheHits; um.PathCacheMisses = km.PathCacheMisses; um.PathCacheStores = km.PathCacheStores; um.PathCacheInvalidations = km.PathCacheInvalidations; um.PathCacheGeneration = km.PathCacheGeneration; um.DirectorySearches = km.DirectorySearches; um.DirectoryBlocksRead = km.DirectoryBlocksRead; um.DirectoryEntriesScanned = km.DirectoryEntriesScanned; um.DataReadRuns = km.DataReadRuns; um.DataReadSectors = km.DataReadSectors; um.DataReadRunMerges = km.DataReadRunMerges; um.MaxDataReadSpanSectors = km.MaxDataReadSpanSectors; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_VOLUME: { IO_VOLUME_STATS km; SYS_VOLUME_STATS um; IoQueryVolumeStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.CreatedVolumes = km.CreatedVolumes; um.MountedVolumes = km.MountedVolumes; um.RemovedVolumes = km.RemovedVolumes; um.DeadIoRejects = km.DeadIoRejects; um.ProviderDestroys = km.ProviderDestroys; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_VOLUME_MANAGER: { IO_VOLUME_MANAGER_STATS km; SYS_VOLUME_MANAGER_STATS um; IoQueryVolumeManagerStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.Snapshots = km.Snapshots; um.VolumeOpens = km.VolumeOpens; um.Waits = km.Waits; um.Wakeups = km.Wakeups; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_DISK: { IO_DISK_STATS km; SYS_DISK_STATS um; IoQueryDiskStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.RegisteredDisks = km.RegisteredDisks; um.ReadRequests = km.ReadRequests; um.ReadSectors = km.ReadSectors; um.WriteRequests = km.WriteRequests; um.WriteSectors = km.WriteSectors; um.MaxReadSectors = km.MaxReadSectors; um.MaxWriteSectors = km.MaxWriteSectors; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_NET_IRP: { NET_IRP_STATS km; SYS_NET_IRP_STATS um; NetQueryIrpStats(&km); memset(&um, 0, sizeof(um)); um.Size = sizeof(um); um.Version = SYS_STATS_VERSION; um.Submitted = km.Submitted; um.Completed = km.Completed; um.CompletedSynchronously = km.CompletedSynchronously; um.CompletedAsynchronously = km.CompletedAsynchronously; um.Pending = km.Pending; um.CancelRequested = km.CancelRequested; um.Cancelled = km.Cancelled; um.WouldBlock = km.WouldBlock; um.Timeout = km.Timeout; um.Failed = km.Failed; for (uint32_t i = 0; i < SYS_NET_IRP_OPERATION_COUNT && i < NetSocketIrpOperationCount; ++i) um.ByOperation[i] = km.ByOperation[i]; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &um, sizeof(um)); } case SYS_KERNEL_STATS_EXCEPTION: { SYS_EXCEPTION_STATS stats; KeQueryExceptionStats(&stats); return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &stats, sizeof(stats)); } case SYS_KERNEL_STATS_PORT: { KE_PORT_STATS kernelStats; SYS_PORT_STATS stats; memset(&kernelStats, 0, sizeof(kernelStats)); memset(&stats, 0, sizeof(stats)); KePortQueryStats(&kernelStats); stats.Size = sizeof(stats); stats.Version = SYS_STATS_VERSION; stats.ConnectionsCreated = kernelStats.ConnectionsCreated; stats.DirectDeliveries = kernelStats.DirectDeliveries; stats.DirectBytes = kernelStats.DirectBytes; stats.QueuedMessages = kernelStats.QueuedMessages; stats.QueuedBytes = kernelStats.QueuedBytes; stats.QueuedDeliveries = kernelStats.QueuedDeliveries; stats.RepliesCompleted = kernelStats.RepliesCompleted; stats.Disconnects = kernelStats.Disconnects; stats.ProcessQuotaRejects = kernelStats.ProcessQuotaRejects; return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &stats, sizeof(stats)); } case SYS_KERNEL_STATS_CPU: { struct { SYS_CPU_STATS Header; SYS_CPU_STAT Records[KE_MAX_CPUS]; } stats; memset(&stats, 0, sizeof(stats)); uint32_t n = gCpuCount; if (n > KE_MAX_CPUS) n = KE_MAX_CPUS; size_t size = sizeof(SYS_CPU_STATS) + (size_t)n * sizeof(SYS_CPU_STAT); stats.Header.Size = (uint32_t)size; stats.Header.Version = SYS_STATS_VERSION; stats.Header.HeaderSize = sizeof(SYS_CPU_STATS); stats.Header.CpuCount = gCpuCount; stats.Header.RecordSize = sizeof(SYS_CPU_STAT); stats.Header.RecordCount = n; for (uint32_t i = 0; i < n; i++) { KCPU* cpu = &gCpus[i]; SYS_CPU_STAT* out = &stats.Records[i]; out->CpuIndex = cpu->CpuIndex; out->HardwareId = cpu->HardwareId; KE_CPU_STATE state = (KE_CPU_STATE)cpu->State; if (state >= KeCpuPresent) out->Flags |= SYS_CPU_FLAG_PRESENT; if (state >= KeCpuStarted) out->Flags |= SYS_CPU_FLAG_STARTED; if (state == KeCpuParked) out->Flags |= SYS_CPU_FLAG_PARKED; if (state == KeCpuOnline) out->Flags |= SYS_CPU_FLAG_ONLINE; if (cpu->IsBootProcessor) out->Flags |= SYS_CPU_FLAG_BOOT_PROCESSOR; if (cpu->IsPrimaryThread) out->Flags |= SYS_CPU_FLAG_PRIMARY; out->CurrentTid = cpu->CurrentThread ? cpu->CurrentThread->ThreadId : 0; if (cpu->CurrentThread && cpu->CurrentThread->IsIdleThread) out->CurrentThreadFlags |= SYS_CPU_THREAD_FLAG_IDLE; out->SchedulerTicks = cpu->SchedulerTicks; out->ContextSwitches = cpu->ContextSwitches; } return KiReturnKernelStats(buffer, bufferSize, requiredSizeOut, &stats, size); } default: return NTLSTATUS_INVALID_PARAMETER; } } // Converts one public audio format into the kernel mixer format. static KSTATUS KiImportAudioFormat(const SYS_AUDIO_FORMAT* source, IO_AUDIO_FORMAT* destination) { if (!source || !destination) return NTLSTATUS_INVALID_PARAMETER; if (source->SampleFormat != SysAudioSampleS16 || (source->ChannelCount != 1 && source->ChannelCount != 2) || (source->SampleRate != 11025u && source->SampleRate != 22050u && source->SampleRate != 44100u && source->SampleRate != 48000u)) return NTLSTATUS_NOT_SUPPORTED; destination->SampleRate = source->SampleRate; destination->ChannelCount = source->ChannelCount; destination->SampleFormat = IoAudioSampleS16; return NTLSTATUS_SUCCESS; } // Converts one kernel audio format into the public syscall representation. static SYS_AUDIO_FORMAT KiExportAudioFormat(const IO_AUDIO_FORMAT* source) { SYS_AUDIO_FORMAT destination; destination.SampleRate = source->SampleRate; destination.ChannelCount = source->ChannelCount; destination.SampleFormat = source->SampleFormat == IoAudioSampleS16 ? SysAudioSampleS16 : 0; return destination; } // Converts one normalized kernel gain into unsigned Q16. static uint32_t KiExportAudioGainQ16(float gain) { if (gain <= 0.0f) return 0; if (gain >= 1.0f) return SYS_AUDIO_GAIN_Q16_UNITY; return (uint32_t)(gain * (float)SYS_AUDIO_GAIN_Q16_UNITY + 0.5f); } // Converts one normalized kernel pan into signed Q16. static int32_t KiExportAudioPanQ16(float pan) { if (pan <= -1.0f) return SYS_AUDIO_PAN_Q16_LEFT; if (pan >= 1.0f) return SYS_AUDIO_PAN_Q16_RIGHT; float scaled = pan * (float)SYS_AUDIO_GAIN_Q16_UNITY; return (int32_t)(scaled + (scaled >= 0.0f ? 0.5f : -0.5f)); } // Converts one public unsigned Q16 gain into the kernel representation. static float KiImportAudioGainQ16(uint32_t gainQ16) { return (float)gainQ16 / (float)SYS_AUDIO_GAIN_Q16_UNITY; } // Converts one public signed Q16 pan into the kernel representation. static float KiImportAudioPanQ16(int32_t panQ16) { return (float)panQ16 / (float)SYS_AUDIO_GAIN_Q16_UNITY; } // Converts one kernel endpoint kind into its public ABI value. static uint32_t KiExportAudioEndpointKind(IO_AUDIO_ENDPOINT_KIND kind) { switch (kind) { case IoAudioEndpointLineOut: return SysAudioEndpointLineOut; case IoAudioEndpointSpeakerHeadphone: return SysAudioEndpointSpeakerHeadphone; case IoAudioEndpointOtherAnalog: return SysAudioEndpointOtherAnalog; default: return 0; } } // Converts provider endpoint capabilities into public capability bits. static uint32_t KiExportAudioEndpointCapabilities(uint32_t capabilities) { uint32_t result = 0; if (capabilities & IO_AUDIO_ENDPOINT_CAP_JACK_SENSE) result |= SYS_AUDIO_ENDPOINT_CAP_JACK_SENSE; if (capabilities & IO_AUDIO_ENDPOINT_CAP_HARDWARE_GAIN) result |= SYS_AUDIO_ENDPOINT_CAP_HARDWARE_GAIN; return result; } // Converts one endpoint lifecycle value into its public ABI value. static uint32_t KiExportAudioEndpointState(KE_AUDIO_ENDPOINT_STATE state) { switch (state) { case KeAudioEndpointStarting: return SysAudioEndpointStarting; case KeAudioEndpointReady: return SysAudioEndpointReady; case KeAudioEndpointStopping: return SysAudioEndpointStopping; case KeAudioEndpointFailed: return SysAudioEndpointFailed; case KeAudioEndpointRemoved: return SysAudioEndpointRemoved; default: return 0; } } // Converts one stream lifecycle value into its public ABI value. static uint32_t KiExportAudioStreamState(KE_AUDIO_STREAM_STATE state) { switch (state) { case KeAudioStreamCreated: return SysAudioStreamCreated; case KeAudioStreamRunning: return SysAudioStreamRunning; case KeAudioStreamPaused: return SysAudioStreamPaused; case KeAudioStreamDraining: return SysAudioStreamDraining; case KeAudioStreamStopped: return SysAudioStreamStopped; case KeAudioStreamFailed: return SysAudioStreamFailed; case KeAudioStreamClosed: default: return 0; } } // Converts public wait flags into the kernel stream condition mask. static uint32_t KiImportAudioWaitMask(uint32_t mask) { uint32_t result = 0; if (mask & SYS_AUDIO_WAIT_WRITABLE) result |= KE_AUDIO_WAIT_WRITABLE; if (mask & SYS_AUDIO_WAIT_CONSUMED) result |= KE_AUDIO_WAIT_CONSUMED; if (mask & SYS_AUDIO_WAIT_PRESENTED) result |= KE_AUDIO_WAIT_PRESENTED; if (mask & SYS_AUDIO_WAIT_STOPPED) result |= KE_AUDIO_WAIT_STOPPED; if (mask & SYS_AUDIO_WAIT_DRAINED) result |= KE_AUDIO_WAIT_DRAINED; if (mask & SYS_AUDIO_WAIT_FAILED) result |= KE_AUDIO_WAIT_FAILED; if (mask & SYS_AUDIO_WAIT_DISCONTINUITY) result |= KE_AUDIO_WAIT_DISCONTINUITY; return result; } // Looks up one audio stream handle with the requested operation rights. static KE_AUDIO_STREAM* KiLookupAudioStream(SYS_HANDLE handle, SYS_RIGHTS rights, KSTATUS* statusOut) { OBJECT* object = KiHandleToObject(handle, ObjectTypeAudioStream, rights, statusOut); return object ? (KE_AUDIO_STREAM*)object : (KE_AUDIO_STREAM*)0; } // Implements SYS_AUDIO_ENUMERATE_ENDPOINTS. static uint64_t SyscallAudioEnumerateEndpoints(const SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST* userRequest) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_AUDIO_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || request.Reserved != 0 || !request.CountOut || (request.Capacity && !request.Endpoints)) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(request.CountOut, sizeof(*request.CountOut)); if (!KSUCCESS(status)) return status; uint32_t copyCapacity = request.Capacity < SYS_AUDIO_MAX_ENDPOINTS ? request.Capacity : SYS_AUDIO_MAX_ENDPOINTS; if (copyCapacity) { status = MmProbeUserWrite(request.Endpoints, (size_t)copyCapacity * sizeof(*request.Endpoints)); if (!KSUCCESS(status)) return status; } KE_AUDIO_ENDPOINT_INFO kernelInfo[KE_AUDIO_MAX_ENDPOINTS]; uint32_t count = 0; status = KeAudioQueryEndpoints(kernelInfo, KE_AUDIO_MAX_ENDPOINTS, &count); if (KFAILED(status)) return status; SYS_AUDIO_ENDPOINT_INFO publicInfo[SYS_AUDIO_MAX_ENDPOINTS]; uint32_t copyCount = copyCapacity < count ? copyCapacity : count; for (uint32_t index = 0; index < copyCount; index++) { const KE_AUDIO_ENDPOINT_INFO* source = &kernelInfo[index]; SYS_AUDIO_ENDPOINT_INFO* destination = &publicInfo[index]; memset(destination, 0, sizeof(*destination)); destination->Size = sizeof(*destination); destination->Version = SYS_AUDIO_ABI_VERSION; destination->EndpointId = source->EndpointId; destination->Kind = KiExportAudioEndpointKind(source->Kind); if (source->Available) destination->Flags |= SYS_AUDIO_ENDPOINT_FLAG_AVAILABLE; if (source->IsDefault) destination->Flags |= SYS_AUDIO_ENDPOINT_FLAG_DEFAULT; destination->Capabilities = KiExportAudioEndpointCapabilities(source->Capabilities); destination->State = KiExportAudioEndpointState(source->State); destination->HardwareFormat = KiExportAudioFormat(&source->HardwareFormat); destination->PeriodCount = source->PeriodCount; destination->FramesPerPeriod = source->FramesPerPeriod; destination->MasterVolumeQ16 = KiExportAudioGainQ16(source->MasterRequestedVolume); destination->MasterMuted = source->MasterMuted ? 1u : 0u; destination->FailureStatus = source->FailureStatus; } if (copyCount) { status = CopyToUser(request.Endpoints, publicInfo, (size_t)copyCount * sizeof(publicInfo[0])); if (!KSUCCESS(status)) return status; } status = CopyToUser(request.CountOut, &count, sizeof(count)); if (!KSUCCESS(status)) return status; return request.Capacity < count ? NTLSTATUS_BUFFER_TOO_SMALL : NTLSTATUS_SUCCESS; } // Implements SYS_AUDIO_OPEN_STREAM. static uint64_t SyscallAudioOpenStream(const SYS_AUDIO_OPEN_STREAM_REQUEST* userRequest) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_AUDIO_ACCESS); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; SYS_AUDIO_OPEN_STREAM_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || request.Reserved != 0 || !request.EndpointId || !request.StreamHandleOut) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(request.StreamHandleOut, sizeof(*request.StreamHandleOut)); if (!KSUCCESS(status)) return status; IO_AUDIO_FORMAT format; status = KiImportAudioFormat(&request.Format, &format); if (!KSUCCESS(status)) return status; KE_AUDIO_STREAM* stream = (KE_AUDIO_STREAM*)0; status = KeAudioCreateStream(request.EndpointId, &format, &stream); if (!KSUCCESS(status)) return status; SYS_HANDLE handle = SYS_HANDLE_INVALID; status = KiAllocHandleForCurrentWithRights((OBJECT*)stream, KeGetValidHandleRights(ObjectTypeAudioStream), &handle); if (!KSUCCESS(status)) return status; status = CopyToUser(request.StreamHandleOut, &handle, sizeof(handle)); if (!KSUCCESS(status)) { PROCESS* process = KeGetCurrentProcess(); if (process) KeCloseHandle(process, handle); } return status; } // Implements SYS_AUDIO_WRITE. static uint64_t SyscallAudioWrite(const SYS_AUDIO_WRITE_REQUEST* userRequest) { SYS_AUDIO_WRITE_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || !request.StreamHandle || !request.WrittenFramesOut || (request.FrameCount && !request.Frames)) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(request.WrittenFramesOut, sizeof(*request.WrittenFramesOut)); if (!KSUCCESS(status)) return status; uint32_t written = 0; KE_AUDIO_STREAM* stream = KiLookupAudioStream(request.StreamHandle, SYS_AUDIO_STREAM_RIGHT_WRITE, &status); if (!stream) return status; if (!request.FrameCount) { ObDereferenceObject((OBJECT*)stream); return CopyToUser(request.WrittenFramesOut, &written, sizeof(written)); } KE_AUDIO_STREAM_INFO info; status = KeAudioQueryStream(stream, &info); if (!KSUCCESS(status)) { ObDereferenceObject((OBJECT*)stream); return status; } if (info.State == KeAudioStreamFailed) { status = KFAILED(info.FailureStatus) ? info.FailureStatus : NTLSTATUS_DEVICE_ERROR; ObDereferenceObject((OBJECT*)stream); (void)CopyToUser(request.WrittenFramesOut, &written, sizeof(written)); return status; } if (info.State == KeAudioStreamDraining) { ObDereferenceObject((OBJECT*)stream); (void)CopyToUser(request.WrittenFramesOut, &written, sizeof(written)); return NTLSTATUS_INVALID_PARAMETER; } if (!info.WritableFrames) { ObDereferenceObject((OBJECT*)stream); status = CopyToUser(request.WrittenFramesOut, &written, sizeof(written)); return KSUCCESS(status) ? NTLSTATUS_WOULD_BLOCK : status; } uint32_t frameCount = request.FrameCount < info.WritableFrames ? request.FrameCount : info.WritableFrames; uint32_t frameBytes = (uint32_t)info.Format.ChannelCount * sizeof(int16_t); size_t byteCount = (size_t)frameCount * frameBytes; status = MmProbeUserRead(request.Frames, byteCount); if (!KSUCCESS(status)) { ObDereferenceObject((OBJECT*)stream); return status; } void* buffer = MmAllocatePoolTag(byteCount, MM_POOL_TAG('a', 'u', 'w', 'r')); if (!buffer) { ObDereferenceObject((OBJECT*)stream); return NTLSTATUS_NO_MEMORY; } status = CopyFromUser(buffer, request.Frames, byteCount); if (KSUCCESS(status)) status = CopyToUser(request.WrittenFramesOut, &written, sizeof(written)); if (KSUCCESS(status)) status = KeAudioWriteStream(stream, buffer, frameCount, &written); MmFreePool(buffer); ObDereferenceObject((OBJECT*)stream); if (!KSUCCESS(status)) return status; return CopyToUser(request.WrittenFramesOut, &written, sizeof(written)); } // Implements SYS_AUDIO_ARM_WAIT. static uint64_t SyscallAudioArmWait(const SYS_AUDIO_ARM_WAIT_REQUEST* userRequest) { SYS_AUDIO_ARM_WAIT_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || request.Reserved != 0 || !request.StreamHandle || !request.Mask || (request.Mask & ~SYS_AUDIO_WAIT_VALID_MASK) != 0) return NTLSTATUS_INVALID_PARAMETER; KE_AUDIO_STREAM_WAIT_CONDITION condition; condition.Mask = KiImportAudioWaitMask(request.Mask); condition.WritableFrames = request.WritableFrames; condition.ConsumedFrames = request.ConsumedFrames; condition.PresentedFrames = request.PresentedFrames; condition.DrainedGeneration = request.DrainedGeneration; condition.DiscontinuityGeneration = request.DiscontinuityGeneration; KE_AUDIO_STREAM* stream = KiLookupAudioStream(request.StreamHandle, SYS_RIGHT_WAIT, &status); if (!stream) return status; status = KeAudioArmStreamWait(stream, &condition); ObDereferenceObject((OBJECT*)stream); return status; } // Implements SYS_AUDIO_QUERY_STREAM. static uint64_t SyscallAudioQueryStream(SYS_HANDLE streamHandle, SYS_AUDIO_STREAM_INFO* infoOut) { if (!streamHandle || !infoOut) return NTLSTATUS_INVALID_PARAMETER; uint32_t userSize = 0; KSTATUS status = CopyFromUser(&userSize, infoOut, sizeof(userSize)); if (!KSUCCESS(status)) return status; if (userSize != sizeof(SYS_AUDIO_STREAM_INFO)) return NTLSTATUS_INVALID_PARAMETER; status = MmProbeUserWrite(infoOut, sizeof(SYS_AUDIO_STREAM_INFO)); if (!KSUCCESS(status)) return status; KE_AUDIO_STREAM* stream = KiLookupAudioStream(streamHandle, SYS_RIGHT_QUERY, &status); if (!stream) return status; KE_AUDIO_STREAM_INFO kernelInfo; status = KeAudioQueryStream(stream, &kernelInfo); ObDereferenceObject((OBJECT*)stream); if (!KSUCCESS(status)) return status; SYS_AUDIO_STREAM_INFO publicInfo; memset(&publicInfo, 0, sizeof(publicInfo)); publicInfo.Size = sizeof(publicInfo); publicInfo.Version = SYS_AUDIO_ABI_VERSION; publicInfo.State = KiExportAudioStreamState(kernelInfo.State); publicInfo.Format = KiExportAudioFormat(&kernelInfo.Format); publicInfo.CapacityFrames = kernelInfo.CapacityFrames; publicInfo.BufferedFrames = kernelInfo.BufferedFrames; publicInfo.WritableFrames = kernelInfo.WritableFrames; publicInfo.FailureStatus = kernelInfo.FailureStatus; publicInfo.FramesSubmitted = kernelInfo.FramesSubmitted; publicInfo.FramesConsumed = kernelInfo.FramesConsumed; publicInfo.FramesPresented = kernelInfo.FramesPresented; publicInfo.FramesDiscarded = kernelInfo.FramesDiscarded; publicInfo.OutputFramesMixed = kernelInfo.OutputFramesMixed; publicInfo.SilenceFramesInserted = kernelInfo.SilenceFramesInserted; publicInfo.Underruns = kernelInfo.Underruns; publicInfo.DrainedGeneration = kernelInfo.DrainedGeneration; publicInfo.DiscontinuityGeneration = kernelInfo.DiscontinuityGeneration; publicInfo.VolumeQ16 = KiExportAudioGainQ16(kernelInfo.Volume); publicInfo.PanQ16 = KiExportAudioPanQ16(kernelInfo.Pan); publicInfo.Muted = kernelInfo.Muted; return CopyToUser(infoOut, &publicInfo, sizeof(publicInfo)); } typedef KSTATUS (*KI_AUDIO_STREAM_OPERATION)(KE_AUDIO_STREAM* stream); // Invokes one existing kernel stream operation through a write-capable handle. static KSTATUS KiCallAudioStreamOperation(SYS_HANDLE handle, KI_AUDIO_STREAM_OPERATION operation) { if (!handle || !operation) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NTLSTATUS_SUCCESS; KE_AUDIO_STREAM* stream = KiLookupAudioStream(handle, SYS_AUDIO_STREAM_RIGHT_CONTROL, &status); if (!stream) return status; status = operation(stream); ObDereferenceObject((OBJECT*)stream); return status; } // Implements SYS_AUDIO_START. static uint64_t SyscallAudioStart(SYS_HANDLE streamHandle) { return KiCallAudioStreamOperation(streamHandle, KeAudioStartStream); } // Implements SYS_AUDIO_PAUSE. static uint64_t SyscallAudioPause(SYS_HANDLE streamHandle) { return KiCallAudioStreamOperation(streamHandle, KeAudioPauseStream); } // Implements SYS_AUDIO_RESUME. static uint64_t SyscallAudioResume(SYS_HANDLE streamHandle) { return KiCallAudioStreamOperation(streamHandle, KeAudioResumeStream); } // Implements SYS_AUDIO_DRAIN. static uint64_t SyscallAudioDrain(SYS_HANDLE streamHandle) { return KiCallAudioStreamOperation(streamHandle, KeAudioDrainStream); } // Implements SYS_AUDIO_STOP. static uint64_t SyscallAudioStop(SYS_HANDLE streamHandle) { return KiCallAudioStreamOperation(streamHandle, KeAudioStopStream); } // Implements SYS_AUDIO_RESET. static uint64_t SyscallAudioReset(SYS_HANDLE streamHandle) { return KiCallAudioStreamOperation(streamHandle, KeAudioResetStream); } // Implements SYS_AUDIO_SET_VOLUME. static uint64_t SyscallAudioSetVolume(const SYS_AUDIO_SET_VOLUME_REQUEST* userRequest) { SYS_AUDIO_SET_VOLUME_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || !request.StreamHandle || ((const uint8_t*)&request)[offsetof(SYS_AUDIO_SET_VOLUME_REQUEST, Muted)] > 1u || request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY || request.PanQ16 < SYS_AUDIO_PAN_Q16_LEFT || request.PanQ16 > SYS_AUDIO_PAN_Q16_RIGHT) return NTLSTATUS_INVALID_PARAMETER; KE_AUDIO_STREAM* stream = KiLookupAudioStream(request.StreamHandle, SYS_AUDIO_STREAM_RIGHT_CONTROL, &status); if (!stream) return status; status = KeAudioSetStreamVolume(stream, KiImportAudioGainQ16(request.VolumeQ16), KiImportAudioPanQ16(request.PanQ16), request.Muted != 0); ObDereferenceObject((OBJECT*)stream); return status; } // Implements SYS_AUDIO_SET_MASTER_VOLUME. static uint64_t SyscallAudioSetMasterVolume(const SYS_AUDIO_SET_MASTER_VOLUME_REQUEST* userRequest) { KSTATUS privilegeStatus = KiRequirePrivilege(SYS_PRIV_AUDIO_MASTER_CONTROL); if (!KSUCCESS(privilegeStatus)) return privilegeStatus; SYS_AUDIO_SET_MASTER_VOLUME_REQUEST request; KSTATUS status = KiCopySizedUserRequest(userRequest, &request, sizeof(request)); if (!KSUCCESS(status)) return status; if (request.Flags != 0 || !request.EndpointId || ((const uint8_t*)&request)[offsetof(SYS_AUDIO_SET_MASTER_VOLUME_REQUEST, Muted)] > 1u || request.VolumeQ16 > SYS_AUDIO_GAIN_Q16_UNITY) return NTLSTATUS_INVALID_PARAMETER; return KeAudioSetMasterVolume(request.EndpointId, KiImportAudioGainQ16(request.VolumeQ16), request.Muted != 0); } // Dispatch table keyed by SYS_* constants. typedef uint64_t (*SYSCALL_HANDLER0)(void); typedef uint64_t (*SYSCALL_HANDLER1)(uint64_t); typedef uint64_t (*SYSCALL_HANDLER2)(uint64_t, uint64_t); typedef uint64_t (*SYSCALL_HANDLER3)(uint64_t, uint64_t, uint64_t); typedef uint64_t (*SYSCALL_HANDLER4)(uint64_t, uint64_t, uint64_t, uint64_t); typedef struct SYSCALL_ENTRY { void* Handler; uint8_t ArgumentCount; } SYSCALL_ENTRY; #define SYSCALL_ENTRY0(handler) { handler, 0 } #define SYSCALL_ENTRY1(handler) { handler, 1 } #define SYSCALL_ENTRY2(handler) { handler, 2 } #define SYSCALL_ENTRY3(handler) { handler, 3 } #define SYSCALL_ENTRY4(handler) { handler, 4 } static const SYSCALL_ENTRY gSyscallTable[SYS_MAX] = { // Process, threads, waits, APC, and exceptions. [SYS_OPEN_POWER_EVENT] = SYSCALL_ENTRY1(SyscallOpenPowerEvent), [SYS_REQUEST_SYSTEM_POWER] = SYSCALL_ENTRY1(SyscallRequestSystemPower), [SYS_QUERY_SYSTEM_POWER] = SYSCALL_ENTRY1(SyscallQuerySystemPower), [SYS_COMPLETE_SYSTEM_POWER] = SYSCALL_ENTRY1(SyscallCompleteSystemPower), [SYS_EXIT_PROCESS] = SYSCALL_ENTRY1(SyscallExitProcess), [SYS_EXIT_THREAD] = SYSCALL_ENTRY1(SyscallExitThread), [SYS_CREATE_PROCESS_OBJECT] = SYSCALL_ENTRY1(SyscallCreateProcessObject), [SYS_ALLOC_PROCESS_VM] = SYSCALL_ENTRY1(SyscallAllocProcessVm), [SYS_PROTECT_PROCESS_VM] = SYSCALL_ENTRY4(SyscallProtectProcessVm), [SYS_START_PROCESS] = SYSCALL_ENTRY1(SyscallStartProcess), [SYS_OPEN_PROCESS] = SYSCALL_ENTRY3(SyscallOpenProcess), [SYS_GET_PROCESS_EXIT_CODE] = SYSCALL_ENTRY2(SyscallGetProcessExitCode), [SYS_KILL_PROCESS] = SYSCALL_ENTRY2(SyscallKillProcess), [SYS_GET_PROCESS_LIST] = SYSCALL_ENTRY3(SyscallGetProcessList), [SYS_GET_PROCESS_PID] = SYSCALL_ENTRY2(SyscallGetProcessPid), [SYS_CREATE_THREAD] = SYSCALL_ENTRY1(SyscallCreateThread), [SYS_OPEN_CURRENT_THREAD] = SYSCALL_ENTRY2(SyscallOpenCurrentThread), [SYS_RESUME_THREAD] = SYSCALL_ENTRY2(SyscallResumeThread), [SYS_RESUME_THREAD_AND_WAIT] = SYSCALL_ENTRY2(SyscallResumeThreadAndWait), [SYS_GET_THREAD_LIST] = SYSCALL_ENTRY4(SyscallGetThreadList), [SYS_GET_THREAD_TID] = SYSCALL_ENTRY2(SyscallGetThreadTid), [SYS_GET_CURRENT_TID] = SYSCALL_ENTRY0(SyscallGetCurrentTid), [SYS_GET_CURRENT_PID] = SYSCALL_ENTRY0(SyscallGetCurrentPid), [SYS_SET_THREAD_PRIORITY] = SYSCALL_ENTRY1(SyscallSetThreadPriority), [SYS_SET_THREAD_NAME] = SYSCALL_ENTRY1(SyscallSetThreadName), [SYS_QUERY_THREAD_NAME] = SYSCALL_ENTRY4(SyscallQueryThreadName), [SYS_YIELD] = SYSCALL_ENTRY0(SyscallYield), [SYS_SLEEP] = SYSCALL_ENTRY1(SyscallSleep), [SYS_WAIT_SINGLE] = SYSCALL_ENTRY3(SyscallWaitSingle), [SYS_WAIT_MANY] = SYSCALL_ENTRY1(SyscallWaitMany), [SYS_SET_USER_APC_DISPATCHER] = SYSCALL_ENTRY1(SyscallSetUserApcDispatcher), [SYS_QUEUE_USER_APC] = SYSCALL_ENTRY1(SyscallQueueUserApc), [SYS_APC_RETURN] = SYSCALL_ENTRY1(SyscallApcReturn), [SYS_SET_EXCEPTION_DISPATCHER] = SYSCALL_ENTRY1(SyscallSetExceptionDispatcher), [SYS_GET_EXCEPTION_DISPATCHER] = SYSCALL_ENTRY1(SyscallGetExceptionDispatcher), [SYS_CONTINUE_EXCEPTION] = SYSCALL_ENTRY1(SyscallContinueException), [SYS_RAISE_EXCEPTION] = SYSCALL_ENTRY1(SyscallRaiseException), // Virtual memory, mappings, and handles. [SYS_ALLOC_VM] = SYSCALL_ENTRY1(SyscallVmAlloc), [SYS_FREE_VM] = SYSCALL_ENTRY1(SyscallVmFree), [SYS_PROTECT_VM] = SYSCALL_ENTRY4(SyscallVmProtect), [SYS_QUERY_VM] = SYSCALL_ENTRY2(SyscallVmQuery), [SYS_CLOSE_HANDLE] = SYSCALL_ENTRY1(SyscallCloseHandle), [SYS_DUPLICATE_HANDLE] = SYSCALL_ENTRY1(SyscallDuplicateHandle), [SYS_DERIVE_HANDLE] = SYSCALL_ENTRY1(SyscallDeriveHandle), [SYS_REVOKE_HANDLE] = SYSCALL_ENTRY1(SyscallRevokeHandle), [SYS_DERIVE_SECURITY_CONTEXT] = SYSCALL_ENTRY1(SyscallDeriveSecurityContext), [SYS_QUERY_SECURITY_CONTEXT] = SYSCALL_ENTRY2(SyscallQuerySecurityContext), [SYS_OPEN_PROCESS_SECURITY_CONTEXT] = SYSCALL_ENTRY3(SyscallOpenProcessSecurityContext), [SYS_CREATE_FILE_MAPPING] = SYSCALL_ENTRY1(SyscallCreateFileMapping), [SYS_MAP_FILE] = SYSCALL_ENTRY1(SyscallMapFile), // Synchronization objects. [SYS_CREATE_MUTEX] = SYSCALL_ENTRY1(SyscallMutexCreate), [SYS_TRY_ACQUIRE_MUTEX] = SYSCALL_ENTRY1(SyscallMutexTryAcquire), [SYS_RELEASE_MUTEX] = SYSCALL_ENTRY1(SyscallMutexRelease), [SYS_CREATE_SEM] = SYSCALL_ENTRY3(SyscallSemCreate), [SYS_TRY_WAIT_SEM] = SYSCALL_ENTRY1(SyscallSemTryWait), [SYS_SIGNAL_SEM] = SYSCALL_ENTRY1(SyscallSemSignal), [SYS_CREATE_EVENT] = SYSCALL_ENTRY3(SyscallEventCreate), [SYS_SIGNAL_EVENT] = SYSCALL_ENTRY1(SyscallEventSignal), [SYS_CLEAR_EVENT] = SYSCALL_ENTRY1(SyscallEventClear), // Queues and named queues. [SYS_CREATE_QUEUE] = SYSCALL_ENTRY1(SyscallQueueCreate), [SYS_SEND_QUEUE] = SYSCALL_ENTRY1(SyscallQueueSend), [SYS_RECV_QUEUE] = SYSCALL_ENTRY1(SyscallQueueRecv), // LPC-like Ports and well-known system services. [SYS_CREATE_PORT] = SYSCALL_ENTRY1(SyscallCreatePort), [SYS_CONNECT_PORT] = SYSCALL_ENTRY1(SyscallConnectPort), [SYS_ACCEPT_PORT] = SYSCALL_ENTRY1(SyscallAcceptPort), [SYS_SEND_PORT] = SYSCALL_ENTRY1(SyscallSendPort), [SYS_RECEIVE_PORT] = SYSCALL_ENTRY1(SyscallReceivePort), [SYS_CALL_PORT] = SYSCALL_ENTRY1(SyscallCallPort), [SYS_REPLY_PORT] = SYSCALL_ENTRY1(SyscallReplyPort), [SYS_DISCONNECT_PORT] = SYSCALL_ENTRY1(SyscallDisconnectPort), [SYS_OPEN_PORT_PEER_PROCESS] = SYSCALL_ENTRY3(SyscallOpenPortPeerProcess), [SYS_REGISTER_SYSTEM_SERVICE] = SYSCALL_ENTRY2(SyscallRegisterSystemService), [SYS_UNREGISTER_SYSTEM_SERVICE] = SYSCALL_ENTRY2(SyscallUnregisterSystemService), [SYS_CONNECT_SYSTEM_SERVICE] = SYSCALL_ENTRY1(SyscallConnectSystemService), // Mounted-volume manager control plane. [SYS_QUERY_VOLUMES] = SYSCALL_ENTRY1(SyscallQueryVolumes), [SYS_OPEN_VOLUME] = SYSCALL_ENTRY4(SyscallOpenVolume), [SYS_WAIT_VOLUME_CHANGE] = SYSCALL_ENTRY3(SyscallWaitVolumeChange), // Capability-relative file and directory operations. [SYS_OPEN_FILE_AT] = SYSCALL_ENTRY1(SyscallOpenFileAt), [SYS_OPEN_DIR_AT] = SYSCALL_ENTRY1(SyscallOpenDirAt), [SYS_STAT_AT] = SYSCALL_ENTRY1(SyscallStatAt), [SYS_DELETE_AT] = SYSCALL_ENTRY1(SyscallDeleteAt), [SYS_RENAME_AT] = SYSCALL_ENTRY1(SyscallRenameAt), [SYS_CREATE_DIR_AT] = SYSCALL_ENTRY1(SyscallCreateDirAt), [SYS_REMOVE_DIR_AT] = SYSCALL_ENTRY1(SyscallRemoveDirAt), [SYS_READ_FILE] = SYSCALL_ENTRY1(SyscallReadFile), [SYS_WRITE_FILE] = SYSCALL_ENTRY1(SyscallWriteFile), [SYS_READ_FILE_ASYNC] = SYSCALL_ENTRY1(SyscallReadFileAsync), [SYS_WRITE_FILE_ASYNC] = SYSCALL_ENTRY1(SyscallWriteFileAsync), [SYS_CANCEL_IO] = SYSCALL_ENTRY1(SyscallCancelIo), [SYS_SEEK_FILE] = SYSCALL_ENTRY2(SyscallSeekFile), [SYS_GET_FILE_POS] = SYSCALL_ENTRY2(SyscallGetFilePos), [SYS_GET_FILE_SIZE] = SYSCALL_ENTRY2(SyscallGetFileSize), [SYS_GET_FILE_STAT] = SYSCALL_ENTRY2(SyscallGetFileStat), [SYS_TRUNCATE_FILE] = SYSCALL_ENTRY2(SyscallTruncateFile), [SYS_FLUSH_FILE] = SYSCALL_ENTRY1(SyscallFlushFile), [SYS_LOCK_FILE] = SYSCALL_ENTRY1(SyscallLockFile), [SYS_UNLOCK_FILE] = SYSCALL_ENTRY1(SyscallUnlockFile), [SYS_QUERY_FILE_LOCK] = SYSCALL_ENTRY1(SyscallQueryFileLock), [SYS_SET_FILE_STATUS] = SYSCALL_ENTRY1(SyscallSetFileStatus), [SYS_CREATE_PIPE] = SYSCALL_ENTRY1(SyscallCreatePipe), [SYS_NOTIFY_PIPE] = SYSCALL_ENTRY1(SyscallNotifyPipe), [SYS_READ_DIR] = SYSCALL_ENTRY2(SyscallReadDir), // Console, display, and surfaces. [SYS_WRITE_CONSOLE] = SYSCALL_ENTRY1(SyscallWriteConsole), [SYS_GET_CONSOLE_INFO] = SYSCALL_ENTRY1(SyscallGetConsoleInfo), [SYS_SET_CONSOLE_CURSOR] = SYSCALL_ENTRY2(SyscallSetConsoleCursor), [SYS_FILL_CONSOLE_CHARACTER] = SYSCALL_ENTRY1(SyscallFillConsoleCharacter), [SYS_FILL_CONSOLE_ATTRIBUTE] = SYSCALL_ENTRY1(SyscallFillConsoleAttribute), [SYS_PUT_SCREEN_CHAR] = SYSCALL_ENTRY1(SyscallPutScreenChar), [SYS_CLEAR_SCREEN] = SYSCALL_ENTRY1(SyscallClearScreen), [SYS_GET_SCREEN_SIZE] = SYSCALL_ENTRY2(SyscallGetScreenSize), [SYS_QUERY_DISPLAY_INFO] = SYSCALL_ENTRY1(SyscallQueryDisplayInfo), [SYS_QUERY_DISPLAY_MANAGER_INFO] = SYSCALL_ENTRY2(SyscallQueryDisplayManagerInfo), [SYS_PRESENT_DISPLAY_MANAGER_SURFACE] = SYSCALL_ENTRY4(SyscallPresentDisplayManagerSurface), [SYS_SET_DISPLAY_MANAGER_CURSOR] = SYSCALL_ENTRY1(SyscallSetDisplayManagerCursor), [SYS_WRITE_DISPLAY_MANAGER_CONSOLE] = SYSCALL_ENTRY2(SyscallWriteDisplayManagerConsole), [SYS_QUERY_DISPLAY_MANAGER_CONSOLE_INFO] = SYSCALL_ENTRY2(SyscallQueryDisplayManagerConsoleInfo), [SYS_SET_DISPLAY_MANAGER_CONSOLE_CURSOR] = SYSCALL_ENTRY3(SyscallSetDisplayManagerConsoleCursor), [SYS_FILL_DISPLAY_MANAGER_CONSOLE_CHARACTER] = SYSCALL_ENTRY2(SyscallFillDisplayManagerConsoleCharacter), [SYS_FILL_DISPLAY_MANAGER_CONSOLE_ATTRIBUTE] = SYSCALL_ENTRY2(SyscallFillDisplayManagerConsoleAttribute), [SYS_PUT_DISPLAY_MANAGER_CONSOLE_CHARACTER] = SYSCALL_ENTRY2(SyscallPutDisplayManagerConsoleCharacter), [SYS_CLEAR_DISPLAY_MANAGER_CONSOLE] = SYSCALL_ENTRY2(SyscallClearDisplayManagerConsole), [SYS_OPEN_DISPLAY_CLOCK] = SYSCALL_ENTRY1(SyscallOpenDisplayClock), [SYS_CREATE_SURFACE] = SYSCALL_ENTRY1(SyscallCreateSurface), [SYS_MAP_SURFACE] = SYSCALL_ENTRY4(SyscallMapSurface), [SYS_UNMAP_SURFACE] = SYSCALL_ENTRY1(SyscallUnmapSurface), [SYS_QUERY_SURFACE] = SYSCALL_ENTRY2(SyscallQuerySurface), [SYS_COMMIT_SURFACE] = SYSCALL_ENTRY4(SyscallCommitSurface), // Native render audio. [SYS_AUDIO_ENUMERATE_ENDPOINTS] = SYSCALL_ENTRY1(SyscallAudioEnumerateEndpoints), [SYS_AUDIO_OPEN_STREAM] = SYSCALL_ENTRY1(SyscallAudioOpenStream), [SYS_AUDIO_WRITE] = SYSCALL_ENTRY1(SyscallAudioWrite), [SYS_AUDIO_ARM_WAIT] = SYSCALL_ENTRY1(SyscallAudioArmWait), [SYS_AUDIO_QUERY_STREAM] = SYSCALL_ENTRY2(SyscallAudioQueryStream), [SYS_AUDIO_START] = SYSCALL_ENTRY1(SyscallAudioStart), [SYS_AUDIO_PAUSE] = SYSCALL_ENTRY1(SyscallAudioPause), [SYS_AUDIO_RESUME] = SYSCALL_ENTRY1(SyscallAudioResume), [SYS_AUDIO_DRAIN] = SYSCALL_ENTRY1(SyscallAudioDrain), [SYS_AUDIO_STOP] = SYSCALL_ENTRY1(SyscallAudioStop), [SYS_AUDIO_RESET] = SYSCALL_ENTRY1(SyscallAudioReset), [SYS_AUDIO_SET_VOLUME] = SYSCALL_ENTRY1(SyscallAudioSetVolume), [SYS_AUDIO_SET_MASTER_VOLUME] = SYSCALL_ENTRY1(SyscallAudioSetMasterVolume), // Input. [SYS_READ_INPUT_MANAGER_EVENT] = SYSCALL_ENTRY1(SyscallReadInputManagerEvent), // Time, debug, and system information. [SYS_CREATE_TIMER] = SYSCALL_ENTRY1(SyscallCreateTimer), [SYS_SET_TIMER] = SYSCALL_ENTRY1(SyscallSetTimer), [SYS_CANCEL_TIMER] = SYSCALL_ENTRY1(SyscallCancelTimer), [SYS_GET_TICK] = SYSCALL_ENTRY0(SyscallGetTick), [SYS_GET_TICK_FREQUENCY] = SYSCALL_ENTRY1(SyscallGetTickFrequency), [SYS_GET_SYSTEM_TIME] = SYSCALL_ENTRY1(SyscallGetSystemTime), [SYS_PRINT_DEBUG] = SYSCALL_ENTRY1(SyscallDebugPrint), [SYS_QUERY_TRAP_FRAME] = SYSCALL_ENTRY1(SyscallQueryTrapFrame), [SYS_GET_MEMORY_STATUS] = SYSCALL_ENTRY1(SyscallGetMemoryStatus), [SYS_QUERY_KERNEL_STATS] = SYSCALL_ENTRY4(SyscallQueryKernelStats), // Native sockets and polling. [SYS_CREATE_SOCKET] = SYSCALL_ENTRY4(SyscallSocket), [SYS_SOCKET_IO_ASYNC] = SYSCALL_ENTRY1(SyscallSocketIoAsync), [SYS_BIND_SOCKET] = SYSCALL_ENTRY3(SyscallBind), [SYS_LISTEN_SOCKET] = SYSCALL_ENTRY2(SyscallListen), [SYS_ACCEPT_SOCKET] = SYSCALL_ENTRY4(SyscallAccept), [SYS_CONNECT_SOCKET] = SYSCALL_ENTRY3(SyscallConnect), [SYS_SEND_SOCKET] = SYSCALL_ENTRY1(SyscallSend), [SYS_RECV_SOCKET] = SYSCALL_ENTRY1(SyscallRecv), [SYS_SHUTDOWN_SOCKET] = SYSCALL_ENTRY2(SyscallShutdown), [SYS_SEND_TO_SOCKET] = SYSCALL_ENTRY2(SyscallSendTo), [SYS_RECV_FROM_SOCKET] = SYSCALL_ENTRY2(SyscallRecvFrom), [SYS_RESOLVE_HOST] = SYSCALL_ENTRY1(SyscallResolveHost), [SYS_SET_SOCKET_OPTION] = SYSCALL_ENTRY2(SyscallSetSocketOption), [SYS_GET_SOCKET_OPTION] = SYSCALL_ENTRY2(SyscallGetSocketOption), [SYS_POLL] = SYSCALL_ENTRY1(SyscallPoll), }; // Invokes one syscall handler through the MS x64 register ABI shape selected by its table entry. static uint64_t KiInvokeSyscall(const SYSCALL_ENTRY* entry, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) { switch (entry->ArgumentCount) { case 0: return ((SYSCALL_HANDLER0)entry->Handler)(); case 1: return ((SYSCALL_HANDLER1)entry->Handler)(a0); case 2: return ((SYSCALL_HANDLER2)entry->Handler)(a0, a1); case 3: return ((SYSCALL_HANDLER3)entry->Handler)(a0, a1, a2); case 4: return ((SYSCALL_HANDLER4)entry->Handler)(a0, a1, a2, a3); default: return NTLSTATUS_NOT_SUPPORTED; } } // Dispatches one user syscall after the assembly entry has restored its four ABI arguments. uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) { KiValidateSyscallTrapFrame(nr, a0, a1, a2, a3); THREAD* self = KeGetCurrentThread(); PROCESS* owner = self ? self->Owner : (PROCESS*)0; if (owner && KeIsProcessTerminating(owner) && !KeThreadTerminationDeferred(self) && nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) { int32_t exitCode = owner->ExitCode; KeExitThread(self, exitCode); while (true) ; } if (nr >= SYS_MAX || !gSyscallTable[nr].Handler) return NTLSTATUS_NOT_SUPPORTED; uint64_t result = KiInvokeSyscall(&gSyscallTable[nr], a0, a1, a2, a3); // A process may be terminated while one of its threads is already inside a // syscall. Do not allow that thread to return to userspace after the owner // has been marked terminating. owner = self ? self->Owner : (PROCESS*)0; if (owner && KeIsProcessTerminating(owner) && !KeThreadTerminationDeferred(self) && nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) { int32_t exitCode = owner->ExitCode; KeExitThread(self, exitCode); while (true) ; } return result; }