5236 lines
193 KiB
C
5236 lines
193 KiB
C
// 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 <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <kernel/ke_thread.h>
|
|
#include <kernel/ke_cpu.h>
|
|
#include <kernel/ke_sched.h>
|
|
#include <kernel/ke_process.h>
|
|
#include <kernel/ke_mutex.h>
|
|
#include <kernel/ke_semaphore.h>
|
|
#include <kernel/ke_event.h>
|
|
#include <kernel/ke_apc.h>
|
|
#include <kernel/ke_queue.h>
|
|
#include <kernel/ke_timer.h>
|
|
#include <kernel/ke_spinlock.h>
|
|
#include <kernel/ke_debug.h>
|
|
|
|
#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 <kernel/mm_virt.h>
|
|
#include <kernel/mm_swap.h>
|
|
#include <kernel/mm_file_cache.h>
|
|
#include <kernel/mm_pfn.h>
|
|
#include <kernel/mm_pte.h>
|
|
#include <kernel/ldr_image.h>
|
|
#include <kernel/syscall_nr.h>
|
|
#include <ntlite/ddi/hal_api.h>
|
|
#include <kernel/io_file.h>
|
|
#include <kernel/io_async.h>
|
|
#include <ntlite/ddi/io_driver.h>
|
|
#include <kernel/net_socket.h>
|
|
#include <ntlite/ddi/net_stack.h>
|
|
#include <kernel/ke_trap.h>
|
|
#include <kernel/ke_input.h>
|
|
#include <kernel/ke_display.h>
|
|
#include <kernel/ke_time.h>
|
|
#include <ntlite/syscall.h>
|
|
|
|
// 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;
|
|
}
|