Files
nt-lite/code/win32/kernel32.c
T

1033 lines
40 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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; }