// 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: // nr = original RAX (syscall number) // a0 .. a5 = RCX, RDX, R8, R9, R10, R11 (user arguments) // Every handler receives the same (a0..a5) signature even if it uses fewer // arguments; unused parameters are silently ignored. // 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 #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 // HAL pointer - set once at boot by KeSyscallSetHal(). static const HAL_API* gHal; typedef struct SYS_IO_STATUS_BLOCK_K { volatile uint32_t 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 FileHandle; uint32_t EventHandle; uint32_t Flags; uint64_t Offset; void* Buffer; size_t Size; SYS_IO_STATUS_BLOCK_K* Iosb; SYS_USER_APC_ROUTINE_K ApcRoutine; void* ApcContext; uint32_t* RequestHandleOut; // optional cancellation/query handle output } SYS_ASYNC_FILE_REQUEST_K; 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; } } typedef struct MODULE_HANDLE_OBJECT { OBJECT Header; PROCESS* Owner; uintptr_t Base; } MODULE_HANDLE_OBJECT; // Releasing the last module handle releases the public load reference it owns. static void KiModuleHandleDestroy(OBJECT* obj) { MODULE_HANDLE_OBJECT* module = CONTAINING_RECORD(obj, MODULE_HANDLE_OBJECT, Header); if (module->Owner && module->Base) { (void)LdrUnloadModuleForProcess(module->Owner, module->Base); module->Base = 0; } if (module->Owner) { ObDereferenceObject(&module->Owner->Header); module->Owner = (PROCESS*)0; } } // Creates a user-visible module handle for one public load reference. static KSTATUS KiCreateModuleHandle(PROCESS* owner, uintptr_t base, uint32_t* handleOut) { if (!owner || !base || !handleOut) return STATUS_INVALID_PARAMETER; MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)ObAllocateObject(ObjectTypeModule, sizeof(MODULE_HANDLE_OBJECT)); if (!module) return STATUS_NO_MEMORY; module->Header.Destroy = KiModuleHandleDestroy; module->Owner = owner; module->Base = base; ObReferenceObject(&owner->Header); KSTATUS s = KeAllocateHandleWithRights(owner, &module->Header, HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE | HANDLE_RIGHT_DUPLICATE, handleOut); ObDereferenceObject(&module->Header); return s; } typedef struct SURFACE_OBJECT { OBJECT Header; PROCESS* Owner; uint32_t Width; uint32_t Height; uint32_t PitchBytes; uint32_t Format; uint32_t Flags; size_t PayloadSize; size_t PageCount; uint8_t StatsRegistered; uint64_t MapCount; uint64_t PresentCount; uint64_t PresentRectCount; uint64_t PresentClippedRectCount; uint64_t PresentEmptyClipCount; uint64_t PresentRowsCopied; uint64_t PresentBytesCopied; PHYSICAL_ADDRESS* Frames; void* UserBase; uint32_t* RowScratch; MUTEX* Lock; } SURFACE_OBJECT; static SPINLOCK gSurfaceStatsLock = SPINLOCK_INIT; static SYS_SURFACE_STATS gSurfaceStats; // Registers a surface in the global surface telemetry snapshot. static void KiSurfaceStatsRegister(SURFACE_OBJECT* surface) { if (!surface || surface->StatsRegistered) return; uintptr_t flags = KeAcquireSpinlock(&gSurfaceStatsLock); gSurfaceStats.CreatedSurfaces++; gSurfaceStats.LiveSurfaces++; gSurfaceStats.LivePages += surface->PageCount; gSurfaceStats.LiveBytes += surface->PayloadSize; surface->StatsRegistered = 1; KeReleaseSpinlock(&gSurfaceStatsLock, flags); } // Removes a surface from the global surface telemetry snapshot. static void KiSurfaceStatsUnregister(SURFACE_OBJECT* surface) { if (!surface || !surface->StatsRegistered) return; uintptr_t flags = KeAcquireSpinlock(&gSurfaceStatsLock); gSurfaceStats.DestroyedSurfaces++; if (gSurfaceStats.LiveSurfaces) gSurfaceStats.LiveSurfaces--; if (gSurfaceStats.LivePages >= surface->PageCount) gSurfaceStats.LivePages -= surface->PageCount; else gSurfaceStats.LivePages = 0; if (gSurfaceStats.LiveBytes >= surface->PayloadSize) gSurfaceStats.LiveBytes -= surface->PayloadSize; else gSurfaceStats.LiveBytes = 0; surface->StatsRegistered = 0; KeReleaseSpinlock(&gSurfaceStatsLock, flags); } // Records a successful surface map operation. static void KiSurfaceStatsRecordMap(SURFACE_OBJECT* surface) { if (!surface) return; uintptr_t flags = KeAcquireSpinlock(&gSurfaceStatsLock); surface->MapCount++; gSurfaceStats.MapCalls++; KeReleaseSpinlock(&gSurfaceStatsLock, flags); } // Records a successful surface present operation. static void KiSurfaceStatsRecordPresent(SURFACE_OBJECT* surface, uint64_t inputRects, uint64_t clippedRects, uint64_t emptyClips, uint64_t rowsCopied, uint64_t bytesCopied) { if (!surface) return; uintptr_t flags = KeAcquireSpinlock(&gSurfaceStatsLock); surface->PresentCount++; surface->PresentRectCount += inputRects; surface->PresentClippedRectCount += clippedRects; surface->PresentEmptyClipCount += emptyClips; surface->PresentRowsCopied += rowsCopied; surface->PresentBytesCopied += bytesCopied; gSurfaceStats.PresentCalls++; gSurfaceStats.PresentRectInputs += inputRects; gSurfaceStats.PresentClippedRects += clippedRects; gSurfaceStats.PresentEmptyClips += emptyClips; gSurfaceStats.PresentRowsCopied += rowsCopied; gSurfaceStats.PresentBytesCopied += bytesCopied; KeReleaseSpinlock(&gSurfaceStatsLock, flags); } // Copies the global surface telemetry snapshot. static void KiSurfaceStatsQuery(SYS_SURFACE_STATS* out) { if (!out) return; uintptr_t flags = KeAcquireSpinlock(&gSurfaceStatsLock); *out = gSurfaceStats; out->Size = sizeof(*out); out->Version = SYS_STATS_VERSION; KeReleaseSpinlock(&gSurfaceStatsLock, flags); } static void KiSurfaceDestroy(OBJECT* obj) { SURFACE_OBJECT* surface = CONTAINING_RECORD(obj, SURFACE_OBJECT, Header); KiSurfaceStatsUnregister(surface); if (surface->Owner && surface->UserBase) { (void)MmVadDestroy(surface->Owner, surface->UserBase); surface->UserBase = (void*)0; } if (surface->Lock) { ObDereferenceObject(&surface->Lock->Header); surface->Lock = (MUTEX*)0; } if (surface->Frames) { for (size_t i = 0; i < surface->PageCount; ++i) { if (surface->Frames[i]) (void)MmPfnRelease(surface->Frames[i]); } MmFreePool(surface->Frames); surface->Frames = (PHYSICAL_ADDRESS*)0; } if (surface->RowScratch) { MmFreePool(surface->RowScratch); surface->RowScratch = (uint32_t*)0; } } typedef struct SURFACE_ROW_CONTEXT { SURFACE_OBJECT* Surface; uint32_t SourceX; uint32_t SourceY; } SURFACE_ROW_CONTEXT; static KSTATUS KiSurfaceCopyRow(void* context, int rowIndex, uint32_t* scratch, size_t pixelCount) { SURFACE_ROW_CONTEXT* ctx = (SURFACE_ROW_CONTEXT*)context; if (!ctx || !ctx->Surface || !scratch || rowIndex < 0) return STATUS_INVALID_PARAMETER; SURFACE_OBJECT* surface = ctx->Surface; uint64_t row = (uint64_t)ctx->SourceY + (uint64_t)(uint32_t)rowIndex; uint64_t offset = row * (uint64_t)surface->PitchBytes + (uint64_t)ctx->SourceX * sizeof(uint32_t); uint64_t bytes = (uint64_t)pixelCount * sizeof(uint32_t); if (offset > (uint64_t)surface->PayloadSize || bytes > (uint64_t)surface->PayloadSize - offset) return STATUS_INVALID_PARAMETER; uint8_t* dst = (uint8_t*)scratch; while (bytes) { size_t frameIndex = (size_t)(offset / PAGE_SIZE); size_t pageOffset = (size_t)(offset & (PAGE_SIZE - 1)); if (frameIndex >= surface->PageCount || !surface->Frames[frameIndex]) return STATUS_INVALID_PARAMETER; size_t chunk = PAGE_SIZE - pageOffset; if (chunk > bytes) chunk = (size_t)bytes; const uint8_t* src = (const uint8_t*)MmPfnToVirtual(surface->Frames[frameIndex]); if (!src) return STATUS_INVALID_PARAMETER; memcpy(dst, src + pageOffset, chunk); dst += chunk; offset += chunk; bytes -= chunk; } return STATUS_SUCCESS; } // Implements the syscall Init operation. void KeSyscallInit(const HAL_API* hal) { gHal = hal; } // Helper: look up a handle and type-check it in one call. // Bumps the refcount on success; caller must ObDereferenceObject when done. // Returns NULL if the handle is invalid or the type does not match. static OBJECT* KiHandleToObject(uint32_t handle, OBJECT_TYPE expected, uint32_t requiredRights) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) return NULL; OBJECT* obj = KeLookupHandleWithRights(proc, handle, requiredRights); if (!obj) return NULL; if (obj->Type != expected) { ObDereferenceObject(obj); return NULL; } return obj; } // 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, uint32_t rights, uint32_t* handleOut) { PROCESS* proc = KeGetCurrentProcess(); if (!proc) { ObDereferenceObject(obj); return STATUS_FAILURE; } KSTATUS s = KeAllocateHandleWithRights(proc, obj, rights, handleOut); // Most syscall create/open paths receive a fresh creator reference from the // subsystem. The handle table 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; } // Creates handle For Current. static KSTATUS KiAllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) { return KiAllocHandleForCurrentWithRights(obj, HANDLE_RIGHT_ALL, handleOut); } #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 user Request. static KSTATUS KiCopyUserRequest(uint64_t arg, void* dst, size_t size) { if (!arg || !dst || size == 0) return STATUS_INVALID_PARAMETER; return CopyFromUser(dst, (const void*)(uintptr_t)arg, size); } static KSTATUS KiImportUserStringView(SYS_STRING_VIEW userView, char* dst, size_t dstSize, SYS_STRING_VIEW* out, bool nulTerminate) { if (!out) return STATUS_INVALID_PARAMETER; *out = SysSvFromParts((const char*)0, 0); if (userView.Length == 0) { if (nulTerminate) { if (!dst || dstSize == 0) return STATUS_BUFFER_TOO_SMALL; dst[0] = '\0'; } return STATUS_SUCCESS; } if (!userView.Data || !dst) return STATUS_INVALID_PARAMETER; if (userView.Length > SYSCALL_STRING_MAX) return STATUS_BUFFER_TOO_SMALL; size_t needed = userView.Length + (nulTerminate ? 1 : 0); if (needed < userView.Length || needed > dstSize) return STATUS_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 STATUS_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 && SysSvContainsByte(*out, '\0')) return STATUS_INVALID_PARAMETER; return STATUS_SUCCESS; } // Copies handle To User. static KSTATUS KiCopyHandleToUser(uint64_t arg, uint32_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } // Copies u32 To User. static KSTATUS KiCopyU32ToUser(uint64_t arg, uint32_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } // Copies size To User. static KSTATUS KiCopySizeToUser(uint64_t arg, size_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } // Copies u64 To User. static KSTATUS KiCopyU64ToUser(uint64_t arg, uint64_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } // Copies int To User. static KSTATUS KiCopyIntToUser(uint64_t arg, int value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } // Copies one signed 32-bit integer to user memory. static KSTATUS KiCopyInt32ToUser(uint64_t arg, int32_t value) { if (!arg) return STATUS_INVALID_PARAMETER; return CopyToUser((void*)(uintptr_t)arg, &value, sizeof(value)); } // Implements the mul Size operation. static int KiMulSize(size_t a, size_t b, size_t* out) { if (!out) return 0; if (a && b > ((size_t)-1) / a) return 0; *out = a * b; return 1; } 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. // SYS_EXIT_PROCESS (0) a0 = exitCode. static uint64_t SyscallExitProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; THREAD* t = KeGetCurrentThread(); PROCESS* proc = t ? t->Owner : NULL; int32_t exitCode = (int32_t)a0; if (!proc || !t) return STATUS_FAILURE; KSTATUS s = KeExitProcess(proc, exitCode); if (!KSUCCESS(s)) return s; KeExitThread(t, exitCode); while (true) ; // unreachable - KeExitThread does not return } // SYS_EXIT_THREAD (1) a0 = exitCode. static uint64_t SyscallExitThread(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; THREAD* t = KeGetCurrentThread(); if (t) KeExitThread(t, (int)a0); while (true); } // SYS_CREATE_THREAD a0=*SYS_CREATE_THREAD_REQUEST. // Creates a runnable user thread and optionally returns both its waitable // handle and diagnostic TID. The handle is the synchronization identity. static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_CREATE_THREAD_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; if (!req.Entry) return STATUS_INVALID_PARAMETER; if (req.Priority >= PriorityCount) return STATUS_INVALID_PARAMETER; if (req.Flags) return STATUS_INVALID_PARAMETER; if (!req.ThreadHandleOut && !req.ThreadIdOut) return STATUS_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; s = KeCreateThread(proc, req.Entry, req.Argument, (THREAD_PRIORITY)req.Priority, &newt); if (!KSUCCESS(s)) return s; uint32_t threadHandle = 0; if (req.ThreadHandleOut) { s = KeAllocateHandleWithRights(proc, &newt->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY, &threadHandle); if (!KSUCCESS(s)) { ObDereferenceObject(&newt->Header); return s; } } if (req.ThreadHandleOut) { s = KiCopyU32ToUser((uint64_t)(uintptr_t)req.ThreadHandleOut, threadHandle); if (!KSUCCESS(s)) { KeCloseHandle(proc, threadHandle); ObDereferenceObject(&newt->Header); return s; } } if (req.ThreadIdOut) { s = KiCopyU32ToUser((uint64_t)(uintptr_t)req.ThreadIdOut, newt->ThreadId); if (!KSUCCESS(s)) { if (threadHandle) KeCloseHandle(proc, threadHandle); ObDereferenceObject(&newt->Header); return s; } } ObDereferenceObject(&newt->Header); return STATUS_SUCCESS; } 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, uint32_t handle, EVENT** eventOut, OBJECT** objectOut) { if (!proc || !eventOut || !objectOut) return STATUS_INVALID_PARAMETER; *eventOut = (EVENT*)0; *objectOut = (OBJECT*)0; OBJECT* obj = KeLookupHandleWithRights(proc, handle, HANDLE_RIGHT_WAIT); if (!obj) return STATUS_INVALID_HANDLE; 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 ObjectTypeInputQueue: event = KeInputQueueWaitEvent(CONTAINING_RECORD(obj, INPUT_QUEUE, Header)); break; case ObjectTypeTimer: event = KeTimerWaitEvent(CONTAINING_RECORD(obj, KTIMER, Header)); break; default: ObDereferenceObject(obj); return STATUS_NOT_SUPPORTED; } if (!event) { ObDereferenceObject(obj); return STATUS_OBJECT_DEAD; } *eventOut = event; *objectOut = obj; return STATUS_SUCCESS; } // SYS_WAIT_SINGLE a0=handle a1=SYS_WAIT_FLAG_* a2=timeoutMs. // 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(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; uint32_t flags = (uint32_t)a1; uint64_t timeoutMs = a2; if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; OBJECT* obj = KeLookupHandleWithRights(proc, (uint32_t)a0, HANDLE_RIGHT_WAIT); if (!obj) return STATUS_INVALID_HANDLE; uint8_t alertable = (flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0; KSTATUS s = STATUS_NOT_SUPPORTED; if (obj->Type == ObjectTypeMutex) { if (alertable) s = STATUS_NOT_SUPPORTED; else if (timeoutMs == 0) { s = KeTryAcquireMutex(CONTAINING_RECORD(obj, MUTEX, Header)); if (s == STATUS_FAILURE) s = STATUS_TIMEOUT; } else if (timeoutMs == SYS_WAIT_TIMEOUT_INFINITE) { s = KeAcquireMutex(CONTAINING_RECORD(obj, MUTEX, Header)); } else { s = STATUS_NOT_SUPPORTED; } ObDereferenceObject(obj); return s; } if (obj->Type == ObjectTypeSemaphore) { if (alertable) s = STATUS_NOT_SUPPORTED; else if (timeoutMs == 0) { s = KeTrySemaphore(CONTAINING_RECORD(obj, SEMAPHORE, Header)); if (s == STATUS_FAILURE) s = STATUS_TIMEOUT; } else if (timeoutMs == SYS_WAIT_TIMEOUT_INFINITE) { s = KeWaitSemaphore(CONTAINING_RECORD(obj, SEMAPHORE, Header)); } else { s = STATUS_NOT_SUPPORTED; } ObDereferenceObject(obj); return s; } ObDereferenceObject(obj); EVENT* event = (EVENT*)0; OBJECT* ref = (OBJECT*)0; s = KiHandleToWaitEvent(proc, (uint32_t)a0, &event, &ref); if (!KSUCCESS(s)) return s; uint32_t selected = 0; s = KeWaitForMultipleEvents(&event, 1, alertable, timeoutMs, &selected); ObDereferenceObject(ref); if (s != STATUS_TIMEOUT && s != STATUS_NOT_SUPPORTED) KiFinishAlertableUserWait(alertable); return s; } // SYS_WAIT_MANY a0=*SYS_WAIT_MANY_REQUEST. // Waits until any passive waitable handle becomes signaled. static uint64_t SyscallWaitMany(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_WAIT_MANY_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER; if (req.Flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER; if (!req.Handles || req.Count == 0 || req.Count > SYS_WAIT_MANY_MAX_HANDLES) return STATUS_INVALID_PARAMETER; uint32_t 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 STATUS_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; } } 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++) ObDereferenceObject(refs[i]); if (KSUCCESS(s) && req.SignaledIndexOut) { KSTATUS cs = KiCopyU32ToUser((uint64_t)(uintptr_t)req.SignaledIndexOut, selected); if (!KSUCCESS(cs)) return cs; } if (s != STATUS_TIMEOUT && s != STATUS_NOT_SUPPORTED) KiFinishAlertableUserWait(alertable); return s; } // SYS_OPEN_INPUT_QUEUE a0=flags a1=*handleOut. static uint64_t SyscallOpenInputQueue(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (a0 != 0) return STATUS_INVALID_PARAMETER; if (!a1) return STATUS_INVALID_PARAMETER; INPUT_QUEUE* queue = (INPUT_QUEUE*)0; KSTATUS s = KeInputOpenQueue(&queue); if (!KSUCCESS(s)) return s; uint32_t handle = 0; s = KiAllocHandleForCurrentWithRights(&queue->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY, &handle); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a1, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); } return s; } // SYS_GET_CURRENT_TID (3). static uint64_t SyscallGetCurrentTid(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; THREAD* t = KeGetCurrentThread(); return t ? t->ThreadId : 0; } // SYS_GET_CURRENT_PID (4). static uint64_t SyscallGetCurrentPid(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* p = KeGetCurrentProcess(); return p ? p->ProcessId : 0; } // SYS_YIELD (5). static uint64_t SyscallYield(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; KeYield(); return STATUS_SUCCESS; } // SYS_CREATE_TIMER a0=*handleOut. static uint64_t SyscallCreateTimer(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; KTIMER* timer = (KTIMER*)0; KSTATUS s = KeTimerCreate(&timer); if (!KSUCCESS(s)) return s; uint32_t handle = 0; s = KiAllocHandleForCurrentWithRights(&timer->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &handle); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a0, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); } return s; } // SYS_SET_TIMER a0=*SYS_SET_TIMER_REQUEST. static uint64_t SyscallSetTimer(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_SET_TIMER_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req) || req.Flags != SYS_TIMER_FLAG_NONE) return STATUS_INVALID_PARAMETER; KTIMER* timer = (KTIMER*)KiHandleToObject(req.TimerHandle, ObjectTypeTimer, HANDLE_RIGHT_SIGNAL); if (!timer) return STATUS_INVALID_HANDLE; s = KeTimerSet(timer, req.DueTimeMilliseconds, req.PeriodMilliseconds); ObDereferenceObject(&timer->Header); return s; } // SYS_CANCEL_TIMER a0=timerHandle. static uint64_t SyscallCancelTimer(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; KTIMER* timer = (KTIMER*)KiHandleToObject((uint32_t)a0, ObjectTypeTimer, HANDLE_RIGHT_SIGNAL); if (!timer) return STATUS_INVALID_HANDLE; KSTATUS s = KeTimerCancel(timer); ObDereferenceObject(&timer->Header); return s; } // SYS_SLEEP a0=milliseconds. // 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 a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (a0 == 0) { KeYield(); return STATUS_SUCCESS; } KTIMER* timer = (KTIMER*)0; KSTATUS s = KeTimerCreate(&timer); if (!KSUCCESS(s)) return s; s = KeTimerSet(timer, a0, 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; } // SYS_SET_USER_APC_DISPATCHER a0 = dispatcher entry in syscall.dll/user image. static uint64_t SyscallSetUserApcDispatcher(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; KSTATUS s = MmProbeUserRead((const void*)(uintptr_t)a0, 1); if (!KSUCCESS(s)) return s; return KeSetUserApcDispatcher((void*)(uintptr_t)a0); } // SYS_APC_RETURN a0 = SYS_USER_APC_FRAME* on the user stack. static uint64_t SyscallApcReturn(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; return KeUserApcReturn((const SYS_USER_APC_FRAME_K*)(uintptr_t)a0); } // SYS_SET_THREAD_PRIORITY (7) a0 = priority. static uint64_t SyscallSetThreadPriority(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (a0 >= PriorityCount) return STATUS_INVALID_PARAMETER; THREAD* t = KeGetCurrentThread(); if (!t) return STATUS_FAILURE; t->Priority = (THREAD_PRIORITY)a0; return STATUS_SUCCESS; } // SYS_VM_* page-granular user virtual memory. static KSTATUS KiVmProtectToPageFlags(uint32_t protect, uint64_t* flagsOut) { if (!flagsOut) return STATUS_INVALID_PARAMETER; switch (protect) { case SYS_VM_NOACCESS: *flagsOut = 0; return STATUS_SUCCESS; case SYS_VM_READ: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_NX; return STATUS_SUCCESS; case SYS_VM_READWRITE: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_WRITE | PAGE_NX; return STATUS_SUCCESS; case SYS_VM_EXECUTE: case SYS_VM_EXECUTE_READ: *flagsOut = PAGE_PRESENT | PAGE_USER; return STATUS_SUCCESS; case SYS_VM_EXECUTE_READWRITE: *flagsOut = PAGE_PRESENT | PAGE_USER | PAGE_WRITE; return STATUS_SUCCESS; default: return STATUS_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; } static uint64_t SyscallVmAlloc(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; void* preferredBase = (void*)(uintptr_t)a0; size_t pageCount = (size_t)a1; uint32_t protect = (uint32_t)a2; uint32_t flags = (uint32_t)a3; void** baseOut = (void**)(uintptr_t)a4; if (!pageCount || !baseOut) return STATUS_INVALID_PARAMETER; if (pageCount > ((size_t)-1) / PAGE_SIZE) return STATUS_NO_MEMORY; if (preferredBase || (flags & SYS_VM_FLAG_FIXED)) return STATUS_NOT_SUPPORTED; uint64_t pageFlags = 0; KSTATUS s = KiVmProtectToPageFlags(protect, &pageFlags); if (!KSUCCESS(s)) return s; PROCESS* process = KeGetCurrentProcess(); if (!process) return STATUS_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 STATUS_SUCCESS; } static uint64_t SyscallVmFree(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* process = KeGetCurrentProcess(); if (!process || !a0) return STATUS_INVALID_PARAMETER; return MmVadDestroy(process, (void*)(uintptr_t)a0); } static uint64_t SyscallVmProtect(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a0 || !a1) return STATUS_INVALID_PARAMETER; if ((size_t)a1 > ((size_t)-1) / PAGE_SIZE) return STATUS_NO_MEMORY; uint64_t pageFlags = 0; KSTATUS s = KiVmProtectToPageFlags((uint32_t)a2, &pageFlags); if (!KSUCCESS(s)) return s; PROCESS* process = KeGetCurrentProcess(); if (!process) return STATUS_FAILURE; if (a3) { void* regionBase = (void*)0; size_t regionSize = 0; uint64_t oldFlags = 0; MM_VAD_TYPE type = MmVadGeneric; uint32_t regionFlags = 0; s = MmVadQuery(process, (void*)(uintptr_t)a0, ®ionBase, ®ionSize, &oldFlags, &type, ®ionFlags); if (!KSUCCESS(s)) return s; uint32_t oldProtect = KiPageFlagsToVmProtect(oldFlags); s = CopyToUser((void*)(uintptr_t)a3, &oldProtect, sizeof(oldProtect)); if (!KSUCCESS(s)) return s; } return MmVadProtect(process, (void*)(uintptr_t)a0, (size_t)a1 * PAGE_SIZE, pageFlags); } static uint64_t SyscallVmQuery(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a0 || !a1) return STATUS_INVALID_PARAMETER; PROCESS* process = KeGetCurrentProcess(); if (!process) return STATUS_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, (void*)(uintptr_t)a0, &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((void*)(uintptr_t)a1, &info, sizeof(info)); } // SYS_CREATE_MUTEX (11) a0 = *handleOut. static uint64_t SyscallMutexCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; MUTEX* mx = NULL; KSTATUS s = KeMutexCreate(&mx); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&mx->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h); // takes ownership of the ref if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a0, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // SYS_TRY_ACQUIRE_MUTEX (13) a0 = handle. static uint64_t SyscallMutexTryAcquire(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; MUTEX* mx = (MUTEX*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_WAIT); if (!mx) return STATUS_NOT_FOUND; KSTATUS s = KeTryAcquireMutex(mx); ObDereferenceObject(&mx->Header); return s; } // SYS_RELEASE_MUTEX (14) a0 = handle. static uint64_t SyscallMutexRelease(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; MUTEX* mx = (MUTEX*)KiHandleToObject((uint32_t)a0, ObjectTypeMutex, HANDLE_RIGHT_SIGNAL); if (!mx) return STATUS_NOT_FOUND; KeReleaseMutex(mx); ObDereferenceObject(&mx->Header); return STATUS_SUCCESS; } // SYS_CREATE_SEM (15) a0=initial a1=max a2=*handleOut. static uint64_t SyscallSemCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a2) return STATUS_INVALID_PARAMETER; SEMAPHORE* sem = NULL; KSTATUS s = KeSemaphoreCreate((int32_t)a0, (int32_t)a1, &sem); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&sem->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a2, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // SYS_TRY_WAIT_SEM (17) a0 = handle. static uint64_t SyscallSemTryWait(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_WAIT); if (!sem) return STATUS_NOT_FOUND; KSTATUS s = KeTrySemaphore(sem); ObDereferenceObject(&sem->Header); return s; } // SYS_SIGNAL_SEM (18) a0 = handle. static uint64_t SyscallSemSignal(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SEMAPHORE* sem = (SEMAPHORE*)KiHandleToObject((uint32_t)a0, ObjectTypeSemaphore, HANDLE_RIGHT_SIGNAL); if (!sem) return STATUS_NOT_FOUND; KeSignalSemaphore(sem); ObDereferenceObject(&sem->Header); return STATUS_SUCCESS; } // SYS_CREATE_EVENT (19) a0=type a1=initialState a2=*handleOut. static uint64_t SyscallEventCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a2 || a0 > EventManualReset) return STATUS_INVALID_PARAMETER; EVENT* ev = NULL; KSTATUS s = KeEventCreate((EVENT_TYPE)a0, (uint32_t)a1, &ev); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&ev->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_SIGNAL | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a2, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // SYS_SIGNAL_EVENT (20) a0 = handle. static uint64_t SyscallEventSignal(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; EVENT* ev = (EVENT*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL); if (!ev) return STATUS_NOT_FOUND; KeSignalEvent(ev); ObDereferenceObject(&ev->Header); return STATUS_SUCCESS; } // SYS_CLEAR_EVENT (21) a0 = handle. static uint64_t SyscallEventClear(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; EVENT* ev = (EVENT*)KiHandleToObject((uint32_t)a0, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL); if (!ev) return STATUS_NOT_FOUND; KeClearEvent(ev); ObDereferenceObject(&ev->Header); return STATUS_SUCCESS; } // SYS_CREATE_QUEUE (23) a0 = *handleOut. static uint64_t SyscallQueueCreate(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; QUEUE* q = NULL; KSTATUS s = KeQueueCreate(&q); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a0, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // SYS_SEND_QUEUE a0=*SYS_QUEUE_SEND_REQUEST. static uint64_t SyscallQueueSend(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_QUEUE_SEND_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req) || req.Flags != SYS_QUEUE_FLAG_NONE || !req.Message) return STATUS_INVALID_PARAMETER; QUEUE_MESSAGE msg; s = CopyFromUser(&msg, req.Message, sizeof(msg)); if (!KSUCCESS(s)) return s; QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, HANDLE_RIGHT_WRITE); if (!q) return STATUS_NOT_FOUND; s = KeSendMessage(q, &msg, req.TimeoutMilliseconds); ObDereferenceObject(&q->Header); return s; } // SYS_RECV_QUEUE a0=*SYS_QUEUE_RECV_REQUEST. static uint64_t SyscallQueueRecv(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_QUEUE_RECV_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req) || req.Flags != SYS_QUEUE_FLAG_NONE || !req.MessageOut) return STATUS_INVALID_PARAMETER; QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, HANDLE_RIGHT_READ); if (!q) return STATUS_NOT_FOUND; QUEUE_MESSAGE msg; s = KeReceiveMessage(q, &msg, req.TimeoutMilliseconds); ObDereferenceObject(&q->Header); if (!KSUCCESS(s)) return s; return CopyToUser(req.MessageOut, &msg, sizeof(msg)); } // SYS_CLOSE_HANDLE (26) a0 = 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(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; uint32_t fileHandle = (uint32_t)a0; uint32_t protect = (uint32_t)a1; uint32_t flags = (uint32_t)a2; uint64_t maximumSize = a3; uint32_t* handleOut = (uint32_t*)(uintptr_t)a4; if (!fileHandle || !handleOut) return STATUS_INVALID_PARAMETER; uint64_t pageFlags = 0; KSTATUS s = KiVmProtectToPageFlags(protect, &pageFlags); if (!KSUCCESS(s)) return s; if (!pageFlags) return STATUS_ACCESS_DENIED; uint32_t neededRights = HANDLE_RIGHT_READ | HANDLE_RIGHT_QUERY; if (pageFlags & PAGE_WRITE) neededRights |= HANDLE_RIGHT_WRITE; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(fileHandle, ObjectTypeFile, neededRights); if (!file) return STATUS_NOT_FOUND; 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 STATUS_NO_MEMORY; } mapping->Header.Destroy = KiFileMappingDestroy; mapping->File = file; // KiHandleToObject returned the mapping object's file reference. mapping->MaximumSize = maximumSize; mapping->PageFlags = pageFlags; mapping->Protect = protect; mapping->Flags = flags; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&mapping->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser((uint64_t)(uintptr_t)handleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } static KSTATUS KiMapAccessToPageFlags(const FILE_MAPPING_OBJECT* mapping, uint32_t access, uint64_t* pageFlagsOut) { if (!mapping || !pageFlagsOut) return STATUS_INVALID_PARAMETER; uint64_t mappingFlags = mapping->PageFlags; if (!mappingFlags) return STATUS_ACCESS_DENIED; if (access == 0) access = SYS_MAP_ACCESS_READ; if ((access & SYS_MAP_ACCESS_WRITE) && (access & SYS_MAP_ACCESS_COPY)) return STATUS_INVALID_PARAMETER; if (((access & SYS_MAP_ACCESS_COPY) || (access & SYS_MAP_ACCESS_WRITE)) && (mappingFlags & PAGE_WRITE) == 0) return STATUS_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 STATUS_ACCESS_DENIED; } else { flags |= PAGE_NX; } *pageFlagsOut = flags; return STATUS_SUCCESS; } static uint64_t SyscallMapFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { uint32_t mappingHandle = (uint32_t)a0; uint64_t offset = a1; size_t size = (size_t)a2; uint32_t access = (uint32_t)a3; uint32_t flags = (uint32_t)a4; void** baseOut = (void**)(uintptr_t)a5; if (!mappingHandle || !baseOut) return STATUS_INVALID_PARAMETER; if (offset & (PAGE_SIZE - 1)) return STATUS_INVALID_PARAMETER; FILE_MAPPING_OBJECT* mapping = (FILE_MAPPING_OBJECT*)KiHandleToObject(mappingHandle, ObjectTypeFileMapping, HANDLE_RIGHT_READ); if (!mapping) return STATUS_NOT_FOUND; uint64_t maximumSize = mapping->MaximumSize; if (maximumSize == 0) maximumSize = IoGetFileSize(mapping->File); if (offset >= maximumSize) { ObDereferenceObject(&mapping->Header); return STATUS_INVALID_PARAMETER; } if (size == 0) { uint64_t tail = maximumSize - offset; if (tail > (uint64_t)((size_t)-1)) { ObDereferenceObject(&mapping->Header); return STATUS_NO_MEMORY; } size = (size_t)tail; } if (size == 0) { ObDereferenceObject(&mapping->Header); return STATUS_INVALID_PARAMETER; } if ((uint64_t)size > maximumSize - offset) { ObDereferenceObject(&mapping->Header); return STATUS_INVALID_PARAMETER; } uint64_t pageFlags = 0; KSTATUS s = KiMapAccessToPageFlags(mapping, access, &pageFlags); if (!KSUCCESS(s)) { ObDereferenceObject(&mapping->Header); return s; } PROCESS* process = KeGetCurrentProcess(); if (!process) { ObDereferenceObject(&mapping->Header); return STATUS_FAILURE; } uint32_t vadFlags = flags; if (access & SYS_MAP_ACCESS_WRITE) vadFlags |= MM_VAD_SHARED_WRITE; void* base = (void*)0; s = MmVadCreateFileMapping(process, mapping->File, offset, size, pageFlags, vadFlags, &base); ObDereferenceObject(&mapping->Header); if (!KSUCCESS(s)) return s; s = CopyToUser(baseOut, &base, sizeof(base)); if (!KSUCCESS(s)) { MmVadDestroy(process, base); return s; } return STATUS_SUCCESS; } static uint64_t SyscallCloseHandle(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; OBJECT* obj = KeLookupHandleWithRights(proc, (uint32_t)a0, 0); if (obj) { if (obj->Type == ObjectTypeFile) { KSTATUS fs = IoFlushFile((FILE_OBJECT*)obj); ObDereferenceObject(obj); if (!KSUCCESS(fs)) return fs; } else { ObDereferenceObject(obj); } } KeCloseHandle(proc, (uint32_t)a0); return STATUS_SUCCESS; } // Converts public file access flags to handle rights and file object flags. static KSTATUS KiFileAccessToHandleRights(uint32_t access, uint32_t* rightsOut, uint32_t* fileFlagsOut) { if (!rightsOut || !fileFlagsOut) return STATUS_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 STATUS_INVALID_PARAMETER; uint32_t rights = 0; uint32_t fileFlags = 0; if (access & SYS_FILE_ACCESS_READ) rights |= HANDLE_RIGHT_READ; if (access & SYS_FILE_ACCESS_WRITE) rights |= HANDLE_RIGHT_WRITE; if (access & SYS_FILE_ACCESS_APPEND) { rights |= HANDLE_RIGHT_WRITE; fileFlags |= IO_FILE_FLAG_APPEND; } if (access & SYS_FILE_ACCESS_QUERY) rights |= HANDLE_RIGHT_QUERY; if (access & SYS_FILE_ACCESS_LOCK) rights |= HANDLE_RIGHT_LOCK; *rightsOut = rights; *fileFlagsOut = fileFlags; return STATUS_SUCCESS; } // Opens a file using the public disposition contract. static KSTATUS KiOpenFileByDisposition(SYS_STRING_VIEW path, uint32_t disposition, uint32_t access, FILE_OBJECT** fileOut) { if (!fileOut) return STATUS_INVALID_PARAMETER; *fileOut = NULL; bool writes = (access & (SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_APPEND)) != 0; KSTATUS s; switch (disposition) { case SYS_FILE_OPEN_EXISTING: return IoOpenFile(path, fileOut); case SYS_FILE_CREATE_NEW: if (!writes) return STATUS_ACCESS_DENIED; return IoCreateFile(path, fileOut); case SYS_FILE_CREATE_ALWAYS: if (!writes) return STATUS_ACCESS_DENIED; s = IoCreateFile(path, fileOut); if (s == STATUS_OBJECT_NAME_COLLISION) { s = IoOpenFile(path, fileOut); if (KSUCCESS(s)) { KSTATUS ts = IoTruncateFile(*fileOut, 0); if (!KSUCCESS(ts)) { IoCloseFile(*fileOut); *fileOut = NULL; return ts; } } } return s; case SYS_FILE_OPEN_ALWAYS: s = IoOpenFile(path, fileOut); if (s == STATUS_NOT_FOUND) { if (!writes) return STATUS_ACCESS_DENIED; s = IoCreateFile(path, fileOut); } return s; case SYS_FILE_TRUNCATE_EXISTING: if (!writes) return STATUS_ACCESS_DENIED; s = IoOpenFile(path, fileOut); if (KSUCCESS(s)) { KSTATUS ts = IoTruncateFile(*fileOut, 0); if (!KSUCCESS(ts)) { IoCloseFile(*fileOut); *fileOut = NULL; return ts; } } return s; default: return STATUS_INVALID_PARAMETER; } } // Implements SYS_OPEN_FILE. static uint64_t SyscallOpenFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_OPEN_FILE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; if (req.Flags != SYS_FILE_FLAG_NONE) return STATUS_INVALID_PARAMETER; uint32_t rights = 0; uint32_t fileFlags = 0; s = KiFileAccessToHandleRights(req.Access, &rights, &fileFlags); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = NULL; s = KiOpenFileByDisposition(path, req.Disposition, req.Access, &file); if (!KSUCCESS(s)) return s; if (fileFlags != 0) { uintptr_t flags = KeAcquireSpinlock(&file->Lock); file->Flags |= fileFlags; KeReleaseSpinlock(&file->Lock, flags); } uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&file->Header, rights, &h); if (!KSUCCESS(s)) { IoCloseFile(file); return s; } s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } 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; for (;;) { 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(uint64_t arg, SYS_FILE_IO_REQUEST* req) { KSTATUS s = KiCopyUserRequest(arg, req, sizeof(*req)); if (!KSUCCESS(s)) return s; if (req->Flags != 0) return STATUS_INVALID_PARAMETER; if (req->Size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (req->Size && !req->Buffer) return STATUS_INVALID_PARAMETER; return STATUS_SUCCESS; } // SYS_READ_FILE a0=*SYS_FILE_IO_REQUEST. static uint64_t SyscallReadFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_FILE_IO_REQUEST req; KSTATUS s = KiImportSyncFileIoRequest(a0, &req); if (!KSUCCESS(s)) return s; if (req.Size == 0) { if (req.TransferredOut) return KiCopySizeToUser((uint64_t)(uintptr_t)req.TransferredOut, 0); return STATUS_SUCCESS; } FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, HANDLE_RIGHT_READ); if (!file) return STATUS_NOT_FOUND; size_t chunkSize = 0; void* kbuf = KiAllocateSyscallIoBuffer(req.Size, &chunkSize); if (!kbuf) { ObDereferenceObject(&file->Header); return STATUS_NO_MEMORY; } uintptr_t user = (uintptr_t)req.Buffer; size_t total = 0; s = STATUS_SUCCESS; while (total < req.Size) { size_t todo = req.Size - total; if (todo > chunkSize) todo = chunkSize; if (user > (uintptr_t)-1 - total) { s = STATUS_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((uint64_t)(uintptr_t)req.TransferredOut, total); if (KSUCCESS(s)) s = cs; } return s; } // SYS_WRITE_FILE a0=*SYS_FILE_IO_REQUEST. static uint64_t SyscallWriteFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_FILE_IO_REQUEST req; KSTATUS s = KiImportSyncFileIoRequest(a0, &req); if (!KSUCCESS(s)) return s; if (req.Size == 0) { if (req.TransferredOut) return KiCopySizeToUser((uint64_t)(uintptr_t)req.TransferredOut, 0); return STATUS_SUCCESS; } FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, HANDLE_RIGHT_WRITE); if (!file) return STATUS_NOT_FOUND; size_t chunkSize = 0; void* kbuf = KiAllocateSyscallIoBuffer(req.Size, &chunkSize); if (!kbuf) { ObDereferenceObject(&file->Header); return STATUS_NO_MEMORY; } uintptr_t user = (uintptr_t)req.Buffer; size_t total = 0; s = STATUS_SUCCESS; while (total < req.Size) { size_t todo = req.Size - total; if (todo > chunkSize) todo = chunkSize; if (user > (uintptr_t)-1 - total) { s = STATUS_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((uint64_t)(uintptr_t)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); 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(uint64_t a0, bool write) { if (!a0) return STATUS_INVALID_PARAMETER; SYS_ASYNC_FILE_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a0, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Flags) return STATUS_INVALID_PARAMETER; if (!req.Iosb) return STATUS_INVALID_PARAMETER; if (!req.Buffer && req.Size) return STATUS_INVALID_PARAMETER; if (req.Size > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; if (req.RequestHandleOut) { s = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut)); if (!KSUCCESS(s)) return s; } FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, write ? HANDLE_RIGHT_WRITE : HANDLE_RIGHT_READ); if (!file) return STATUS_NOT_FOUND; EVENT* event = (EVENT*)0; if (req.EventHandle) { event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL); if (!event) { ObDereferenceObject(&file->Header); return STATUS_NOT_FOUND; } } 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 STATUS_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 STATUS_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.Size) { s = MmLockUserBuffer(proc, req.Buffer, req.Size, 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.Size, req.Offset, (IO_STATUS_BLOCK*)req.Iosb, event, completionApc, lockedBuffer, lockedIosb, &asyncReq); } else { s = IoReadFileAsyncKernel(file, req.Buffer, req.Size, 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 == STATUS_PENDING && req.RequestHandleOut && asyncReq) { uint32_t requestHandle = 0; KSTATUS hs = KeAllocateHandleWithRights(proc, IoAsyncFileRequestObject(asyncReq), HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE, &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) { uint32_t zeroHandle = 0; KSTATUS cs = CopyToUser(req.RequestHandleOut, &zeroHandle, sizeof(zeroHandle)); if (!KSUCCESS(cs)) { if (asyncReq) ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); if (s != STATUS_PENDING && apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return cs; } } if (asyncReq) ObDereferenceObject(IoAsyncFileRequestObject(asyncReq)); // drop creator reference if (s != STATUS_PENDING && apcCtx) SyscallUserAsyncApcContextFree(apcCtx); if (event) ObDereferenceObject(&event->Header); ObDereferenceObject(&file->Header); return s; } // SYS_READ_FILE_ASYNC (43) a0=*SYS_ASYNC_FILE_REQUEST. static uint64_t SyscallReadFileAsync(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; return SyscallAsyncFileCommon(a0, false); } // SYS_WRITE_FILE_ASYNC (44) a0=*SYS_ASYNC_FILE_REQUEST. static uint64_t SyscallWriteFileAsync(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; return SyscallAsyncFileCommon(a0, true); } // SYS_CANCEL_IO a0 = async request handle. static uint64_t SyscallCancelIo(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; IO_ASYNC_REQUEST_OBJECT* req = (IO_ASYNC_REQUEST_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeAsyncIo, HANDLE_RIGHT_TERMINATE); if (!req) return STATUS_INVALID_HANDLE; KSTATUS s = IoCancelAsyncRequest(req); ObDereferenceObject(IoAsyncRequestObject(req)); return s; } // SYS_DELETE_FILE (31) a0=*path. static uint64_t SyscallDeleteFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; return IoDeleteFile(path); } // SYS_TRUNCATE_FILE (32) a0=handle a1=newSize. static uint64_t SyscallTruncateFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_WRITE); if (!file) return STATUS_NOT_FOUND; KSTATUS s = IoTruncateFile(file, (uint64_t)a1); ObDereferenceObject(&file->Header); return s; } // SYS_FLUSH_FILE a0=handle. static uint64_t SyscallFlushFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_WRITE); if (!file) return STATUS_NOT_FOUND; KSTATUS s = IoFlushFile(file); ObDereferenceObject(&file->Header); return s; } // SYS_GET_FILE_POS (33) a0=handle a1=*positionOut. static uint64_t SyscallGetFilePos(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY); if (!file) return STATUS_INVALID_HANDLE; uintptr_t flags = KeAcquireSpinlock(&file->Lock); uint64_t pos = file->Offset; KeReleaseSpinlock(&file->Lock, flags); ObDereferenceObject(&file->Header); return KiCopyU64ToUser(a1, pos); } // SYS_SEEK_FILE (34) a0=handle a1=offset. static uint64_t SyscallSeekFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY); if (!file) return STATUS_NOT_FOUND; KSTATUS s = IoSeekFile(file, (uint64_t)a1); ObDereferenceObject(&file->Header); return s; } // SYS_GET_FILE_SIZE (35) a0=handle a1=*sizeOut. static uint64_t SyscallGetFileSize(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY); if (!file) return STATUS_INVALID_HANDLE; uint64_t sz = IoGetFileSize(file); ObDereferenceObject(&file->Header); return KiCopyU64ToUser(a1, sz); } // SYS_GET_FILE_STAT a0=handle a1=*SYS_FILE_STAT. static uint64_t SyscallGetFileStat(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeFile, HANDLE_RIGHT_QUERY); if (!file) return STATUS_INVALID_HANDLE; SYS_FILE_STAT stat; KSTATUS s = IoGetFileStat(file, &stat); ObDereferenceObject(&file->Header); if (!KSUCCESS(s)) return s; return CopyToUser((void*)(uintptr_t)a1, &stat, sizeof(stat)); } // SYS_STAT_FILE a0=*SYS_STAT_FILE_REQUEST. static uint64_t SyscallStatFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_STAT_FILE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.StatOut) return STATUS_INVALID_PARAMETER; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; SYS_FILE_STAT stat; s = IoStatFile(path, &stat); if (!KSUCCESS(s)) return s; return CopyToUser(req.StatOut, &stat, sizeof(stat)); } // SYS_CREATE_SURFACE (99) a0=width a1=height a2=format a3=flags a4=*handleOut. static uint64_t SyscallCreateSurface(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; uint32_t width = (uint32_t)a0; uint32_t height = (uint32_t)a1; uint32_t format = (uint32_t)a2; uint32_t flags = (uint32_t)a3; if (!width || !height || !a4) return STATUS_INVALID_PARAMETER; if (format != SYS_SURFACE_FORMAT_XRGB8888 || flags != SYS_SURFACE_FLAG_NONE) return STATUS_NOT_SUPPORTED; DISPLAY_MODE mode; KSTATUS s = KeDisplayGetMode(&mode); if (!KSUCCESS(s)) return s; if (width > mode.Width || height > mode.Height) return STATUS_INVALID_PARAMETER; size_t rowBytes = 0; if (!KiMulSize((size_t)width, sizeof(uint32_t), &rowBytes)) return STATUS_INVALID_PARAMETER; size_t pitchBytes = KiAlignUpSize(rowBytes, 64); if (pitchBytes < rowBytes) return STATUS_INVALID_PARAMETER; size_t payloadSize = 0; if (!KiMulSize(pitchBytes, (size_t)height, &payloadSize)) return STATUS_INVALID_PARAMETER; size_t pageCount = (payloadSize + PAGE_SIZE - 1) / PAGE_SIZE; if (!pageCount || pageCount > (SIZE_MAX / sizeof(PHYSICAL_ADDRESS))) return STATUS_INVALID_PARAMETER; SURFACE_OBJECT* surface = (SURFACE_OBJECT*)ObAllocateObject(ObjectTypeSurface, sizeof(SURFACE_OBJECT)); if (!surface) return STATUS_NO_MEMORY; surface->Header.Destroy = KiSurfaceDestroy; surface->Owner = KeGetCurrentProcess(); surface->Width = width; surface->Height = height; surface->PitchBytes = (uint32_t)pitchBytes; surface->Format = format; surface->Flags = flags; surface->PayloadSize = payloadSize; surface->PageCount = pageCount; surface->Frames = (PHYSICAL_ADDRESS*)MmAllocatePoolTag(pageCount * sizeof(PHYSICAL_ADDRESS), MM_POOL_TAG('s', 'f', 'r', 'm')); surface->RowScratch = (uint32_t*)MmAllocatePoolTag(rowBytes, MM_POOL_TAG('s', 'r', 'o', 'w')); s = KeMutexCreate(&surface->Lock); if (!surface->Frames || !surface->RowScratch || !KSUCCESS(s)) { ObDereferenceObject(&surface->Header); return STATUS_NO_MEMORY; } memset(surface->Frames, 0, pageCount * sizeof(PHYSICAL_ADDRESS)); for (size_t i = 0; i < pageCount; ++i) { PHYSICAL_ADDRESS frame = MmPfnAllocate(MmPfnUseSurface); if (!frame) { ObDereferenceObject(&surface->Header); return STATUS_NO_MEMORY; } surface->Frames[i] = frame; void* kva = MmPfnToVirtual(frame); if (kva) memset(kva, 0, PAGE_SIZE); } uint32_t handle = 0; s = KiAllocHandleForCurrentWithRights(&surface->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &handle); if (!KSUCCESS(s)) return s; KiSurfaceStatsRegister(surface); s = CopyToUser((void*)(uintptr_t)a4, &handle, sizeof(handle)); if (!KSUCCESS(s)) { KeCloseHandle(KeGetCurrentProcess(), handle); return s; } return STATUS_SUCCESS; } // SYS_MAP_SURFACE (100) a0=surfaceHandle a1=*baseOut a2=*pitchBytesOut. static uint64_t SyscallMapSurface(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a1 || !a2) return STATUS_INVALID_PARAMETER; SURFACE_OBJECT* surface = (SURFACE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSurface, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!surface) return STATUS_INVALID_PARAMETER; PROCESS* current = KeGetCurrentProcess(); if (surface->Owner != current) { ObDereferenceObject(&surface->Header); return STATUS_ACCESS_DENIED; } KSTATUS s = KeAcquireMutex(surface->Lock); if (!KSUCCESS(s)) { ObDereferenceObject(&surface->Header); return s; } if (!surface->UserBase) { s = MmVadCreateSharedViewFromFrames(current, surface->Frames, surface->PageCount, surface->PayloadSize, PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX, MM_VAD_ZERO, MmVadShared, &surface->UserBase); } void* userBase = surface->UserBase; uint32_t pitchBytes = surface->PitchBytes; KeReleaseMutex(surface->Lock); if (KSUCCESS(s)) s = CopyToUser((void*)(uintptr_t)a1, &userBase, sizeof(userBase)); if (KSUCCESS(s)) s = CopyToUser((void*)(uintptr_t)a2, &pitchBytes, sizeof(pitchBytes)); if (KSUCCESS(s)) KiSurfaceStatsRecordMap(surface); ObDereferenceObject(&surface->Header); return s; } // SYS_QUERY_SURFACE (101) a0=surfaceHandle a1=*SYS_SURFACE_DESC. static uint64_t SyscallQuerySurface(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; SURFACE_OBJECT* surface = (SURFACE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSurface, HANDLE_RIGHT_QUERY); if (!surface) return STATUS_INVALID_PARAMETER; if (surface->Owner != KeGetCurrentProcess()) { ObDereferenceObject(&surface->Header); return STATUS_ACCESS_DENIED; } SYS_SURFACE_DESC desc; memset(&desc, 0, sizeof(desc)); KSTATUS s = KeAcquireMutex(surface->Lock); if (KSUCCESS(s)) { desc.Width = surface->Width; desc.Height = surface->Height; desc.PitchBytes = surface->PitchBytes; desc.Format = surface->Format; desc.Base = surface->UserBase; desc.PayloadBytes = surface->PayloadSize; desc.PageCount = surface->PageCount; KeReleaseMutex(surface->Lock); uintptr_t statsFlags = KeAcquireSpinlock(&gSurfaceStatsLock); desc.MapCount = surface->MapCount; desc.PresentCount = surface->PresentCount; desc.PresentRectCount = surface->PresentRectCount; desc.PresentClippedRectCount = surface->PresentClippedRectCount; desc.PresentEmptyClipCount = surface->PresentEmptyClipCount; desc.PresentRowsCopied = surface->PresentRowsCopied; desc.PresentBytesCopied = surface->PresentBytesCopied; KeReleaseSpinlock(&gSurfaceStatsLock, statsFlags); s = CopyToUser((void*)(uintptr_t)a1, &desc, sizeof(desc)); } ObDereferenceObject(&surface->Header); return s; } static int KiClipSurfaceRect(const SURFACE_OBJECT* surface, const DISPLAY_RECT* in, DISPLAY_RECT* out) { if (!surface || !out) return 0; DISPLAY_RECT r; if (in) { r = *in; } else { r.X = 0; r.Y = 0; r.W = (int32_t)surface->Width; r.H = (int32_t)surface->Height; } if (r.W <= 0 || r.H <= 0) return 0; int64_t x0 = r.X; int64_t y0 = r.Y; int64_t x1 = x0 + r.W; int64_t y1 = y0 + r.H; if (x0 < 0) x0 = 0; if (y0 < 0) y0 = 0; if (x1 > surface->Width) x1 = surface->Width; if (y1 > surface->Height) y1 = surface->Height; if (x0 >= x1 || y0 >= y1) return 0; out->X = (int32_t)x0; out->Y = (int32_t)y0; out->W = (int32_t)(x1 - x0); out->H = (int32_t)(y1 - y0); return 1; } // SYS_PRESENT_SURFACE (102) a0=surfaceHandle a1=*DISPLAY_RECT[] a2=rectCount. static uint64_t SyscallPresentSurface(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; uint32_t rectCount = (uint32_t)a2; if (rectCount > SYS_SURFACE_MAX_RECTS) return STATUS_INVALID_PARAMETER; if (rectCount != 0 && !a1) return STATUS_INVALID_PARAMETER; SURFACE_OBJECT* surface = (SURFACE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSurface, HANDLE_RIGHT_READ); if (!surface) return STATUS_INVALID_PARAMETER; if (surface->Owner != KeGetCurrentProcess()) { ObDereferenceObject(&surface->Header); return STATUS_ACCESS_DENIED; } DISPLAY_RECT rects[SYS_SURFACE_MAX_RECTS]; if (rectCount == 0) { rects[0].X = 0; rects[0].Y = 0; rects[0].W = (int32_t)surface->Width; rects[0].H = (int32_t)surface->Height; rectCount = 1; } else { KSTATUS cs = CopyFromUser(rects, (const void*)(uintptr_t)a1, (size_t)rectCount * sizeof(DISPLAY_RECT)); if (!KSUCCESS(cs)) { ObDereferenceObject(&surface->Header); return cs; } } uint64_t inputRects = (uint64_t)a2; uint64_t clippedRects = 0; uint64_t emptyClips = 0; uint64_t rowsCopied = 0; uint64_t bytesCopied = 0; KSTATUS s = STATUS_SUCCESS; for (uint32_t i = 0; i < rectCount; ++i) { DISPLAY_RECT clipped; if (!KiClipSurfaceRect(surface, &rects[i], &clipped)) { emptyClips++; continue; } SURFACE_ROW_CONTEXT ctx; ctx.Surface = surface; ctx.SourceX = (uint32_t)clipped.X; ctx.SourceY = (uint32_t)clipped.Y; s = KeDisplayBlitRows(clipped.X, clipped.Y, clipped.W, clipped.H, KiSurfaceCopyRow, &ctx, surface->RowScratch); if (!KSUCCESS(s)) break; clippedRects++; rowsCopied += (uint64_t)(uint32_t)clipped.H; bytesCopied += (uint64_t)(uint32_t)clipped.W * (uint64_t)(uint32_t)clipped.H * sizeof(uint32_t); } if (KSUCCESS(s)) KiSurfaceStatsRecordPresent(surface, inputRects, clippedRects, emptyClips, rowsCopied, bytesCopied); ObDereferenceObject(&surface->Header); return s; } // SYS_CLEAR_SCREEN (67) a0=color. static uint64_t SyscallClearScreen(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; return KeDisplayClear((uint32_t)a0); } // SYS_GET_SCREEN_SIZE (68) a0=*widthOut a1=*heightOut. static uint64_t SyscallGetScreenSize(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; int w = 0, h = 0; KSTATUS gs = KeDisplayGetSize(&w, &h); if (!KSUCCESS(gs)) return gs; if (a0) { KSTATUS s = KiCopyIntToUser(a0, w); if (!KSUCCESS(s)) return s; } if (a1) { KSTATUS s = KiCopyIntToUser(a1, h); if (!KSUCCESS(s)) return s; } return STATUS_SUCCESS; } // SYS_PUT_SCREEN_CHAR (65) a0=c a1=x a2=y a3=fg a4=bg. static uint64_t SyscallPutScreenChar(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; return KeDisplayPutChar((char)a0, (int)a1, (int)a2, (uint32_t)a3, (uint32_t)a4); } // SYS_GET_CONSOLE_INFO (61) a0=*SYS_CONSOLE_INFO. static uint64_t SyscallGetConsoleInfo(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; KE_CONSOLE_INFO ki; KSTATUS s = KeDisplayGetConsoleInfo(&ki); if (!KSUCCESS(s)) return s; 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((void*)(uintptr_t)a0, &out, sizeof(out)); } // SYS_SET_CONSOLE_CURSOR (62) a0=column a1=row. static uint64_t SyscallSetConsoleCursor(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; int col = (int)(int32_t)a0; int row = (int)(int32_t)a1; if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER; return KeDisplaySetConsoleCursor(col, row); } // SYS_FILL_CONSOLE_CHARACTER (63) a0=char a1=column a2=row a3=count a4=*writtenOut. static uint64_t SyscallFillConsoleCharacter(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; int col = (int)(int32_t)a1; int row = (int)(int32_t)a2; if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER; size_t written = 0; KSTATUS s = KeDisplayFillConsoleChars((char)a0, col, row, (size_t)a3, &written); if (a4) { KSTATUS cs = KiCopySizeToUser(a4, written); if (KSUCCESS(s) && !KSUCCESS(cs)) s = cs; } return s; } // SYS_FILL_CONSOLE_ATTRIBUTE (64) a0=attribute a1=column a2=row a3=count a4=*writtenOut. static uint64_t SyscallFillConsoleAttribute(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; int col = (int)(int32_t)a1; int row = (int)(int32_t)a2; if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER; size_t written = 0; KSTATUS s = KeDisplayFillConsoleAttrs((uint16_t)a0, col, row, (size_t)a3, &written); if (a4) { KSTATUS cs = KiCopySizeToUser(a4, written); if (KSUCCESS(s) && !KSUCCESS(cs)) s = cs; } return s; } // Validates a user input queue handle. Handle zero means the default queue. static KSTATUS KiValidateInputQueueHandle(uint32_t handle) { if (handle == 0) return STATUS_SUCCESS; OBJECT* obj = KiHandleToObject(handle, ObjectTypeInputQueue, HANDLE_RIGHT_READ); if (!obj) return STATUS_INVALID_HANDLE; ObDereferenceObject(obj); return STATUS_SUCCESS; } // Copies one keyboard input record to user mode. static KSTATUS KiCopyInputRecordToUser(INPUT_KEY_RECORD* userRecord, const INPUT_KEY_RECORD* record) { if (!userRecord || !record) return STATUS_INVALID_PARAMETER; return CopyToUser(userRecord, record, sizeof(*record)); } // Imports and validates a keyboard input event request. static KSTATUS KiCopyInputEventRequest(uint64_t arg, SYS_INPUT_EVENT_REQUEST* req) { KSTATUS s = KiCopyUserRequest(arg, req, sizeof(*req)); if (!KSUCCESS(s)) return s; if (req->Size != sizeof(*req)) return STATUS_INVALID_PARAMETER; if (req->Flags != SYS_INPUT_EVENT_FLAG_NONE) return STATUS_INVALID_PARAMETER; if (!req->RecordOut) return STATUS_INVALID_PARAMETER; return KiValidateInputQueueHandle(req->QueueHandle); } // SYS_POLL_KEYBOARD a0=*INPUT_KEY -> KSTATUS. static uint64_t SyscallKbPoll(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; INPUT_KEY key = KeInputKbPoll(); KSTATUS s = CopyToUser((void*)(uintptr_t)a0, &key, sizeof(key)); if (!KSUCCESS(s)) return s; return (key == INPUT_KEY_NONE) ? STATUS_WOULD_BLOCK : STATUS_SUCCESS; } // SYS_IS_KEY_DOWN a0=INPUT_KEY a1=*bool -> KSTATUS. static uint64_t SyscallKbIsKeyDown(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; bool down = KeInputIsKeyDown((INPUT_KEY)a0); return CopyToUser((void*)(uintptr_t)a1, &down, sizeof(down)); } // SYS_READ_KEYBOARD_CHAR a0=*char -> KSTATUS. static uint64_t SyscallKbReadChar(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; for (;;) { char ch = KeInputReadChar(); if (ch) return CopyToUser((void*)(uintptr_t)a0, &ch, sizeof(ch)); KSTATUS s = KeInputWaitKey(0, SYS_WAIT_TIMEOUT_INFINITE); if (!KSUCCESS(s)) return s; } } // SYS_READ_KEY a0=*INPUT_KEY -> KSTATUS. static uint64_t SyscallKbReadKey(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; for (;;) { INPUT_KEY key = KeInputReadKey(); if (key != INPUT_KEY_NONE) return CopyToUser((void*)(uintptr_t)a0, &key, sizeof(key)); KSTATUS s = KeInputWaitKey(0, SYS_WAIT_TIMEOUT_INFINITE); if (!KSUCCESS(s)) return s; } } // SYS_GET_KEY_MODIFIERS -> uint32_t INPUT_MOD_* flags. static uint64_t SyscallKbGetModifiers(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; return (uint64_t)KeInputGetModifiers(); } // SYS_READ_INPUT_EVENT a0=*SYS_INPUT_EVENT_REQUEST -> KSTATUS. static uint64_t SyscallKbReadInputEvent(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_INPUT_EVENT_REQUEST req; KSTATUS s = KiCopyInputEventRequest(a0, &req); if (!KSUCCESS(s)) return s; INPUT_KEY_RECORD record; for (;;) { if (KeInputReadEvent(&record)) return KiCopyInputRecordToUser(req.RecordOut, &record); if (req.TimeoutMilliseconds == 0) return STATUS_WOULD_BLOCK; s = KeInputWaitKey(0, req.TimeoutMilliseconds); if (!KSUCCESS(s)) return s; } } // SYS_PEEK_INPUT_EVENT a0=*SYS_INPUT_EVENT_REQUEST -> KSTATUS. static uint64_t SyscallKbPeekInputEvent(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_INPUT_EVENT_REQUEST req; KSTATUS s = KiCopyInputEventRequest(a0, &req); if (!KSUCCESS(s)) return s; INPUT_KEY_RECORD record; for (;;) { if (KeInputPeekEvent(&record)) return KiCopyInputRecordToUser(req.RecordOut, &record); if (req.TimeoutMilliseconds == 0) return STATUS_WOULD_BLOCK; s = KeInputWaitKey(0, req.TimeoutMilliseconds); if (!KSUCCESS(s)) return s; } } // SYS_PEEK_MOUSE a0=*INPUT_MOUSE_EVENT -> KSTATUS. static uint64_t SyscallMousePeek(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; INPUT_MOUSE_EVENT event; if (!KeInputMousePeek(&event)) return STATUS_WOULD_BLOCK; return CopyToUser((void*)(uintptr_t)a0, &event, sizeof(event)); } // SYS_READ_MOUSE a0=*INPUT_MOUSE_EVENT -> KSTATUS. static uint64_t SyscallMouseRead(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; INPUT_MOUSE_EVENT event; if (!KeInputMouseRead(&event)) return STATUS_WOULD_BLOCK; return CopyToUser((void*)(uintptr_t)a0, &event, sizeof(event)); } // SYS_GET_MOUSE_POS a0=*x a1=*y -> KSTATUS. static uint64_t SyscallMouseGetPos(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a0 || !a1) return STATUS_INVALID_PARAMETER; int x = 0, y = 0; KeInputMouseGetPosition(&x, &y); KSTATUS s = KiCopyIntToUser(a0, x); if (!KSUCCESS(s)) return s; return KiCopyIntToUser(a1, y); } // SYS_GET_TICK (75) returns monotonic ticks since boot. static uint64_t SyscallGetTick(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; return gHal ? gHal->Timer.QueryTicks() : 0; } // SYS_GET_TICK_FREQUENCY (110) copies SysGetTick ticks per second. static uint64_t SyscallGetTickFrequency(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!gHal || !gHal->Timer.QueryTickFrequency) return STATUS_DEVICE_ERROR; uint64_t frequency = gHal->Timer.QueryTickFrequency(); if (!frequency) return STATUS_DEVICE_ERROR; return KiCopyU64ToUser(a0, frequency); } // SYS_GET_SYSTEM_TIME a0=*SYS_SYSTEM_TIME. static uint64_t SyscallGetSystemTime(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; SYS_SYSTEM_TIME t; KSTATUS s = KeQuerySystemTime(&t); if (!KSUCCESS(s)) return s; return CopyToUser((void*)(uintptr_t)a0, &t, sizeof(t)); } // SYS_PRINT_DEBUG (79) a0=*str. static uint64_t SyscallDebugPrint(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PRINT_DEBUG_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Text.Length == 0) return STATUS_SUCCESS; if (!req.Text.Data) return STATUS_INVALID_PARAMETER; if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return STATUS_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 STATUS_SUCCESS; } // SYS_QUERY_TRAP_FRAME a0=*SYS_TRAP_FRAME_INFO. static uint64_t SyscallQueryTrapFrame(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; uint32_t userSize = 0; KSTATUS s = CopyFromUser(&userSize, (const void*)(uintptr_t)a0, sizeof(userSize)); if (!KSUCCESS(s)) return s; SYS_TRAP_FRAME_INFO_K info; memset(&info, 0, sizeof(info)); info.Size = userSize; s = KeQueryCurrentTrapFrame(&info); size_t copySize = (s == STATUS_BUFFER_TOO_SMALL) ? sizeof(info.Size) : sizeof(info); KSTATUS cs = CopyToUser((void*)(uintptr_t)a0, &info, copySize); if (!KSUCCESS(cs)) return cs; return s; } // SYS_CREATE_PROCESS (50) a0=*path a1=*handleOut a2=*cmdline (optional). static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_CREATE_PROCESS_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char pathBuf[SYSCALL_STRING_MAX]; char cmdlineBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; SYS_STRING_VIEW cmdlineView; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; s = KiImportUserTextView(req.CommandLine, cmdlineBuf, sizeof(cmdlineBuf), &cmdlineView, true); if (!KSUCCESS(s)) return s; s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; // Read the executable from disk. FILE_OBJECT* f; ProcDbg("CreateProcess: path='%v' cmd='%v' callerPid=%u\n", path, cmdlineView, KeGetCurrentProcess() ? KeGetCurrentProcess()->ProcessId : 0); s = IoOpenFile(path, &f); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: IoOpenFile failed st=%d\n", s); return s; } uint64_t size = IoGetFileSize(f); ProcDbg("CreateProcess: fileSize=%llu\n", size); // Create the process object. PROCESS* proc; s = KeCreateProcess(pathBuf, &proc); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: KeCreateProcess failed st=%d\n", s); IoCloseFile(f); return s; } ProcDbg("CreateProcess: created pid=%u name=%s\n", proc->ProcessId, proc->Name); // Inherit CWD from parent. PROCESS* parent = KeGetCurrentProcess(); if (parent) { uintptr_t flags = KeAcquireSpinlock(&parent->CwdLock); size_t cwdLen = parent->CurrentDirectoryLength; if (cwdLen >= sizeof(proc->CurrentDirectory)) cwdLen = sizeof(proc->CurrentDirectory) - 1; memcpy(proc->CurrentDirectory, parent->CurrentDirectory, cwdLen); proc->CurrentDirectory[cwdLen] = '\0'; proc->CurrentDirectoryLength = cwdLen; proc->CurrentDrive = parent->CurrentDrive; KeReleaseSpinlock(&parent->CwdLock, flags); } s = KeSetupUserProcessEnvironment(proc, path, cmdlineView, SysSvFromParts(proc->CurrentDirectory, proc->CurrentDirectoryLength)); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: env setup failed st=%d\n", s); IoCloseFile(f); ObDereferenceObject(&proc->Header); return s; } // Map the PE image into memory. uintptr_t imageBase = 0; s = LdrLoadImageFileForProcess(proc, f, &imageBase); IoCloseFile(f); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: LdrLoadImageFileForProcess failed st=%d\n", s); ObDereferenceObject(&proc->Header); return s; } ProcDbg("CreateProcess: image mapped pid=%u base=%016llx size=%llx\n", proc->ProcessId, (unsigned long long)imageBase, (unsigned long long)LdrGetMappedImageSize(proc, imageBase)); s = LdrResolveImportsForProcess(proc, imageBase); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: resolve imports failed st=%d pid=%u image=%016llx\n", s, proc->ProcessId, (unsigned long long)imageBase); MmVadDebugDumpProcess(proc); ObDereferenceObject(&proc->Header); return s; } ProcDbg("CreateProcess: imports resolved pid=%u modules=%u\n", proc->ProcessId, proc->ModuleCount); s = LdrProtectImageForProcess(proc, imageBase, true); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: protect image failed st=%d pid=%u\n", s, proc->ProcessId); MmVadDebugDumpProcess(proc); ObDereferenceObject(&proc->Header); return s; } proc->ImageBase = imageBase; proc->ImageSize = LdrGetMappedImageSize(proc, imageBase); PROCESS* caller = KeGetCurrentProcess(); if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; } uint32_t handle = 0; s = KeAllocateHandleWithRights(caller, &proc->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE | HANDLE_RIGHT_DUPLICATE, &handle); if (!KSUCCESS(s)) { proc->ImageBase = 0; proc->ImageSize = 0; ObDereferenceObject(&proc->Header); return s; } THREAD* thread; uintptr_t entryPoint = LdrGetEntryPoint(imageBase); s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread); if (!KSUCCESS(s)) { ProcDbg("CreateProcess: KeCreateThread failed st=%d pid=%u entry=%016llx\n", s, proc->ProcessId, (unsigned long long)entryPoint); MmVadDebugDumpProcess(proc); KeCloseHandle(caller, handle); ObDereferenceObject(&proc->Header); return s; } uint32_t tid = thread->ThreadId; ProcDbg("CreateProcess: thread created pid=%u tid=%llu entry=%016llx handle=%u\n", proc->ProcessId, (unsigned long long)tid, (unsigned long long)entryPoint, handle); ObDereferenceObject(&thread->Header); s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle); if (!KSUCCESS(s)) { KeTerminateProcess(proc, (int)STATUS_PROCESS_PAGE_FAULT); KeCloseHandle(caller, handle); ObDereferenceObject(&proc->Header); return s; } // Drop the creator's local reference. The returned handle and live thread // references now own the process lifetime. ObDereferenceObject(&proc->Header); return STATUS_SUCCESS; } // SYS_OPEN_PROCESS a0=pid a1=*handleOut. static uint64_t SyscallOpenProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; PROCESS* proc = KeGetProcessById((uint32_t)a0); if (!proc) return STATUS_NOT_FOUND; PROCESS* caller = KeGetCurrentProcess(); if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; } uint32_t handle = 0; s = KeAllocateHandleWithRights(caller, &proc->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE | HANDLE_RIGHT_DUPLICATE, &handle); ObDereferenceObject(&proc->Header); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser(a1, handle); if (!KSUCCESS(s)) { KeCloseHandle(caller, handle); return s; } return STATUS_SUCCESS; } // SYS_GET_PROCESS_EXIT_CODE (4) copies the process exit code. static uint64_t SyscallGetProcessExitCode(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_QUERY); if (!proc) return STATUS_INVALID_HANDLE; int32_t exitCode = 0; KSTATUS s = KeGetProcessExitCode(proc, &exitCode); ObDereferenceObject(&proc->Header); if (!KSUCCESS(s)) return s; return KiCopyInt32ToUser(a1, exitCode); } // SYS_WRITE_CONSOLE (60) a0=*buf a1=len // Writes user console text in white; distinct from green SYS_PRINT_DEBUG. static uint64_t SyscallWriteConsole(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_WRITE_CONSOLE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Text.Length == 0) return STATUS_SUCCESS; if (!req.Text.Data) return STATUS_INVALID_PARAMETER; if (req.Text.Length > SYSCALL_TEXT_MAX_BYTES) return STATUS_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; KePrintConsole(chunk, todo); offset += todo; } return STATUS_SUCCESS; } // SYS_OPEN_DIR (54) a0=*dirPath a1=*handleOut. static uint64_t SyscallOpenDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_HANDLE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char dirPathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW dirPath; s = KiImportUserTextView(req.Path, dirPathBuf, sizeof(dirPathBuf), &dirPath, true); if (!KSUCCESS(s)) return s; s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; DIR_OBJECT* dir = NULL; s = IoOpenDir(dirPath, &dir); if (!KSUCCESS(s)) return s; uint32_t handle = 0; s = KiAllocHandleForCurrentWithRights(&dir->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_QUERY, &handle); if (!KSUCCESS(s)) return s; // KiAllocHandleForCurrentWithRights consumed IoOpenDir's creator reference; // the returned process handle now owns the directory object. s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); return s; } return STATUS_SUCCESS; } // SYS_READ_DIR (55) a0=handle a1=*KDIR_ENTRY // Returns STATUS_NO_MORE_FILES when the directory is exhausted. static uint64_t SyscallReadDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; DIR_OBJECT* dir = (DIR_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeDir, HANDLE_RIGHT_READ); if (!dir) return STATUS_INVALID_PARAMETER; KDIR_ENTRY entry; KSTATUS s = IoReadDir(dir, &entry); ObDereferenceObject(&dir->Header); if (!KSUCCESS(s)) return s; return CopyToUser((void*)(uintptr_t)a1, &entry, sizeof(entry)); } // SYS_SET_CWD (56) a0=*path. static uint64_t SyscallSetCwd(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; return IoSetCwd(path); } // SYS_REAL_PATH a0=*SYS_REAL_PATH_REQUEST. static uint64_t SyscallRealPath(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_REAL_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req) || req.Flags != SYS_REAL_PATH_FLAG_NONE) return STATUS_INVALID_PARAMETER; if (!req.Buffer && req.BufferSize != 0) return STATUS_INVALID_PARAMETER; if (!req.Buffer && !req.RequiredSizeOut) return STATUS_INVALID_PARAMETER; if (req.RequiredSizeOut) { s = MmProbeUserWrite(req.RequiredSizeOut, sizeof(size_t)); if (!KSUCCESS(s)) return s; } char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; char resolved[SYS_CWD_MAX]; size_t required = 0; s = IoRealPath(path, resolved, sizeof(resolved), &required); if (!KSUCCESS(s)) return s; if (req.RequiredSizeOut) { KSTATUS cs = KiCopySizeToUser((uint64_t)(uintptr_t)req.RequiredSizeOut, required); if (!KSUCCESS(cs)) return cs; } if (!req.Buffer) return STATUS_SUCCESS; if (req.BufferSize < required) return STATUS_BUFFER_TOO_SMALL; return CopyToUser(req.Buffer, resolved, required); } // SYS_GET_PROCESS_PID (59) a0=procHandle a1=*pidOut // 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(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_QUERY); if (!proc) return STATUS_INVALID_HANDLE; uint32_t pid = proc->ProcessId; ObDereferenceObject(&proc->Header); return KiCopyU32ToUser(a1, pid); } // Named Queue Registry // A flat, fixed-size table of { name -> QUEUE* } pairs protected by a // spinlock. The registry holds one reference to each published queue for // the lifetime of the entry, so the queue object cannot be destroyed while // it is registered even if every process closes its own handle to it. // Limits: NAMED_QUEUE_MAX names, each up to NAMED_QUEUE_NAME_MAX bytes. // These are generous for the intended usage (a handful of system servers). #define NAMED_QUEUE_MAX 16 #define NAMED_QUEUE_NAME_MAX 64 typedef struct { char Name[NAMED_QUEUE_NAME_MAX]; size_t NameLength; QUEUE* Queue; // NULL = slot free; non-NULL = occupied uint32_t PublisherPid; // ProcessId of whoever called SysPublishQueue } NAMED_QUEUE_ENTRY; static NAMED_QUEUE_ENTRY gNamedQueues[NAMED_QUEUE_MAX]; static SPINLOCK gNamedQueueLock; static int KiNamedQueueNameEquals(const NAMED_QUEUE_ENTRY* entry, SYS_STRING_VIEW name) { if (!entry || !entry->Queue) return 0; return SysSvEqual(SysSvFromParts(entry->Name, entry->NameLength), name); } // SYS_PUBLISH_QUEUE (60) a0=*name a1=queueHandle // Registers the queue identified by 'queueHandle' under 'name'. // Fails with STATUS_ALREADY_EXISTS if the name is already taken. // The registry bumps the queue's refcount so it outlives the handle. static uint64_t SyscallPublishQueue(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PUBLISH_QUEUE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char name[NAMED_QUEUE_NAME_MAX]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); if (!KSUCCESS(s)) return s; if (nameView.Length == 0) return STATUS_INVALID_PARAMETER; QUEUE* q = (QUEUE*)KiHandleToObject(req.QueueHandle, ObjectTypeQueue, HANDLE_RIGHT_DUPLICATE); if (!q) return STATUS_INVALID_HANDLE; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { KeReleaseSpinlock(&gNamedQueueLock, flags); ObDereferenceObject(&q->Header); return STATUS_ALREADY_EXISTS; } } for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (!gNamedQueues[i].Queue) { memcpy(gNamedQueues[i].Name, name, nameView.Length); gNamedQueues[i].NameLength = nameView.Length; gNamedQueues[i].Queue = q; gNamedQueues[i].PublisherPid = KeGetCurrentProcess()->ProcessId; KeReleaseSpinlock(&gNamedQueueLock, flags); return STATUS_SUCCESS; } } KeReleaseSpinlock(&gNamedQueueLock, flags); ObDereferenceObject(&q->Header); return STATUS_NO_MEMORY; } // SYS_OPEN_NAMED_QUEUE (61) a0=*name a1=*handleOut // Looks up 'name' in the registry and allocates a handle to the queue in the // calling process's handle table. Returns STATUS_NOT_FOUND if no match. // Callers that race against the publisher should retry on NOT_FOUND. static uint64_t SyscallOpenNamedQueue(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_OPEN_NAMED_QUEUE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.HandleOut) return STATUS_INVALID_PARAMETER; char name[NAMED_QUEUE_NAME_MAX]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); if (!KSUCCESS(s)) return s; if (nameView.Length == 0) return STATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.HandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); QUEUE* q = NULL; for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { q = gNamedQueues[i].Queue; ObReferenceObject(&q->Header); break; } } KeReleaseSpinlock(&gNamedQueueLock, flags); if (!q) return STATUS_NOT_FOUND; uint32_t handle = 0; s = KiAllocHandleForCurrentWithRights(&q->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_DUPLICATE | HANDLE_RIGHT_QUERY, &handle); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.HandleOut, handle); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, handle); return s; } return STATUS_SUCCESS; } // SYS_DUPLICATE_HANDLE a0=*request. // Duplicates a handle from the caller into the target process table. static uint64_t SyscallDuplicateHandle(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_DUPLICATE_HANDLE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.SourceHandle || !req.TargetProcessHandle || !req.TargetHandleOut) return STATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.TargetHandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; PROCESS* caller = KeGetCurrentProcess(); if (!caller) return STATUS_FAILURE; PROCESS* target = (PROCESS*)KiHandleToObject(req.TargetProcessHandle, ObjectTypeProcess, HANDLE_RIGHT_DUPLICATE); if (!target) return STATUS_INVALID_HANDLE; uint32_t targetHandle = 0; s = KeDuplicateHandleToProcess(caller, req.SourceHandle, target, req.DesiredRights, req.Flags, &targetHandle); if (!KSUCCESS(s)) { ObDereferenceObject(&target->Header); return s; } s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.TargetHandleOut, targetHandle); if (!KSUCCESS(s)) { KeCloseHandle(target, targetHandle); ObDereferenceObject(&target->Header); return s; } ObDereferenceObject(&target->Header); return STATUS_SUCCESS; } // SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS a0=*request. // Opens a process handle for the process that published a named queue. static uint64_t SyscallOpenNamedQueueOwnerProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_NAMED_QUEUE_OWNER_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.ProcessHandleOut) return STATUS_INVALID_PARAMETER; char name[NAMED_QUEUE_NAME_MAX]; SYS_STRING_VIEW nameView; s = KiImportUserTextView(req.Name, name, sizeof(name), &nameView, false); if (!KSUCCESS(s)) return s; if (nameView.Length == 0) return STATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.ProcessHandleOut, sizeof(uint32_t)); if (!KSUCCESS(s)) return s; uint32_t pid = 0; uintptr_t flags = KeAcquireSpinlock(&gNamedQueueLock); for (int i = 0; i < NAMED_QUEUE_MAX; i++) { if (KiNamedQueueNameEquals(&gNamedQueues[i], nameView)) { pid = gNamedQueues[i].PublisherPid; break; } } KeReleaseSpinlock(&gNamedQueueLock, flags); if (!pid) return STATUS_NOT_FOUND; PROCESS* owner = KeGetProcessById(pid); if (!owner) return STATUS_NOT_FOUND; PROCESS* caller = KeGetCurrentProcess(); if (!caller) { ObDereferenceObject(&owner->Header); return STATUS_FAILURE; } uint32_t handle = 0; s = KeAllocateHandleWithRights(caller, &owner->Header, HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_DUPLICATE, &handle); ObDereferenceObject(&owner->Header); if (!KSUCCESS(s)) return s; s = KiCopyHandleToUser((uint64_t)(uintptr_t)req.ProcessHandleOut, handle); if (!KSUCCESS(s)) { KeCloseHandle(caller, handle); return s; } return STATUS_SUCCESS; } static uint64_t SyscallKillProcess(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = (PROCESS*)KiHandleToObject((uint32_t)a0, ObjectTypeProcess, HANDLE_RIGHT_TERMINATE); if (!proc) return STATUS_INVALID_HANDLE; THREAD* self = KeGetCurrentThread(); int32_t exitCode = (int32_t)a1; 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; } // SYS_GET_PROCESS_LIST (6) a0=*SYS_PROCESS_INFO[] a1=maxCount a2=*countOut. static uint64_t SyscallGetProcessList(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; size_t maxCount = (size_t)a1; if (maxCount && !a0) return STATUS_INVALID_PARAMETER; if (a2) { KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a2, 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 STATUS_INVALID_PARAMETER; SYS_PROCESS_INFO* tmp = (SYS_PROCESS_INFO*)MmAllocatePoolTag(bytes, MM_POOL_TAG('s', 'y', 's', ' ')); if (!tmp) return STATUS_NO_MEMORY; size_t copied = KeCopyProcessInfo(tmp, toCopy); if (copied < toCopy) toCopy = copied; KSTATUS s = CopyToUser((void*)(uintptr_t)a0, tmp, toCopy * sizeof(SYS_PROCESS_INFO)); MmFreePool(tmp); if (!KSUCCESS(s)) return s; } if (a2) return KiCopySizeToUser(a2, total); return STATUS_SUCCESS; } // SYS_GET_THREAD_LIST (103) a0=pid a1=*SYS_THREAD_INFO[] a2=maxCount a3=*countOut. static uint64_t SyscallGetThreadList(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; uint32_t pid = (uint32_t)a0; size_t maxCount = (size_t)a2; if (maxCount && !a1) return STATUS_INVALID_PARAMETER; if (a3) { KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a3, 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 STATUS_INVALID_PARAMETER; SYS_THREAD_INFO* tmp = (SYS_THREAD_INFO*)MmAllocatePoolTag(bytes, MM_POOL_TAG('s', 't', 'h', 'r')); if (!tmp) return STATUS_NO_MEMORY; size_t copied = 0; s = KeCopyProcessThreadInfo(pid, tmp, toCopy, &copied); if (KSUCCESS(s)) { if (copied < toCopy) toCopy = copied; s = CopyToUser((void*)(uintptr_t)a1, tmp, toCopy * sizeof(SYS_THREAD_INFO)); } MmFreePool(tmp); if (!KSUCCESS(s)) return s; } if (a3) return KiCopySizeToUser(a3, total); return STATUS_SUCCESS; } // SYS_LOAD_MODULE a0=*SYS_LOAD_MODULE_REQUEST. static uint64_t SyscallLoadModule(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_LOAD_MODULE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER; if (req.Flags != SYS_LOAD_MODULE_FLAG_NONE) return STATUS_INVALID_PARAMETER; if (!req.ModuleHandleOut) return STATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.ModuleHandleOut, sizeof(*req.ModuleHandleOut)); if (!KSUCCESS(s)) return s; if (req.InfoOut) { s = MmProbeUserWrite(req.InfoOut, sizeof(*req.InfoOut)); if (!KSUCCESS(s)) return s; } char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; uintptr_t base = 0; s = LdrLoadModuleForProcess(proc, pathBuf, LDR_MODULE_LOAD_PUBLIC, &base); if (!KSUCCESS(s)) return s; uint32_t handle = 0; s = KiCreateModuleHandle(proc, base, &handle); if (!KSUCCESS(s)) { (void)LdrUnloadModuleForProcess(proc, base); return s; } SYS_MODULE_INFO info; if (req.InfoOut) { s = LdrQueryModuleForProcess(proc, base, &info); if (!KSUCCESS(s)) { KeCloseHandle(proc, handle); return s; } } s = KiCopyU32ToUser((uint64_t)(uintptr_t)req.ModuleHandleOut, handle); if (!KSUCCESS(s)) { KeCloseHandle(proc, handle); return s; } if (req.InfoOut) { s = CopyToUser(req.InfoOut, &info, sizeof(info)); if (!KSUCCESS(s)) { KeCloseHandle(proc, handle); return s; } } return STATUS_SUCCESS; } // SYS_UNLOAD_MODULE a0=moduleHandle. static uint64_t SyscallUnloadModule(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; uint32_t handle = (uint32_t)a0; MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject(handle, ObjectTypeModule, HANDLE_RIGHT_TERMINATE); if (!module) return STATUS_INVALID_HANDLE; KeCloseHandle(proc, handle); ObDereferenceObject(&module->Header); return STATUS_SUCCESS; } // SYS_GET_MODULE_INFO a0=moduleHandle a1=*SYS_MODULE_INFO. static uint64_t SyscallGetModuleInfo(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(SYS_MODULE_INFO)); if (!KSUCCESS(s)) return s; MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeModule, HANDLE_RIGHT_QUERY); if (!module) return STATUS_INVALID_HANDLE; SYS_MODULE_INFO info; s = LdrQueryModuleForProcess(module->Owner, module->Base, &info); ObDereferenceObject(&module->Header); if (!KSUCCESS(s)) return s; return CopyToUser((void*)(uintptr_t)a1, &info, sizeof(info)); } // SYS_GET_MODULE_EXPORT a0=*SYS_GET_MODULE_EXPORT_REQUEST. static uint64_t SyscallGetModuleExport(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_GET_MODULE_EXPORT_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER; if (!req.AddressOut) return STATUS_INVALID_PARAMETER; s = MmProbeUserWrite(req.AddressOut, sizeof(*req.AddressOut)); if (!KSUCCESS(s)) return s; char nameBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW name; s = KiImportUserTextView(req.Name, nameBuf, sizeof(nameBuf), &name, true); if (!KSUCCESS(s)) return s; if (name.Length == 0) return STATUS_INVALID_PARAMETER; MODULE_HANDLE_OBJECT* module = (MODULE_HANDLE_OBJECT*)KiHandleToObject(req.ModuleHandle, ObjectTypeModule, HANDLE_RIGHT_QUERY); if (!module) return STATUS_INVALID_HANDLE; void* address = LdrGetModuleExportForProcess(module->Owner, module->Base, nameBuf); ObDereferenceObject(&module->Header); if (!address) return STATUS_NOT_FOUND; uintptr_t userAddress = (uintptr_t)address; return CopyToUser(req.AddressOut, &userAddress, sizeof(userAddress)); } // SYS_GET_MODULE_LIST a0=*SYS_MODULE_INFO[] a1=maxCount a2=*countOut. static uint64_t SyscallGetModuleList(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; size_t maxCount = (size_t)a1; if (maxCount && !a0) return STATUS_INVALID_PARAMETER; if (a2) { KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a2, sizeof(size_t)); if (!KSUCCESS(s)) return s; } PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; size_t total = 0; KSTATUS s = LdrCopyProcessModuleInfo(proc, NULL, 0, &total); if (s != STATUS_BUFFER_TOO_SMALL && !KSUCCESS(s)) return s; size_t toCopy = total < maxCount ? total : maxCount; if (toCopy) { size_t bytes = 0; if (!KiMulSize(toCopy, sizeof(SYS_MODULE_INFO), &bytes)) return STATUS_INVALID_PARAMETER; SYS_MODULE_INFO* tmp = (SYS_MODULE_INFO*)MmAllocatePoolTag(bytes, MM_POOL_TAG('m', 'o', 'd', 'l')); if (!tmp) return STATUS_NO_MEMORY; size_t copiedTotal = 0; s = LdrCopyProcessModuleInfo(proc, tmp, toCopy, &copiedTotal); if (s == STATUS_BUFFER_TOO_SMALL) s = STATUS_SUCCESS; if (KSUCCESS(s)) s = CopyToUser((void*)(uintptr_t)a0, tmp, toCopy * sizeof(SYS_MODULE_INFO)); MmFreePool(tmp); if (!KSUCCESS(s)) return s; } if (a2) return KiCopySizeToUser(a2, total); return STATUS_SUCCESS; } // SYS_RENAME_FILE (66) a0=*oldPath a1=*newPath. static uint64_t SyscallRenameFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_RENAME_FILE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char oldPathBuf[SYSCALL_STRING_MAX]; char newPathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW oldPath; SYS_STRING_VIEW newPath; s = KiImportUserTextView(req.OldPath, oldPathBuf, sizeof(oldPathBuf), &oldPath, true); if (!KSUCCESS(s)) return s; s = KiImportUserTextView(req.NewPath, newPathBuf, sizeof(newPathBuf), &newPath, true); if (!KSUCCESS(s)) return s; if (oldPath.Length == 0 || newPath.Length == 0) return STATUS_INVALID_PARAMETER; return IoRenameFile(oldPath, newPath); } // SYS_LOCK_FILE a0=*SYS_LOCK_FILE_REQUEST. static uint64_t SyscallLockFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_LOCK_FILE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER; if (req.Flags != SYS_FILE_LOCK_FLAG_NONE) return STATUS_INVALID_PARAMETER; if (req.Mode != SYS_FILE_LOCK_SHARED && req.Mode != SYS_FILE_LOCK_EXCLUSIVE) return STATUS_INVALID_PARAMETER; if (req.Length == 0) return STATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, HANDLE_RIGHT_LOCK); if (!file) return STATUS_INVALID_HANDLE; s = IoLockFile(file, proc, req.Mode, req.Offset, req.Length, req.TimeoutMilliseconds); ObDereferenceObject(&file->Header); return s; } // SYS_UNLOCK_FILE a0=*SYS_UNLOCK_FILE_REQUEST. static uint64_t SyscallUnlockFile(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_UNLOCK_FILE_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Size != sizeof(req)) return STATUS_INVALID_PARAMETER; if (req.Flags != SYS_FILE_LOCK_FLAG_NONE) return STATUS_INVALID_PARAMETER; if (req.Length == 0) return STATUS_INVALID_PARAMETER; PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; FILE_OBJECT* file = (FILE_OBJECT*)KiHandleToObject(req.FileHandle, ObjectTypeFile, HANDLE_RIGHT_LOCK); if (!file) return STATUS_INVALID_HANDLE; s = IoUnlockFile(file, proc, req.Offset, req.Length); ObDereferenceObject(&file->Header); return s; } // SYS_CREATE_DIR a0=*path. static uint64_t SyscallCreateDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; return IoCreateDir(path); } // SYS_REMOVE_DIR (68) a0=*path. static uint64_t SyscallRemoveDir(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; SYS_PATH_REQUEST req; KSTATUS s = KiCopyUserRequest(a0, &req, sizeof(req)); if (!KSUCCESS(s)) return s; char pathBuf[SYSCALL_STRING_MAX]; SYS_STRING_VIEW path; s = KiImportUserTextView(req.Path, pathBuf, sizeof(pathBuf), &path, true); if (!KSUCCESS(s)) return s; if (path.Length == 0) return STATUS_INVALID_PARAMETER; return IoRemoveDir(path); } typedef struct SYS_SOCKET_SEND_TO_REQUEST_K { 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 { 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 { 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 Option; void* Value; size_t ValueSize; size_t* ActualSizeOut; } SYS_SOCKET_OPTION_REQUEST_K; typedef struct SYS_ASYNC_SOCKET_REQUEST_K { uint32_t Size; uint32_t SocketHandle; uint32_t 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; uint32_t* HandleOut; uint32_t* PollReturnedEventsOut; SYS_IO_STATUS_BLOCK_K* Iosb; SYS_USER_APC_ROUTINE_K ApcRoutine; void* ApcContext; uint32_t* RequestHandleOut; } SYS_ASYNC_SOCKET_REQUEST_K; 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 // SYS_CREATE_SOCKET a0=domain a1=type a2=protocol a3=*handleOut. static uint64_t SyscallSocket(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a3) return STATUS_INVALID_PARAMETER; SOCKET_OBJECT* socket = NULL; KSTATUS s = NetSocketCreate((uint32_t)a0, (uint32_t)a1, (uint32_t)a2, &socket); if (!KSUCCESS(s)) return s; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&socket->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &h); if (!KSUCCESS(s)) return s; // KiAllocHandleForCurrentWithRights consumed NetSocketCreate's creator // reference; the returned process handle now owns the socket object. s = KiCopyHandleToUser(a3, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // SYS_BIND_SOCKET a0=socketHandle a1=*SYS_SOCKET_ADDRESS a2=addressSize. static uint64_t SyscallBind(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; if ((size_t)a2 < sizeof(SYS_SOCKET_ADDRESS_K)) return STATUS_BUFFER_TOO_SMALL; SYS_SOCKET_ADDRESS_K address; KSTATUS s = CopyFromUser(&address, (const void*)(uintptr_t)a1, sizeof(address)); if (!KSUCCESS(s)) return s; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; s = NetSocketBind(socket, &address, (size_t)a2); ObDereferenceObject(&socket->Header); return s; } // SYS_LISTEN_SOCKET a0=socketHandle a1=backlog. static uint64_t SyscallListen(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; KSTATUS s = NetSocketListen(socket, (uint32_t)a1); ObDereferenceObject(&socket->Header); return s; } // SYS_ACCEPT_SOCKET a0=listenSocket a1=*remoteAddressOut(optional) // a2=*remoteAddressSizeInOut(optional) a3=*acceptedHandleOut. static uint64_t SyscallAccept(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a3) return STATUS_INVALID_PARAMETER; bool wantRemote = (a1 != 0); size_t remoteCapacity = 0; if (wantRemote) { if (!a2) return STATUS_INVALID_PARAMETER; KSTATUS cs = CopyFromUser(&remoteCapacity, (const void*)(uintptr_t)a2, sizeof(remoteCapacity)); if (!KSUCCESS(cs)) return cs; if (remoteCapacity < sizeof(SYS_SOCKET_ADDRESS_K)) { size_t required = sizeof(SYS_SOCKET_ADDRESS_K); (void)CopyToUser((void*)(uintptr_t)a2, &required, sizeof(required)); return STATUS_BUFFER_TOO_SMALL; } cs = MmProbeUserWrite((void*)(uintptr_t)a1, sizeof(SYS_SOCKET_ADDRESS_K)); if (!KSUCCESS(cs)) return cs; } SOCKET_OBJECT* listener = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!listener) return STATUS_NOT_FOUND; 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; uint32_t h = 0; s = KiAllocHandleForCurrentWithRights(&accepted->Header, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE | HANDLE_RIGHT_QUERY, &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((void*)(uintptr_t)a1, &remote, sizeof(remote)); if (KSUCCESS(s)) s = CopyToUser((void*)(uintptr_t)a2, &actual, sizeof(actual)); } if (KSUCCESS(s)) s = KiCopyHandleToUser(a3, h); if (!KSUCCESS(s)) { PROCESS* proc = KeGetCurrentProcess(); if (proc) KeCloseHandle(proc, h); } return s; } // SYS_CONNECT_SOCKET a0=socketHandle a1=*SYS_SOCKET_ADDRESS a2=addressSize. static uint64_t SyscallConnect(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; if ((size_t)a2 < sizeof(SYS_SOCKET_ADDRESS_K)) return STATUS_BUFFER_TOO_SMALL; SYS_SOCKET_ADDRESS_K address; KSTATUS s = CopyFromUser(&address, (const void*)(uintptr_t)a1, sizeof(address)); if (!KSUCCESS(s)) return s; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; s = NetSocketConnect(socket, &address, (size_t)a2, (IO_ASYNC_REQUEST_OBJECT*)0); ObDereferenceObject(&socket->Header); return s; } // SYS_SEND_SOCKET a0=socketHandle a1=*buffer a2=length a3=flags a4=*sentOut(optional). static uint64_t SyscallSend(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; size_t length = (size_t)a2; if (length && !a1) return STATUS_INVALID_PARAMETER; if (length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (length == 0) { if (a4) return KiCopySizeToUser(a4, 0); return STATUS_SUCCESS; } void* kbuf = MmAllocatePoolTag(length, MM_POOL_TAG('s', 'y', 's', ' ')); if (!kbuf) return STATUS_NO_MEMORY; KSTATUS s = CopyFromUser(kbuf, (const void*)(uintptr_t)a1, length); if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; } SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t sent = 0; s = NetSocketSend(socket, kbuf, length, (uint32_t)a3, &sent); ObDereferenceObject(&socket->Header); MmFreePool(kbuf); if (a4) { KSTATUS cs = KiCopySizeToUser(a4, sent); if (KSUCCESS(s)) s = cs; } return s; } // SYS_RECV_SOCKET a0=socketHandle a1=*buffer a2=length a3=flags a4=*receivedOut(optional). static uint64_t SyscallRecv(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; size_t length = (size_t)a2; if (length && !a1) return STATUS_INVALID_PARAMETER; if (length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (length == 0) { if (a4) return KiCopySizeToUser(a4, 0); return STATUS_SUCCESS; } KSTATUS s = MmProbeUserWrite((void*)(uintptr_t)a1, length); if (!KSUCCESS(s)) return s; void* kbuf = MmAllocatePoolTag(length, MM_POOL_TAG('s', 'y', 's', ' ')); if (!kbuf) return STATUS_NO_MEMORY; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t received = 0; s = NetSocketReceive(socket, kbuf, length, (uint32_t)a3, &received, (IO_ASYNC_REQUEST_OBJECT*)0); ObDereferenceObject(&socket->Header); if (KSUCCESS(s) && received) s = CopyToUser((void*)(uintptr_t)a1, kbuf, received); MmFreePool(kbuf); if (KSUCCESS(s) && a4) s = KiCopySizeToUser(a4, received); return s; } // SYS_SHUTDOWN_SOCKET a0=socketHandle a1=SYS_SHUTDOWN_SOCKET_*. static uint64_t SyscallShutdown(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; KSTATUS s = NetSocketShutdown(socket, (uint32_t)a1); ObDereferenceObject(&socket->Header); return s; } // SYS_SEND_TO_SOCKET a0=socketHandle a1=*SYS_SOCKET_SEND_TO_REQUEST. static uint64_t SyscallSendTo(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_SEND_TO_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return STATUS_INVALID_PARAMETER; if (!req.RemoteAddress) return STATUS_INVALID_PARAMETER; if (req.RemoteAddressSize < sizeof(SYS_SOCKET_ADDRESS_K)) return STATUS_BUFFER_TOO_SMALL; if (req.Length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; SYS_SOCKET_ADDRESS_K remote; s = CopyFromUser(&remote, req.RemoteAddress, sizeof(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 STATUS_NO_MEMORY; s = CopyFromUser(kbuf, req.Buffer, req.Length); if (!KSUCCESS(s)) { MmFreePool(kbuf); return s; } } SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { if (kbuf) MmFreePool(kbuf); return STATUS_NOT_FOUND; } size_t sent = 0; s = NetSocketSendTo(socket, kbuf, req.Length, req.Flags, &remote, req.RemoteAddressSize, &sent); ObDereferenceObject(&socket->Header); if (kbuf) MmFreePool(kbuf); if (req.SentOut) { KSTATUS cs = CopyToUser(req.SentOut, &sent, sizeof(sent)); if (KSUCCESS(s)) s = cs; } return s; } // SYS_RECV_FROM_SOCKET a0=socketHandle a1=*SYS_SOCKET_RECV_FROM_REQUEST. static uint64_t SyscallRecvFrom(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_RECV_FROM_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (req.Length && !req.Buffer) return STATUS_INVALID_PARAMETER; if (req.Length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; bool wantRemote = (req.RemoteAddressOut != NULL); size_t remoteCapacity = 0; if (wantRemote) { if (!req.RemoteAddressSizeInOut) return STATUS_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 STATUS_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 STATUS_NO_MEMORY; } SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { if (kbuf) MmFreePool(kbuf); return STATUS_NOT_FOUND; } SYS_SOCKET_ADDRESS_K remote; memset(&remote, 0, sizeof(remote)); size_t received = 0; s = NetSocketReceiveFrom(socket, kbuf, req.Length, req.Flags, wantRemote ? &remote : NULL, &received, (IO_ASYNC_REQUEST_OBJECT*)0); ObDereferenceObject(&socket->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; } // SYS_RESOLVE_HOST a0=*SYS_RESOLVE_HOST_REQUEST. static uint64_t SyscallResolveHost(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; SYS_RESOLVE_HOST_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a0, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Name.Data || req.Name.Length == 0 || req.Name.Length > SYS_RESOLVE_HOST_NAME_MAX) return STATUS_INVALID_PARAMETER; if (req.Family != SYS_AF_INET && req.Family != SYS_AF_INET6) return STATUS_NOT_SUPPORTED; if (req.Flags != 0) return STATUS_NOT_SUPPORTED; if (!req.AddressOut || !req.AddressSizeInOut) return STATUS_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 STATUS_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)); } // SYS_SET_SOCKET_OPTION a0=socketHandle a1=*SYS_SOCKET_OPTION_REQUEST. static uint64_t SyscallSetSocketOption(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_OPTION_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Value) return STATUS_INVALID_PARAMETER; uint8_t value[sizeof(uint64_t)]; if (req.ValueSize > sizeof(value)) return STATUS_BUFFER_TOO_SMALL; s = CopyFromUser(value, req.Value, req.ValueSize); if (!KSUCCESS(s)) return s; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; s = NetSocketSetOption(socket, req.Option, value, req.ValueSize); ObDereferenceObject(&socket->Header); return s; } // SYS_GET_SOCKET_OPTION a0=socketHandle a1=*SYS_SOCKET_OPTION_REQUEST. static uint64_t SyscallGetSocketOption(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; if (!a1) return STATUS_INVALID_PARAMETER; SYS_SOCKET_OPTION_REQUEST_K req; KSTATUS s = CopyFromUser(&req, (const void*)(uintptr_t)a1, sizeof(req)); if (!KSUCCESS(s)) return s; if (!req.Value) return STATUS_INVALID_PARAMETER; uint8_t value[sizeof(uint64_t)]; if (req.ValueSize > sizeof(value)) return STATUS_BUFFER_TOO_SMALL; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject((uint32_t)a0, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; size_t actual = 0; s = NetSocketGetOption(socket, req.Option, value, req.ValueSize, &actual); ObDereferenceObject(&socket->Header); if (!KSUCCESS(s)) { if (s == STATUS_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 int KiPollDeadlineExpired(uint64_t start, uint32_t timeoutMs) { if (timeoutMs == 0) return 1; if (timeoutMs == SYS_POLL_TIMEOUT_INFINITE) return 0; if (!gHal) return 1; return (gHal->Timer.QueryTicks() - start) >= ((uint64_t)timeoutMs * 1000u); } // SYS_POLL a0=*SYS_POLLFD array a1=count a2=timeoutMs a3=*readyOut(optional). static uint64_t SyscallPoll(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; size_t count = (size_t)a1; if (count == 0 || count > SYS_POLL_MAX_FDS) return STATUS_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 STATUS_NO_MEMORY; KSTATUS s = CopyFromUser(fds, (const void*)(uintptr_t)a0, bytes); if (!KSUCCESS(s)) { MmFreePool(fds); return s; } uint32_t timeoutMs = (uint32_t)a2; uint64_t start = gHal ? gHal->Timer.QueryTicks() : 0; size_t ready = 0; for (;;) { ready = 0; for (size_t i = 0; i < count; i++) { fds[i].ReturnedEvents = 0; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(fds[i].Handle, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) { fds[i].ReturnedEvents = SYS_POLL_INVALID; ready++; continue; } uint32_t revents = 0; s = NetSocketPoll(socket, fds[i].Events, &revents); ObDereferenceObject(&socket->Header); 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((void*)(uintptr_t)a0, fds, bytes); if (KSUCCESS(s) && a3) s = KiCopySizeToUser(a3, 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 STATUS_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 STATUS_INVALID_PARAMETER; if (req->AddressSize < sizeof(SYS_SOCKET_ADDRESS_K)) return STATUS_BUFFER_TOO_SMALL; KSTATUS status = CopyFromUser(&submit->Address, req->Address, sizeof(submit->Address)); if (!KSUCCESS(status)) return status; submit->AddressSize = req->AddressSize; return STATUS_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 STATUS_INVALID_PARAMETER; if (!req->AddressOut) return STATUS_SUCCESS; if (!req->AddressSizeInOut) return STATUS_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 STATUS_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 STATUS_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 STATUS_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 STATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_ACCEPT: { if (!req->HandleOut) return STATUS_INVALID_PARAMETER; uint32_t zero = 0; 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 STATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return STATUS_INVALID_PARAMETER; if (req->Length) { submit->KernelBuffer = MmAllocatePoolTag(req->Length, MM_POOL_TAG('s', 'a', 's', 'n')); if (!submit->KernelBuffer) return STATUS_NO_MEMORY; return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length); } return STATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_RECV: if (req->Length && !req->Buffer) return STATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return STATUS_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 STATUS_NO_MEMORY; } submit->UserBuffer = req->Buffer; return STATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_SEND_TO: { if (req->Length && !req->Buffer) return STATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return STATUS_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 STATUS_NO_MEMORY; return CopyFromUser(submit->KernelBuffer, req->Buffer, req->Length); } return STATUS_SUCCESS; } case SYS_ASYNC_SOCKET_OP_RECV_FROM: { if (req->Length && !req->Buffer) return STATUS_INVALID_PARAMETER; if (req->Length > SYSCALL_IO_MAX_BYTES) return STATUS_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 STATUS_NO_MEMORY; } submit->UserBuffer = req->Buffer; return STATUS_SUCCESS; } case SYS_ASYNC_SOCKET_OP_SET_OPT: if (!req->OptionValue) return STATUS_INVALID_PARAMETER; if (req->OptionValueSize > sizeof(uint64_t)) return STATUS_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 STATUS_NO_MEMORY; return CopyFromUser(submit->OptionValue, req->OptionValue, req->OptionValueSize); } return STATUS_SUCCESS; case SYS_ASYNC_SOCKET_OP_GET_OPT: { if (!req->OptionValue) return STATUS_INVALID_PARAMETER; if (req->OptionValueSize > sizeof(uint64_t)) return STATUS_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 STATUS_NO_MEMORY; } return STATUS_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 STATUS_SUCCESS; } default: return STATUS_INVALID_PARAMETER; } } // SYS_SOCKET_IO_ASYNC a0=*SYS_ASYNC_SOCKET_REQUEST. static uint64_t SyscallSocketIoAsync(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; SYS_ASYNC_SOCKET_REQUEST_K req; KSTATUS status = CopyFromUser(&req, (const void*)(uintptr_t)a0, sizeof(req)); if (!KSUCCESS(status)) return status; if (req.Size != sizeof(req) || req.Flags != SYS_ASYNC_SOCKET_FLAG_NONE || !req.Iosb) return STATUS_INVALID_PARAMETER; if (req.RequestHandleOut) { status = MmProbeUserWrite(req.RequestHandleOut, sizeof(*req.RequestHandleOut)); if (!KSUCCESS(status)) return status; } PROCESS* proc = KeGetCurrentProcess(); if (!proc) return STATUS_FAILURE; SOCKET_OBJECT* socket = (SOCKET_OBJECT*)KiHandleToObject(req.SocketHandle, ObjectTypeSocket, HANDLE_RIGHT_READ | HANDLE_RIGHT_WRITE); if (!socket) return STATUS_NOT_FOUND; EVENT* event = (EVENT*)0; if (req.EventHandle) { event = (EVENT*)KiHandleToObject(req.EventHandle, ObjectTypeEvent, HANDLE_RIGHT_SIGNAL); if (!event) { ObDereferenceObject(&socket->Header); return STATUS_NOT_FOUND; } } 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 STATUS_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 STATUS_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 == STATUS_PENDING) { memset(&submit, 0, sizeof(submit)); } if (!KSUCCESS(status) && status != STATUS_PENDING) goto fail; if (req.RequestHandleOut && asyncReq) { uint32_t requestHandle = 0; KSTATUS hs = KeAllocateHandleWithRights(proc, NetAsyncSocketRequestObject(asyncReq), HANDLE_RIGHT_WAIT | HANDLE_RIGHT_QUERY | HANDLE_RIGHT_TERMINATE, &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) { uint32_t zeroHandle = 0; 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; } // SYS_GET_MEMORY_STATUS a0=*SYS_MEMORY_STATUS. static uint64_t SyscallGetMemoryStatus(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_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((void*)(uintptr_t)a0, &status, sizeof(status)); } // SYS_QUERY_KERNEL_STATS a0=classId a1=buffer a2=bufferSize a3=*requiredSizeOut. static KSTATUS KiReturnKernelStats(uint64_t bufferArg, size_t bufferSize, uint64_t requiredArg, const void* src, size_t size) { if (requiredArg) { KSTATUS s = KiCopySizeToUser(requiredArg, size); if (!KSUCCESS(s)) return s; } if (!bufferArg) return STATUS_SUCCESS; if (bufferSize < size) return STATUS_BUFFER_TOO_SMALL; return CopyToUser((void*)(uintptr_t)bufferArg, src, size); } static uint64_t SyscallQueryDisplayInfo(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; if (!a0) return STATUS_INVALID_PARAMETER; KE_DISPLAY_INFO ki; KSTATUS s = KeDisplayQueryInfo(&ki); if (!KSUCCESS(s)) return s; 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.ManagerStats = ki.ManagerStats; out.DeviceStats = ki.DeviceStats; return CopyToUser((void*)(uintptr_t)a0, &out, sizeof(out)); } static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; switch ((uint32_t)a0) { 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; return KiReturnKernelStats(a1, (size_t)a2, a3, &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(a1, (size_t)a2, a3, &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(a1, (size_t)a2, a3, &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(a1, (size_t)a2, a3, &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(a1, (size_t)a2, a3, &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.ReadAheadRequests = km.ReadAheadRequests; um.ReadAheadPagesRequested = km.ReadAheadPagesRequested; um.ReadAheadPagesQueued = km.ReadAheadPagesQueued; um.ReadAheadPagesLoaded = km.ReadAheadPagesLoaded; um.ReadAheadPagesSkipped = km.ReadAheadPagesSkipped; um.ReadAheadFailures = km.ReadAheadFailures; um.ReadAheadPagesThrottled = km.ReadAheadPagesThrottled; um.MaxReadAheadPagesPerRequest = km.MaxReadAheadPagesPerRequest; um.BatchReads = km.BatchReads; um.BatchPagesRequested = km.BatchPagesRequested; um.BatchPagesLoaded = km.BatchPagesLoaded; um.MaxBatchPagesRequested = km.MaxBatchPagesRequested; um.MaxBatchPagesLoaded = km.MaxBatchPagesLoaded; um.ResidentPages = km.ResidentPages; um.ActivePages = km.ActivePages; um.StandbyPages = km.StandbyPages; um.MaxResidentPages = km.MaxResidentPages; return KiReturnKernelStats(a1, (size_t)a2, a3, &um, sizeof(um)); } case SYS_KERNEL_STATS_SURFACE: { SYS_SURFACE_STATS stats; memset(&stats, 0, sizeof(stats)); KiSurfaceStatsQuery(&stats); return KiReturnKernelStats(a1, (size_t)a2, a3, &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(a1, (size_t)a2, a3, &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(a1, (size_t)a2, a3, &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; um.ReadaheadHits = km.ReadaheadHits; um.ReadaheadMisses = km.ReadaheadMisses; um.ReadaheadStores = km.ReadaheadStores; um.ReadaheadInvalidations = km.ReadaheadInvalidations; um.ReadaheadBytesServed = km.ReadaheadBytesServed; um.ReadaheadSectorsLoaded = km.ReadaheadSectorsLoaded; um.ReadaheadSequentialDetections = km.ReadaheadSequentialDetections; um.ReadaheadSuppressed = km.ReadaheadSuppressed; um.ReadaheadAllocFailures = km.ReadaheadAllocFailures; um.ReadaheadReadFailures = km.ReadaheadReadFailures; um.MaxReadaheadSectors = km.MaxReadaheadSectors; return KiReturnKernelStats(a1, (size_t)a2, a3, &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(a1, (size_t)a2, a3, &um, sizeof(um)); } 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->LapicId = cpu->LapicId; if (cpu->Present) out->Flags |= SYS_CPU_FLAG_PRESENT; if (cpu->Started) out->Flags |= SYS_CPU_FLAG_STARTED; if (cpu->Parked) out->Flags |= SYS_CPU_FLAG_PARKED; if (cpu->Online) out->Flags |= SYS_CPU_FLAG_ONLINE; if (cpu->IsBsp) out->Flags |= SYS_CPU_FLAG_BSP; 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(a1, (size_t)a2, a3, &stats, size); } default: return STATUS_INVALID_PARAMETER; } } // Dispatch table keyed by SYS_* constants. typedef uint64_t (*SYSCALL_HANDLER)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t); static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = { // Process, threads, modules, waits, and APC. [SYS_EXIT_PROCESS] = SyscallExitProcess, [SYS_EXIT_THREAD] = SyscallExitThread, [SYS_CREATE_PROCESS] = SyscallCreateProcess, [SYS_OPEN_PROCESS] = SyscallOpenProcess, [SYS_GET_PROCESS_EXIT_CODE] = SyscallGetProcessExitCode, [SYS_KILL_PROCESS] = SyscallKillProcess, [SYS_GET_PROCESS_LIST] = SyscallGetProcessList, [SYS_GET_PROCESS_PID] = SyscallGetProcessPid, [SYS_CREATE_THREAD] = SyscallCreateThread, [SYS_GET_THREAD_LIST] = SyscallGetThreadList, [SYS_LOAD_MODULE] = SyscallLoadModule, [SYS_UNLOAD_MODULE] = SyscallUnloadModule, [SYS_GET_MODULE_INFO] = SyscallGetModuleInfo, [SYS_GET_MODULE_EXPORT] = SyscallGetModuleExport, [SYS_GET_MODULE_LIST] = SyscallGetModuleList, [SYS_GET_CURRENT_TID] = SyscallGetCurrentTid, [SYS_GET_CURRENT_PID] = SyscallGetCurrentPid, [SYS_SET_THREAD_PRIORITY] = SyscallSetThreadPriority, [SYS_YIELD] = SyscallYield, [SYS_SLEEP] = SyscallSleep, [SYS_WAIT_SINGLE] = SyscallWaitSingle, [SYS_WAIT_MANY] = SyscallWaitMany, [SYS_SET_USER_APC_DISPATCHER] = SyscallSetUserApcDispatcher, [SYS_APC_RETURN] = SyscallApcReturn, // Virtual memory, mappings, and handles. [SYS_ALLOC_VM] = SyscallVmAlloc, [SYS_FREE_VM] = SyscallVmFree, [SYS_PROTECT_VM] = SyscallVmProtect, [SYS_QUERY_VM] = SyscallVmQuery, [SYS_CLOSE_HANDLE] = SyscallCloseHandle, [SYS_DUPLICATE_HANDLE] = SyscallDuplicateHandle, [SYS_CREATE_FILE_MAPPING] = SyscallCreateFileMapping, [SYS_MAP_FILE] = SyscallMapFile, // Synchronization objects. [SYS_CREATE_MUTEX] = SyscallMutexCreate, [SYS_TRY_ACQUIRE_MUTEX] = SyscallMutexTryAcquire, [SYS_RELEASE_MUTEX] = SyscallMutexRelease, [SYS_CREATE_SEM] = SyscallSemCreate, [SYS_TRY_WAIT_SEM] = SyscallSemTryWait, [SYS_SIGNAL_SEM] = SyscallSemSignal, [SYS_CREATE_EVENT] = SyscallEventCreate, [SYS_SIGNAL_EVENT] = SyscallEventSignal, [SYS_CLEAR_EVENT] = SyscallEventClear, // Queues and named queues. [SYS_CREATE_QUEUE] = SyscallQueueCreate, [SYS_SEND_QUEUE] = SyscallQueueSend, [SYS_RECV_QUEUE] = SyscallQueueRecv, [SYS_PUBLISH_QUEUE] = SyscallPublishQueue, [SYS_OPEN_NAMED_QUEUE] = SyscallOpenNamedQueue, [SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS] = SyscallOpenNamedQueueOwnerProcess, // Files, directories, and current directory. [SYS_OPEN_FILE] = SyscallOpenFile, [SYS_READ_FILE] = SyscallReadFile, [SYS_WRITE_FILE] = SyscallWriteFile, [SYS_READ_FILE_ASYNC] = SyscallReadFileAsync, [SYS_WRITE_FILE_ASYNC] = SyscallWriteFileAsync, [SYS_CANCEL_IO] = SyscallCancelIo, [SYS_SEEK_FILE] = SyscallSeekFile, [SYS_GET_FILE_POS] = SyscallGetFilePos, [SYS_GET_FILE_SIZE] = SyscallGetFileSize, [SYS_GET_FILE_STAT] = SyscallGetFileStat, [SYS_STAT_FILE] = SyscallStatFile, [SYS_TRUNCATE_FILE] = SyscallTruncateFile, [SYS_FLUSH_FILE] = SyscallFlushFile, [SYS_DELETE_FILE] = SyscallDeleteFile, [SYS_RENAME_FILE] = SyscallRenameFile, [SYS_LOCK_FILE] = SyscallLockFile, [SYS_UNLOCK_FILE] = SyscallUnlockFile, [SYS_OPEN_DIR] = SyscallOpenDir, [SYS_READ_DIR] = SyscallReadDir, [SYS_CREATE_DIR] = SyscallCreateDir, [SYS_REMOVE_DIR] = SyscallRemoveDir, [SYS_SET_CWD] = SyscallSetCwd, [SYS_REAL_PATH] = SyscallRealPath, // Console, display, and surfaces. [SYS_WRITE_CONSOLE] = SyscallWriteConsole, [SYS_GET_CONSOLE_INFO] = SyscallGetConsoleInfo, [SYS_SET_CONSOLE_CURSOR] = SyscallSetConsoleCursor, [SYS_FILL_CONSOLE_CHARACTER] = SyscallFillConsoleCharacter, [SYS_FILL_CONSOLE_ATTRIBUTE] = SyscallFillConsoleAttribute, [SYS_PUT_SCREEN_CHAR] = SyscallPutScreenChar, [SYS_CLEAR_SCREEN] = SyscallClearScreen, [SYS_GET_SCREEN_SIZE] = SyscallGetScreenSize, [SYS_QUERY_DISPLAY_INFO] = SyscallQueryDisplayInfo, [SYS_CREATE_SURFACE] = SyscallCreateSurface, [SYS_MAP_SURFACE] = SyscallMapSurface, [SYS_QUERY_SURFACE] = SyscallQuerySurface, [SYS_PRESENT_SURFACE] = SyscallPresentSurface, // Input. [SYS_POLL_KEYBOARD] = SyscallKbPoll, [SYS_IS_KEY_DOWN] = SyscallKbIsKeyDown, [SYS_READ_KEYBOARD_CHAR] = SyscallKbReadChar, [SYS_READ_KEY] = SyscallKbReadKey, [SYS_GET_KEY_MODIFIERS] = SyscallKbGetModifiers, [SYS_OPEN_INPUT_QUEUE] = SyscallOpenInputQueue, [SYS_READ_INPUT_EVENT] = SyscallKbReadInputEvent, [SYS_PEEK_INPUT_EVENT] = SyscallKbPeekInputEvent, [SYS_PEEK_MOUSE] = SyscallMousePeek, [SYS_READ_MOUSE] = SyscallMouseRead, [SYS_GET_MOUSE_POS] = SyscallMouseGetPos, // Time, debug, and system information. [SYS_CREATE_TIMER] = SyscallCreateTimer, [SYS_SET_TIMER] = SyscallSetTimer, [SYS_CANCEL_TIMER] = SyscallCancelTimer, [SYS_GET_TICK] = SyscallGetTick, [SYS_GET_TICK_FREQUENCY] = SyscallGetTickFrequency, [SYS_GET_SYSTEM_TIME] = SyscallGetSystemTime, [SYS_PRINT_DEBUG] = SyscallDebugPrint, [SYS_QUERY_TRAP_FRAME] = SyscallQueryTrapFrame, [SYS_GET_MEMORY_STATUS] = SyscallGetMemoryStatus, [SYS_QUERY_KERNEL_STATS] = SyscallQueryKernelStats, // Native sockets and polling. [SYS_CREATE_SOCKET] = SyscallSocket, [SYS_SOCKET_IO_ASYNC] = SyscallSocketIoAsync, [SYS_BIND_SOCKET] = SyscallBind, [SYS_LISTEN_SOCKET] = SyscallListen, [SYS_ACCEPT_SOCKET] = SyscallAccept, [SYS_CONNECT_SOCKET] = SyscallConnect, [SYS_SEND_SOCKET] = SyscallSend, [SYS_RECV_SOCKET] = SyscallRecv, [SYS_SHUTDOWN_SOCKET] = SyscallShutdown, [SYS_SEND_TO_SOCKET] = SyscallSendTo, [SYS_RECV_FROM_SOCKET] = SyscallRecvFrom, [SYS_RESOLVE_HOST] = SyscallResolveHost, [SYS_SET_SOCKET_OPTION] = SyscallSetSocketOption, [SYS_GET_SOCKET_OPTION] = SyscallGetSocketOption, [SYS_POLL] = SyscallPoll, }; // KeSyscallDispatch is entered from ke_syscall.asm after the assembly stub has reordered registers into the C calling convention. uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { KiValidateSyscallTrapFrame(nr, a0, a1, a2, a3, a4, a5); THREAD* self = KeGetCurrentThread(); PROCESS* owner = self ? self->Owner : (PROCESS*)0; if (owner && KeIsProcessTerminating(owner) && nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) { int32_t exitCode = owner->ExitCode; KeExitThread(self, exitCode); while (true) ; } if (nr >= SYS_MAX || !gSyscallTable[nr]) return STATUS_NOT_SUPPORTED; uint64_t result = gSyscallTable[nr](a0, a1, a2, a3, a4, a5); // 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) && nr != SYS_EXIT_PROCESS && nr != SYS_EXIT_THREAD) { int32_t exitCode = owner->ExitCode; KeExitThread(self, exitCode); while (true) ; } return result; }