Files
nt-lite/code/win32/kernel32.c
T
2026-05-04 09:17:00 +02:00

1033 lines
40 KiB
C

// kernel32.c - Win32 personality layer
//
// Thin wrappers around syscall.dll stubs that expose a familiar Win32 API
// surface to user-mode code originally written for Windows.
//
// Design notes:
// - HANDLE is void*; we store the uint32_t kernel handle as a pointer value
// so Win32 code can pass it around opaquely without knowing its size.
// HANDLE_TO_KH / KH_TO_HANDLE do that conversion in one place.
// - INVALID_HANDLE_VALUE maps to kernel handle 0 (the always-invalid slot).
// - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and
// always use the single kernel user-heap. This is fine: we don't expose
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - GetLastError / SetLastError are per-thread on Windows. We keep a single
// global for now; enough for Bink and similar single-threaded guests.
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE /
// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
// SysCloseFile).
// - WriteFile / CreateFileA in write-mode / SetEndOfFile are stubs because
// the VFS has no write path yet.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
// ---------------------------------------------------------------------------
// Syscall stubs (from syscall.dll, assembled from syscall_stubs.asm)
// ---------------------------------------------------------------------------
extern void* SysAlloc(size_t size);
extern void SysFree(void* ptr, size_t size);
extern void* SysRealloc(void* ptr, size_t newSize);
extern uint32_t SysOpenFile(const char* path, uint32_t* handleOut);
extern uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesReadOut);
extern uint32_t SysSeekFile(uint32_t handle, uint64_t offset);
extern uint64_t SysGetFileSize(uint32_t handle);
extern void SysCloseHandle(uint32_t handle);
extern void SysExitProcess(int exitCode);
extern void SysExitThread(int exitCode);
extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg,
uint32_t priority, uint32_t* tidOut);
extern uint32_t SysGetCurrentTid(void);
extern uint32_t SysGetCurrentPid(void);
extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut);
extern uint32_t SysWaitForProcess(uint32_t handle);
extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
extern char SysKbReadChar(void);
extern uint32_t SysKbPoll(void);
extern void SysYield(void);
extern void SysSleep(uint32_t ms);
extern uint64_t SysGetTick(void);
extern void SysDebugPrint(const char* str);
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
typedef void* HANDLE;
typedef unsigned long DWORD;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
#define INVALID_HANDLE_VALUE ((HANDLE)(uintptr_t)0)
// CreateFileA disposition / access flags (subset used by Bink and similar)
#define GENERIC_READ 0x80000000UL
#define GENERIC_WRITE 0x40000000UL
#define OPEN_EXISTING 3
#define CREATE_ALWAYS 2
#define FILE_BEGIN 0
#define FILE_CURRENT 1
#define FILE_END 2
// HeapAlloc flags
#define HEAP_ZERO_MEMORY 0x00000008UL
// ---------------------------------------------------------------------------
// Handle conversion helpers
// uint32_t kernel handle <-> opaque Win32 HANDLE
// Kernel handle 0 == invalid; we map that to INVALID_HANDLE_VALUE (NULL).
// ---------------------------------------------------------------------------
static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; }
static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; }
// ---------------------------------------------------------------------------
// GetLastError / SetLastError (single-threaded stub)
// ---------------------------------------------------------------------------
static DWORD gLastError = 0;
__declspec(dllexport) DWORD GetLastError(void) {
return gLastError;
}
__declspec(dllexport) void SetLastError(DWORD err) {
gLastError = err;
}
// ---------------------------------------------------------------------------
// Process termination
// ---------------------------------------------------------------------------
__declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
SysExitProcess((int)code);
while (true) ; // unreachable
}
// ---------------------------------------------------------------------------
// Debug output
// ---------------------------------------------------------------------------
__declspec(dllexport) void OutputDebugStringA(const char* str) {
if (str) SysDebugPrint(str);
}
// ---------------------------------------------------------------------------
// Timer
// ---------------------------------------------------------------------------
__declspec(dllexport) DWORD GetTickCount(void) {
return (DWORD)SysGetTick();
}
// ---------------------------------------------------------------------------
// Memory (heap argument is ignored - single kernel user-heap)
// ---------------------------------------------------------------------------
__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
(void)heap;
if (!size) return NULL;
void* ptr = SysAlloc(size);
if (ptr && (flags & HEAP_ZERO_MEMORY))
memset(ptr, 0, size);
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return ptr;
}
__declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) {
(void)heap; (void)flags;
// Win32's HeapFree doesn't need the size; we pass 0 and the kernel's
// allocator handles it (the block header carries the size).
if (ptr) SysFree(ptr, 0);
return TRUE;
}
__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags,
void* ptr, size_t size) {
(void)heap; (void)flags;
void* newptr = SysRealloc(ptr, size);
if (!newptr && size) gLastError = 8;
return newptr;
}
// GetProcessHeap - return a sentinel non-NULL value so callers can pass it
// back into HeapAlloc/HeapFree without crashing (we ignore it anyway).
__declspec(dllexport) void* GetProcessHeap(void) {
return (void*)1;
}
// ---------------------------------------------------------------------------
// Locale / code-page stubs
// ---------------------------------------------------------------------------
__declspec(dllexport) int MultiByteToWideChar(unsigned int cp, DWORD flags,
const char* src, int srcLen,
unsigned short* dst, int dstLen) {
(void)cp; (void)flags;
// Trivial ASCII -> UTF-16LE expansion, good enough for English strings.
if (!src || !dstLen) return 0;
if (srcLen < 0) srcLen = (int)strlen(src) + 1;
int out = 0;
while (out < dstLen && out < srcLen) {
if (dst) dst[out] = (unsigned short)(unsigned char)src[out];
out++;
}
return out;
}
__declspec(dllexport) int WideCharToMultiByte(unsigned int cp, DWORD flags,
const unsigned short* src, int srcLen,
char* dst, int dstLen,
const char* defChar, int* usedDefault) {
(void)cp; (void)flags; (void)defChar;
if (usedDefault) *usedDefault = FALSE;
if (!src || !dstLen) return 0;
if (srcLen < 0) {
srcLen = 0;
while (src[srcLen]) srcLen++;
srcLen++;
}
int out = 0;
while (out < dstLen && out < srcLen) {
if (dst) dst[out] = (char)(src[out] < 128 ? src[out] : '?');
out++;
}
return out;
}
__declspec(dllexport) DWORD GetACP(void) { return 1252; } // Windows-1252
__declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US
// ---------------------------------------------------------------------------
// File I/O
// ---------------------------------------------------------------------------
// CreateFileA - open an existing file for reading.
// Write access, CREATE_ALWAYS and other modes return INVALID_HANDLE_VALUE
// because the VFS has no write path yet.
__declspec(dllexport) HANDLE CreateFileA(const char* name,
DWORD access, DWORD share,
void* sa, DWORD disposition,
DWORD flags, void* templ) {
(void)share; (void)sa; (void)flags; (void)templ;
// Only OPEN_EXISTING + read supported for now.
if (disposition != OPEN_EXISTING || (access & GENERIC_WRITE)) {
gLastError = 5; // ERROR_ACCESS_DENIED
return INVALID_HANDLE_VALUE;
}
uint32_t kh = 0;
uint32_t s = SysOpenFile(name, &kh);
if (s != 0 /* STATUS_SUCCESS */) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return INVALID_HANDLE_VALUE;
}
return KH_TO_HANDLE(kh);
}
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile) { gLastError = 6; return 0xFFFFFFFFUL; } // ERROR_INVALID_HANDLE
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
}
// ReadFile - sequential read at the file's current position.
// Overlapped is not supported (must be NULL).
__declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) {
(void)overlapped;
if (!hFile || !buf) { gLastError = 6; return FALSE; }
size_t got = 0;
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
if (bytesRead) *bytesRead = (DWORD)got;
if (s != 0) { gLastError = 1; return FALSE; } // ERROR_INVALID_FUNCTION
return TRUE;
}
// SetFilePointer - seek to an absolute or relative position.
// Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) {
if (!hFile) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t offset = 0;
if (method == FILE_BEGIN) {
offset = (uint64_t)(int64_t)dist;
if (distHigh) offset |= (uint64_t)(uint32_t)*distHigh << 32;
} else if (method == FILE_CURRENT || method == FILE_END) {
// FILE_CURRENT and FILE_END require knowing the current offset or
// the file size; expose as unsupported for now.
gLastError = 1; // ERROR_INVALID_FUNCTION
return 0xFFFFFFFFUL;
} else {
gLastError = 87; // ERROR_INVALID_PARAMETER
return 0xFFFFFFFFUL;
}
uint32_t s = SysSeekFile(HANDLE_TO_KH(hFile), offset);
if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; }
if (distHigh) *distHigh = (long)(offset >> 32);
return (DWORD)(offset & 0xFFFFFFFFULL);
}
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject) { gLastError = 6; return FALSE; }
SysCloseHandle(HANDLE_TO_KH(hObject));
return TRUE;
}
// WriteFile stub - no write VFS support yet.
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)hFile; (void)buf; (void)toWrite; (void)overlapped;
if (bytesWritten) *bytesWritten = 0;
gLastError = 1; // ERROR_INVALID_FUNCTION
return FALSE;
}
// ===========================================================================
// Extended shims - funcs.txt additions
// All functions here return plausible values; no real implementation exists
// unless a matching syscall stub is available.
// ===========================================================================
// ---------------------------------------------------------------------------
// Additional Heap APIs
// We have one kernel heap; the HANDLE is always ignored.
// ---------------------------------------------------------------------------
typedef int HEAP_INFORMATION_CLASS; // opaque
__declspec(dllexport) HANDLE HeapCreate(DWORD flOptions,
size_t dwInitialSize,
size_t dwMaximumSize) {
(void)flOptions; (void)dwInitialSize; (void)dwMaximumSize;
return (HANDLE)(uintptr_t)1; // non-NULL sentinel
}
__declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
(void)hHeap;
return TRUE;
}
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
(void)heap; (void)flags; (void)ptr;
return (size_t)-1; // Win32 returns (SIZE_T)-1 on error / unsupported
}
__declspec(dllexport) BOOL HeapSetInformation(HANDLE HeapHandle,
HEAP_INFORMATION_CLASS cls,
void* info, size_t infoLen) {
(void)HeapHandle; (void)cls; (void)info; (void)infoLen;
return TRUE;
}
// ---------------------------------------------------------------------------
// Process & Module pseudo-handles / queries
// ---------------------------------------------------------------------------
// Pseudo-handles matching Windows convention: -1 = process, -2 = thread.
__declspec(dllexport) HANDLE GetCurrentProcess(void) {
return (HANDLE)(uintptr_t)(~(uintptr_t)0);
}
__declspec(dllexport) HANDLE GetCurrentThread(void) {
return (HANDLE)(uintptr_t)(~(uintptr_t)1);
}
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
// GetModuleHandleA/W - no PE map; return NULL so callers that check can fail
// gracefully. GetModuleHandleA(NULL) on Windows returns the exe base; we
// return a recognisable non-NULL sentinel so code that just passes it to
// GetProcAddress doesn't immediately crash.
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL;
}
__declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName) {
return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL;
}
// GetProcAddress - no dynamic linker; always fail.
__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) {
(void)hModule; (void)lpProcName;
gLastError = 127; // ERROR_PROC_NOT_FOUND
return NULL;
}
// LoadLibraryA - no dynamic loader; always fail.
__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
(void)lpLibFileName;
gLastError = 2; // ERROR_FILE_NOT_FOUND
return NULL;
}
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
(void)hLibModule;
return TRUE;
}
__declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
char* lpFilename, DWORD nSize) {
(void)hModule;
if (lpFilename && nSize) lpFilename[0] = '\0';
return 0;
}
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
(void)hProcess;
SysExitProcess((int)uExitCode);
while (true) ;
}
// STARTUPINFOA - only cb matters for our stub.
typedef struct {
DWORD cb;
char* lpReserved;
char* lpDesktop;
char* lpTitle;
DWORD dwX, dwY, dwXSize, dwYSize;
DWORD dwXCountChars, dwYCountChars;
DWORD dwFillAttribute, dwFlags;
unsigned short wShowWindow, cbReserved2;
unsigned char* lpReserved2;
HANDLE hStdInput, hStdOutput, hStdError;
} STARTUPINFOA;
__declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
if (!lpStartupInfo) return;
// Zero every field, then set the mandatory cb.
unsigned char* p = (unsigned char*)lpStartupInfo;
for (size_t i = 0; i < sizeof(*lpStartupInfo); i++) p[i] = 0;
lpStartupInfo->cb = (DWORD)sizeof(*lpStartupInfo);
}
__declspec(dllexport) char* GetCommandLineA(void) {
// Return a valid but empty-ish command line.
static char cmdline[4] = "";
return cmdline;
}
// ---------------------------------------------------------------------------
// Threading
// ---------------------------------------------------------------------------
#define WAIT_OBJECT_0 0x00000000UL
#define WAIT_TIMEOUT 0x00000102UL
#define INFINITE 0xFFFFFFFFUL
__declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
size_t dwStackSize,
void* lpStartAddress,
void* lpParameter,
DWORD dwCreationFlags,
DWORD* lpThreadId) {
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)(uintptr_t)lpStartAddress,
(uint64_t)(uintptr_t)lpParameter,
0 /* normal priority */, &tid);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid;
return KH_TO_HANDLE(tid);
}
// WaitForSingleObject - blocks on process handles via SysWaitForProcess;
// thread and other handle types are not yet waitable so return immediately.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
(void)dwMilliseconds;
uint32_t kh = HANDLE_TO_KH(hHandle);
if (kh) SysWaitForProcess(kh); // no-op KSTATUS if not a process handle
return WAIT_OBJECT_0;
}
// ---------------------------------------------------------------------------
// Process creation
// ---------------------------------------------------------------------------
typedef struct {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
#define STILL_ACTIVE 259UL
__declspec(dllexport) BOOL CreateProcessA(
const char* lpApplicationName,
char* lpCommandLine,
void* lpProcessAttributes,
void* lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
void* lpEnvironment,
const char* lpCurrentDirectory,
STARTUPINFOA* lpStartupInfo,
PROCESS_INFORMATION* lpProcessInformation) {
(void)lpCommandLine; (void)lpProcessAttributes; (void)lpThreadAttributes;
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
(void)lpCurrentDirectory; (void)lpStartupInfo;
if (!lpApplicationName || !lpProcessInformation) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return FALSE;
}
uint32_t kh = 0;
uint32_t s = SysCreateProcess(lpApplicationName, &kh);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND / generic launch failure
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
lpProcessInformation->dwProcessId = 0;
lpProcessInformation->dwThreadId = 0;
return TRUE;
}
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
if (!lpExitCode) { gLastError = 87; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hProcess);
if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
int code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
*lpExitCode = (DWORD)code;
return TRUE;
}
__declspec(dllexport) DWORD ResumeThread(HANDLE hThread) {
(void)hThread;
return 0; // previous suspend count
}
#define THREAD_PRIORITY_NORMAL 0
__declspec(dllexport) BOOL SetThreadPriority(HANDLE hThread, int nPriority) {
(void)hThread; (void)nPriority;
return TRUE;
}
__declspec(dllexport) int GetThreadPriority(HANDLE hThread) {
(void)hThread;
return THREAD_PRIORITY_NORMAL;
}
__declspec(dllexport) uintptr_t SetThreadAffinityMask(HANDLE hThread,
uintptr_t dwThreadAffinityMask) {
(void)hThread; (void)dwThreadAffinityMask;
return 1; // previous mask: CPU 0 only
}
__declspec(dllexport) void Sleep(DWORD dwMilliseconds) {
SysSleep(dwMilliseconds);
}
// ---------------------------------------------------------------------------
// Console I/O
// ---------------------------------------------------------------------------
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
// We have no real console handles; return a sentinel the other APIs recognise.
__declspec(dllexport) HANDLE GetStdHandle(DWORD nStdHandle) {
return (HANDLE)(uintptr_t)nStdHandle;
}
// ReadConsoleA: blocking read of up to nNumberOfCharsToRead characters.
// Each character is read individually via SysKbReadChar so the caller can
// do its own line-editing (echo, backspace, etc.) one char at a time.
__declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
void* lpBuffer,
DWORD nNumberOfCharsToRead,
DWORD* lpNumberOfCharsRead,
void* pInputControl) {
(void)hConsoleInput; (void)pInputControl;
if (!lpBuffer || nNumberOfCharsToRead == 0) return FALSE;
char* buf = (char*)lpBuffer;
DWORD i = 0; // Notice we switched to a while loop
while (i < nNumberOfCharsToRead) {
char c = SysKbReadChar();
// Ignore unmapped keys (Shift, Ctrl, etc.)
if (c == 0) continue;
// Deliver the raw character; the caller handles echo and line editing.
buf[i] = c;
i++;
// Stop reading when the user presses Enter
if (c == '\n' || c == '\r') {
// If your shell expects strings to be \n terminated, leave this.
// If your shell expects \0 termination without a newline,
// you might want to replace buf[i-1] with \0 here!
break;
}
}
if (lpNumberOfCharsRead) *lpNumberOfCharsRead = i;
return TRUE;
}
// ---------------------------------------------------------------------------
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
return (HANDLE)(uintptr_t)0x4D555458UL; // 'MUTX' sentinel
}
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
(void)hMutex;
return TRUE;
}
__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
long lInitialCount, long lMaximumCount,
const char* lpName) {
(void)lpSemaphoreAttributes; (void)lInitialCount;
(void)lMaximumCount; (void)lpName;
return (HANDLE)(uintptr_t)0x53454D41UL; // 'SEMA' sentinel
}
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
long* lpPreviousCount) {
(void)hSemaphore; (void)lReleaseCount;
if (lpPreviousCount) *lpPreviousCount = 0;
return TRUE;
}
// ---------------------------------------------------------------------------
// Critical Sections & Condition Variables
// Single-threaded: all locks are no-ops.
// ---------------------------------------------------------------------------
// Minimal opaque types; callers allocate these from their Windows headers -
// we just need the pointer to be accepted by our stubs.
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS;
}
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS,
DWORD dwSpinCount) {
(void)lpCS; (void)dwSpinCount;
return TRUE;
}
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS; return TRUE;
}
__declspec(dllexport) void InitializeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
__declspec(dllexport) BOOL SleepConditionVariableCS(CONDITION_VARIABLE* cv,
CRITICAL_SECTION* cs,
DWORD dwMilliseconds) {
(void)cv; (void)cs; (void)dwMilliseconds;
return TRUE;
}
__declspec(dllexport) void WakeConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
__declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (void)cv; }
// ---------------------------------------------------------------------------
// Timing & Performance Counters
// We report a 1 MHz counter so QPC / QPF is consistent with GetTickCount.
// ---------------------------------------------------------------------------
typedef union {
struct { DWORD LowPart; long HighPart; } u;
long long QuadPart;
} LARGE_INTEGER;
#define QPC_FREQUENCY 1000000LL // 1 MHz
__declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency) {
if (lpFrequency) lpFrequency->QuadPart = QPC_FREQUENCY;
return TRUE;
}
__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) {
// SysGetTick returns milliseconds; scale to microseconds for 1 MHz QPC.
if (lpPerformanceCount)
lpPerformanceCount->QuadPart = (long long)SysGetTick() * 1000LL;
return TRUE;
}
__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) {
// Return a fixed FILETIME representing 2024-01-01 00:00:00 UTC.
// FILETIME = 100-nanosecond intervals since 1601-01-01.
// 2024-01-01 offset from 1601 in 100ns units: 133186080000000000
if (!lpSystemTimeAsFileTime) return;
unsigned long long ft = 133186080000000000ULL
+ (unsigned long long)SysGetTick() * 10000ULL;
unsigned char* p = (unsigned char*)lpSystemTimeAsFileTime;
for (int i = 0; i < 8; i++) p[i] = (unsigned char)(ft >> (i * 8));
}
// ---------------------------------------------------------------------------
// System Information
// ---------------------------------------------------------------------------
typedef struct {
union {
DWORD dwOemId;
struct { unsigned short wProcessorArchitecture;
unsigned short wReserved; };
};
DWORD dwPageSize;
void* lpMinimumApplicationAddress;
void* lpMaximumApplicationAddress;
uintptr_t dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
unsigned short wProcessorLevel;
unsigned short wProcessorRevision;
} SYSTEM_INFO;
__declspec(dllexport) void GetSystemInfo(SYSTEM_INFO* lpSystemInfo) {
if (!lpSystemInfo) return;
unsigned char* p = (unsigned char*)lpSystemInfo;
for (size_t i = 0; i < sizeof(*lpSystemInfo); i++) p[i] = 0;
lpSystemInfo->wProcessorArchitecture = 9; // PROCESSOR_ARCHITECTURE_AMD64
lpSystemInfo->dwPageSize = 4096;
lpSystemInfo->lpMinimumApplicationAddress = (void*)0x10000;
lpSystemInfo->lpMaximumApplicationAddress = (void*)0x7FFFFFFEFFFFull;
lpSystemInfo->dwActiveProcessorMask = 1;
lpSystemInfo->dwNumberOfProcessors = 1;
lpSystemInfo->dwProcessorType = 8664; // PROCESSOR_AMD_X8664
lpSystemInfo->dwAllocationGranularity= 65536;
lpSystemInfo->wProcessorLevel = 6;
lpSystemInfo->wProcessorRevision = 0;
}
typedef struct {
DWORD dwLength;
DWORD dwMemoryLoad;
unsigned long long ullTotalPhys;
unsigned long long ullAvailPhys;
unsigned long long ullTotalPageFile;
unsigned long long ullAvailPageFile;
unsigned long long ullTotalVirtual;
unsigned long long ullAvailVirtual;
unsigned long long ullAvailExtendedVirtual;
} MEMORYSTATUSEX;
__declspec(dllexport) BOOL GetLogicalProcessorInformation(void* Buffer,
DWORD* ReturnedLength) {
(void)Buffer;
// Signal that the buffer is too small so callers know to handle failure.
if (ReturnedLength) *ReturnedLength = 0;
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return FALSE;
}
#define MAX_PATH 260
__declspec(dllexport) unsigned int GetSystemDirectoryA(char* lpBuffer, unsigned int uSize) {
static const char sysdir[] = "C:\\Windows\\System32";
unsigned int needed = sizeof(sysdir); // includes NUL
if (lpBuffer && uSize >= needed) {
for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = sysdir[i];
}
return needed - 1; // return length without NUL, matching Win32
}
__declspec(dllexport) unsigned int GetWindowsDirectoryA(char* lpBuffer, unsigned int uSize) {
static const char windir[] = "C:\\Windows";
unsigned int needed = sizeof(windir);
if (lpBuffer && uSize >= needed) {
for (unsigned int i = 0; i < needed; i++) lpBuffer[i] = windir[i];
}
return needed - 1;
}
__declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer,
DWORD nSize) {
(void)lpName; (void)lpBuffer; (void)nSize;
gLastError = 203; // ERROR_ENVVAR_NOT_FOUND
return 0;
}
__declspec(dllexport) char* GetEnvironmentStrings(void) {
// Return a double-NUL-terminated empty environment block.
static char emptyEnv[2] = { '\0', '\0' };
return emptyEnv;
}
__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) {
(void)penv; return TRUE;
}
__declspec(dllexport) unsigned short* GetEnvironmentStringsW(void) {
static unsigned short emptyEnvW[2] = { 0, 0 };
return emptyEnvW;
}
__declspec(dllexport) BOOL FreeEnvironmentStringsW(unsigned short* penv) {
(void)penv; return TRUE;
}
// ---------------------------------------------------------------------------
// Handle / Error helpers
// ---------------------------------------------------------------------------
__declspec(dllexport) unsigned int SetErrorMode(unsigned int uMode) {
(void)uMode;
return 0; // previous mode
}
__declspec(dllexport) unsigned int SetHandleCount(unsigned int uNumber) {
return uNumber < 256 ? 256 : uNumber;
}
// ---------------------------------------------------------------------------
// File helpers
// ---------------------------------------------------------------------------
#define FILE_TYPE_UNKNOWN 0x0000
#define FILE_TYPE_DISK 0x0001
#define FILE_TYPE_CHAR 0x0002
#define FILE_TYPE_PIPE 0x0003
#define STD_INPUT_HANDLE ((DWORD)-10)
#define STD_OUTPUT_HANDLE ((DWORD)-11)
#define STD_ERROR_HANDLE ((DWORD)-12)
__declspec(dllexport) DWORD GetFileType(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) return FILE_TYPE_UNKNOWN;
return FILE_TYPE_DISK;
}
// ---------------------------------------------------------------------------
// Exception Handling & Debugging
// ---------------------------------------------------------------------------
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_EXECUTE_HANDLER 1
__declspec(dllexport) void* AddVectoredExceptionHandler(unsigned long First, void* Handler) {
(void)First; (void)Handler;
// Return a non-NULL sentinel so callers can pass it to RemoveVectoredExceptionHandler.
return (void*)(uintptr_t)0x56455843UL; // 'VEXC'
}
__declspec(dllexport) unsigned long RemoveVectoredExceptionHandler(void* Handle) {
(void)Handle;
return 1; // success
}
__declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
const uintptr_t* lpArguments) {
(void)dwExceptionCode; (void)dwExceptionFlags;
(void)nNumberOfArguments; (void)lpArguments;
// Best-effort: nothing to unwind; just halt.
while (true) ;
}
typedef struct _EXCEPTION_POINTERS { void* ExceptionRecord; void* ContextRecord; } EXCEPTION_POINTERS;
__declspec(dllexport) long UnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
(void)ExceptionInfo;
return EXCEPTION_CONTINUE_SEARCH;
}
typedef long (*LPTOP_LEVEL_EXCEPTION_FILTER)(EXCEPTION_POINTERS*);
__declspec(dllexport) LPTOP_LEVEL_EXCEPTION_FILTER
SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpFilter) {
(void)lpFilter;
return NULL; // previous filter: none
}
__declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}
// ---------------------------------------------------------------------------
// RTL Exception / Unwind (SEH infrastructure - all stubs)
// ---------------------------------------------------------------------------
typedef struct _RUNTIME_FUNCTION {
unsigned int BeginAddress;
unsigned int EndAddress;
unsigned int UnwindInfoAddress;
} RUNTIME_FUNCTION;
// We have no runtime function table, so RtlLookupFunctionEntry always fails.
__declspec(dllexport) RUNTIME_FUNCTION*
RtlLookupFunctionEntry(unsigned long long ControlPc,
unsigned long long* ImageBase,
void* HistoryTable) {
(void)ControlPc; (void)HistoryTable;
if (ImageBase) *ImageBase = 0;
return NULL;
}
__declspec(dllexport) void* RtlVirtualUnwind(unsigned long HandlerType,
unsigned long long ImageBase,
unsigned long long ControlPc,
RUNTIME_FUNCTION* FunctionEntry,
void* ContextRecord,
void** HandlerData,
unsigned long long* EstablisherFrame,
void* ContextPointers) {
(void)HandlerType; (void)ImageBase; (void)ControlPc; (void)FunctionEntry;
(void)ContextRecord; (void)ContextPointers;
if (HandlerData) *HandlerData = NULL;
if (EstablisherFrame)*EstablisherFrame = 0;
return NULL;
}
__declspec(dllexport) void RtlCaptureContext(void* ContextRecord) {
// Zero out the context record so callers don't read garbage.
if (!ContextRecord) return;
// CONTEXT is ~1232 bytes on x64; zero a conservative upper bound.
unsigned char* p = (unsigned char*)ContextRecord;
for (int i = 0; i < 1232; i++) p[i] = 0;
}
__declspec(dllexport) void RtlUnwindEx(void* TargetFrame, void* TargetIp,
void* ExceptionRecord, void* ReturnValue,
void* ContextRecord, void* HistoryTable) {
(void)TargetFrame; (void)TargetIp; (void)ExceptionRecord;
(void)ReturnValue; (void)ContextRecord; (void)HistoryTable;
// No real unwind possible; halt rather than corrupt the stack silently.
while (true) ;
}
// ---------------------------------------------------------------------------
// Code Pages & Locale helpers
// ---------------------------------------------------------------------------
typedef struct { unsigned int MaxCharSize; unsigned char DefaultChar[2]; unsigned char LeadByte[12]; } CPINFO;
__declspec(dllexport) BOOL GetCPInfo(unsigned int CodePage, CPINFO* lpCPInfo) {
(void)CodePage;
if (lpCPInfo) {
lpCPInfo->MaxCharSize = 1;
lpCPInfo->DefaultChar[0] = '?';
lpCPInfo->DefaultChar[1] = 0;
for (int i = 0; i < 12; i++) lpCPInfo->LeadByte[i] = 0;
}
return TRUE;
}
__declspec(dllexport) BOOL IsValidCodePage(unsigned int CodePage) {
// Acknowledge the two code pages we claim to support.
return (CodePage == 1252 || CodePage == 437) ? TRUE : FALSE;
}
__declspec(dllexport) int LCMapStringA(unsigned int Locale, DWORD dwMapFlags,
const char* lpSrcStr, int cchSrc,
char* lpDestStr, int cchDest) {
(void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc;
(void)lpDestStr; (void)cchDest;
return 0;
}
__declspec(dllexport) int LCMapStringW(unsigned int Locale, DWORD dwMapFlags,
const unsigned short* lpSrcStr, int cchSrc,
unsigned short* lpDestStr, int cchDest) {
(void)Locale; (void)dwMapFlags; (void)lpSrcStr; (void)cchSrc;
(void)lpDestStr; (void)cchDest;
return 0;
}
__declspec(dllexport) BOOL GetStringTypeA(unsigned int Locale, DWORD dwInfoType,
const char* lpSrcStr, int cchSrc,
unsigned short* lpCharType) {
(void)Locale; (void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType;
return FALSE;
}
__declspec(dllexport) BOOL GetStringTypeW(DWORD dwInfoType,
const unsigned short* lpSrcStr, int cchSrc,
unsigned short* lpCharType) {
(void)dwInfoType; (void)lpSrcStr; (void)cchSrc; (void)lpCharType;
return FALSE;
}
__declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType,
char* lpLCData, int cchData) {
(void)Locale; (void)LCType; (void)lpLCData; (void)cchData;
return 0;
}
// ---------------------------------------------------------------------------
// Fiber Local Storage (FLS)
// We implement a minimal 64-slot static table so callers that use FLS for
// thread-local-ish state don't immediately fail.
// ---------------------------------------------------------------------------
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
static void* gFlsSlots[FLS_MAX_SLOTS];
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
__declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
(void)lpCallback;
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
if (!gFlsUsed[i]) {
gFlsUsed[i] = 1;
gFlsSlots[i] = NULL;
return (DWORD)i;
}
}
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return (DWORD)FLS_OUT_OF_INDEXES;
}
__declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return NULL;
}
return gFlsSlots[dwFlsIndex];
}
__declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
gFlsSlots[dwFlsIndex] = lpFlsData;
return TRUE;
}
__declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
if (dwFlsIndex >= FLS_MAX_SLOTS || !gFlsUsed[dwFlsIndex]) {
gLastError = 87;
return FALSE;
}
gFlsUsed[dwFlsIndex] = 0;
gFlsSlots[dwFlsIndex] = NULL;
return TRUE;
}
// ---------------------------------------------------------------------------
// Pointer encoding (security cookie obfuscation)
// We have no security cookie; return the pointer unchanged.
// ---------------------------------------------------------------------------
__declspec(dllexport) void* EncodePointer(void* Ptr) { return Ptr; }
__declspec(dllexport) void* DecodePointer(void* Ptr) { return Ptr; }