doom is now using the native api, add a PEB + TEB to replace the dummy and the awkward argument passing to processes, all allocations are zeroed because i had enough, small readme changes and dedication.

This commit is contained in:
maksw committed 2026-05-18 21:30:07 +02:00
1 parent ddb5d5f5d8
commit f5fd7bdd75
28 files changed
+941 -329

No files matched your search

Binary file not shown.
+1 -1
View File
@@ -167,7 +167,7 @@ for %%f in (%SRC%\doom\*.c) do (
:: ---- Link doom.exe ----
echo Linking doom.exe...
link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
/OUT:"bin\doom.exe" /PDB:"bin\doom.pdb" bin\win32.lib obj\libc.obj obj\doom\*.obj || exit /b 1
/OUT:"bin\doom.exe" /PDB:"bin\doom.pdb" bin\syscall.lib obj\libc.obj obj\doom\*.obj || exit /b 1
:: ---- Compile video player ----
echo Compiling video...
+183 -151
View File
@@ -1,43 +1,51 @@
// doom.c — NT-Lite port of PureDOOM
// Win32 personality carrying all the weight, just like the real ntoskrnl does.
// doom.c — PureDOOM port
// Native personality carrying all the weight.
// Drop doom1.wad (or doom.wad) next to this binary on your FAT32 volume.
#include <windows.h>
#define DOOM_IMPLEMENTATION
#include "PureDOOM.h"
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <intrin.h>
int _fltused;
#include <syscall.h>
// ---------------------------------------------------------------------------
// Globals
// ---------------------------------------------------------------------------
static HANDLE g_stdout;
#define DOOM_IMPLEMENTATION
#include "PureDOOM.h"
int _fltused;
// ---------------------------------------------------------------------------
// doom_print_fn
// ---------------------------------------------------------------------------
static void doom_print_wrapper(const char* msg)
{
UINT len = (UINT)strlen(msg);
WriteConsoleA(g_stdout, msg, len, 0, 0);
SysWriteConsole(msg, strlen(msg));
}
// ---------------------------------------------------------------------------
// doom_malloc_fn / doom_free_fn
// Prepend size tracking metadata to satisfy SysFree's size requirement.
// ---------------------------------------------------------------------------
static void* doom_malloc_wrapper(int size)
{
void* p = HeapAlloc(0, HEAP_ZERO_MEMORY, size);
if (!p) ExitProcess(GetLastError());
return p;
size_t total_size = size + sizeof(size_t);
void* p = SysAlloc(total_size);
if (!p) SysExitProcess(1);
// Store total allocated size at the start of the block
*(size_t*)p = total_size;
// Return pointer past the size prefix
return (void*)((size_t*)p + 1);
}
static void doom_free_wrapper(void* ptr)
{
HeapFree(0, 0, ptr);
if (!ptr) return;
size_t* real_p = (size_t*)ptr - 1;
size_t total_size = *real_p;
SysFree(real_p, total_size);
}
// ---------------------------------------------------------------------------
@@ -45,61 +53,71 @@ static void doom_free_wrapper(void* ptr)
// ---------------------------------------------------------------------------
static void* doom_open_wrapper(const char* path, const char* mode)
{
DWORD access = GENERIC_READ;
DWORD disp = OPEN_EXISTING;
uint32_t handle = 0;
uint32_t result;
if (mode[0] == 'w') {
access = GENERIC_WRITE;
disp = CREATE_ALWAYS;
} else if (mode[0] == 'r' && mode[1] == '+') {
access = GENERIC_READ | GENERIC_WRITE;
result = SysCreateFile(path, &handle);
} else {
result = SysOpenFile(path, &handle);
}
HANDLE h = CreateFileA(path, access, FILE_SHARE_READ, 0, disp, 0, 0);
return (h == INVALID_HANDLE_VALUE) ? 0 : h;
return (result == 0) ? (void*)(uintptr_t)handle : 0;
}
static void doom_close_wrapper(void* handle)
{
CloseHandle((HANDLE)handle);
SysCloseHandle((uint32_t)(uintptr_t)handle);
}
static int doom_read_wrapper(void* handle, void* buf, int count)
{
DWORD read = 0;
ReadFile((HANDLE)handle, buf, (DWORD)count, &read, 0);
size_t read = 0;
SysReadFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &read);
return (int)read;
}
static int doom_write_wrapper(void* handle, const void* buf, int count)
{
DWORD written = 0;
WriteFile((HANDLE)handle, buf, (DWORD)count, &written, 0);
size_t written = 0;
SysWriteFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &written);
return (int)written;
}
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
{
DWORD method;
uint32_t h = (uint32_t)(uintptr_t)handle;
uint64_t absolute_offset = 0;
switch (origin) {
case DOOM_SEEK_SET: method = FILE_BEGIN; break;
case DOOM_SEEK_CUR: method = FILE_CURRENT; break;
case DOOM_SEEK_END: method = FILE_END; break;
default: method = FILE_BEGIN; break;
case DOOM_SEEK_SET:
absolute_offset = (uint64_t)offset;
break;
case DOOM_SEEK_CUR:
absolute_offset = SysGetFilePos(h) + offset;
break;
case DOOM_SEEK_END:
absolute_offset = SysGetFileSize(h) + offset;
break;
default:
absolute_offset = (uint64_t)offset;
break;
}
SetFilePointer((HANDLE)handle, (LONG)offset, 0, method);
SysSeekFile(h, absolute_offset);
return 0;
}
static int doom_tell_wrapper(void* handle)
{
return (int)SetFilePointer((HANDLE)handle, 0, 0, FILE_CURRENT);
return (int)SysGetFilePos((uint32_t)(uintptr_t)handle);
}
static int doom_eof_wrapper(void* handle)
{
DWORD size = GetFileSize((HANDLE)handle, 0);
DWORD pos = (DWORD)doom_tell_wrapper(handle);
uint32_t h = (uint32_t)(uintptr_t)handle;
uint64_t size = SysGetFileSize(h);
uint64_t pos = SysGetFilePos(h);
return (pos >= size) ? 1 : 0;
}
@@ -108,9 +126,9 @@ static int doom_eof_wrapper(void* handle)
// ---------------------------------------------------------------------------
static void doom_gettime_wrapper(int* sec, int* usec)
{
DWORD t = GetTickCount(); // milliseconds
*sec = (int)(t / 1000);
*usec = (int)((t % 1000) * 1000); // ms remainder → us
uint64_t t = SysGetTick();
*sec = (int)(t / 1000000); // 1,000,000 us = 1 second
*usec = (int)(t % 1000000); // Remainder in microseconds
}
// ---------------------------------------------------------------------------
@@ -118,16 +136,16 @@ static void doom_gettime_wrapper(int* sec, int* usec)
// ---------------------------------------------------------------------------
static void doom_exit_wrapper(int code)
{
ExitProcess((UINT)code);
SysExitProcess(code);
}
// ---------------------------------------------------------------------------
// doom_getenv_fn — we don't have environment variables
// doom_getenv_fn
// ---------------------------------------------------------------------------
static char* doom_getenv_wrapper(const char* name)
{
if (strcmp(name, "HOME") == 0) {
static char home[] = "c:";
static char home[] = ".";
doom_print_wrapper("getenv: Returning HOME="); doom_print_wrapper(home); doom_print_wrapper("\n");
return home;
}
@@ -136,102 +154,88 @@ static char* doom_getenv_wrapper(const char* name)
}
// ---------------------------------------------------------------------------
// Input — poll GetAsyncKeyState every frame
// Generates key_down / key_up edge events, not held-key spam.
// Input — Maps Scancode Set 1 values to PureDOOM
// ---------------------------------------------------------------------------
typedef struct { int vk; doom_key_t key; } KeyMap;
typedef struct { uint8_t scancode; bool extended; doom_key_t key; } KeyMap;
// NOTE: Win32 VK codes for letters are uppercase ASCII (e.g. 'W' = 0x57).
// DOOM_KEY_* for letters are lowercase ASCII (e.g. DOOM_KEY_W = 'w' = 0x77).
// These are different values — the VK column is what GetAsyncKeyState takes,
// the doom_key column is what PureDOOM wants. Don't collapse them.
static const KeyMap g_keymap[] = {
// Letters — VK is uppercase, DOOM key is lowercase
{ 'W', DOOM_KEY_W },
{ 'A', DOOM_KEY_A },
{ 'S', DOOM_KEY_S },
{ 'D', DOOM_KEY_D },
{ 'E', DOOM_KEY_E },
{ 'Q', DOOM_KEY_Q },
{ 'Y', DOOM_KEY_Y }, // yes/no menus
{ 'N', DOOM_KEY_N },
// Letters
{ 0x11, false, DOOM_KEY_W },
{ 0x1E, false, DOOM_KEY_A },
{ 0x1F, false, DOOM_KEY_S },
{ 0x20, false, DOOM_KEY_D },
{ 0x12, false, DOOM_KEY_E },
{ 0x10, false, DOOM_KEY_Q },
{ 0x15, false, DOOM_KEY_Y },
{ 0x31, false, DOOM_KEY_N },
// Arrow keys
{ VK_UP, DOOM_KEY_UP_ARROW },
{ VK_DOWN, DOOM_KEY_DOWN_ARROW },
{ VK_LEFT, DOOM_KEY_LEFT_ARROW },
{ VK_RIGHT, DOOM_KEY_RIGHT_ARROW },
{ 0x48, true, DOOM_KEY_UP_ARROW },
{ 0x50, true, DOOM_KEY_DOWN_ARROW },
{ 0x4B, true, DOOM_KEY_LEFT_ARROW },
{ 0x4D, true, DOOM_KEY_RIGHT_ARROW },
// Action keys — Ctrl=fire, Shift=run, Alt=strafe
{ VK_CONTROL, DOOM_KEY_CTRL },
{ VK_SPACE, DOOM_KEY_SPACE },
{ VK_SHIFT, DOOM_KEY_SHIFT },
{ VK_MENU, DOOM_KEY_ALT },
// Action keys
{ 0x1D, false, DOOM_KEY_CTRL },
{ 0x39, false, DOOM_KEY_SPACE },
{ 0x2A, false, DOOM_KEY_SHIFT },
{ 0x38, false, DOOM_KEY_ALT },
// Navigation
{ VK_ESCAPE, DOOM_KEY_ESCAPE },
{ VK_RETURN, DOOM_KEY_ENTER },
{ VK_TAB, DOOM_KEY_TAB },
{ VK_BACK, DOOM_KEY_BACKSPACE },
{ VK_PAUSE, DOOM_KEY_PAUSE },
{ 0x01, false, DOOM_KEY_ESCAPE },
{ 0x1C, false, DOOM_KEY_ENTER },
{ 0x0F, false, DOOM_KEY_TAB },
{ 0x0E, false, DOOM_KEY_BACKSPACE },
// Weapon slots
{ '1', DOOM_KEY_1 },
{ '2', DOOM_KEY_2 },
{ '3', DOOM_KEY_3 },
{ '4', DOOM_KEY_4 },
{ '5', DOOM_KEY_5 },
{ '6', DOOM_KEY_6 },
{ '7', DOOM_KEY_7 },
{ 0x02, false, DOOM_KEY_1 },
{ 0x03, false, DOOM_KEY_2 },
{ 0x04, false, DOOM_KEY_3 },
{ 0x05, false, DOOM_KEY_4 },
{ 0x06, false, DOOM_KEY_5 },
{ 0x07, false, DOOM_KEY_6 },
{ 0x08, false, DOOM_KEY_7 },
// Misc
{ '-', DOOM_KEY_MINUS },
{ '=', DOOM_KEY_EQUALS },
{ ',', DOOM_KEY_COMMA }, // default strafe left
{ '.', DOOM_KEY_PERIOD }, // default strafe right
{ 0x0C, false, DOOM_KEY_MINUS },
{ 0x0D, false, DOOM_KEY_EQUALS },
{ 0x33, false, DOOM_KEY_COMMA },
{ 0x34, false, DOOM_KEY_PERIOD },
// Function keys — save/load/volume/etc
{ VK_F1, DOOM_KEY_F1 },
{ VK_F2, DOOM_KEY_F2 },
{ VK_F3, DOOM_KEY_F3 },
{ VK_F4, DOOM_KEY_F4 },
{ VK_F5, DOOM_KEY_F5 },
{ VK_F6, DOOM_KEY_F6 },
{ VK_F7, DOOM_KEY_F7 },
{ VK_F8, DOOM_KEY_F8 },
{ VK_F9, DOOM_KEY_F9 },
{ VK_F10, DOOM_KEY_F10 },
{ VK_F11, DOOM_KEY_F11 },
{ VK_F12, DOOM_KEY_F12 },
// Function keys
{ 0x3B, false, DOOM_KEY_F1 },
{ 0x3C, false, DOOM_KEY_F2 },
{ 0x3D, false, DOOM_KEY_F3 },
{ 0x3E, false, DOOM_KEY_F4 },
{ 0x3F, false, DOOM_KEY_F5 },
{ 0x40, false, DOOM_KEY_F6 },
{ 0x41, false, DOOM_KEY_F7 },
{ 0x42, false, DOOM_KEY_F8 },
{ 0x43, false, DOOM_KEY_F9 },
{ 0x44, false, DOOM_KEY_F10 },
{ 0x57, false, DOOM_KEY_F11 },
{ 0x58, false, DOOM_KEY_F12 },
};
#define KEYMAP_COUNT (sizeof(g_keymap) / sizeof(g_keymap[0]))
static short g_prev_key[256];
static bool g_prev_key[KEYMAP_COUNT];
static void poll_input(void)
{
for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
int vk = g_keymap[i].vk;
short cur = GetAsyncKeyState(vk);
int cur_down = (cur & 0x8000) != 0;
int prev_down = (g_prev_key[vk] & 0x8000) != 0;
bool cur_down = (SysKbIsKeyDown(g_keymap[i].scancode, g_keymap[i].extended) != 0);
bool prev_down = g_prev_key[i];
if (cur_down && !prev_down) doom_key_down(g_keymap[i].key);
if (!cur_down && prev_down) doom_key_up (g_keymap[i].key);
g_prev_key[vk] = cur;
g_prev_key[i] = cur_down;
}
}
// ---------------------------------------------------------------------------
// Render — StretchDIBits to screen, same pipeline as video.c.
//
// NOTE: PureDOOM's framebuffer is RGBA; Win32 StretchDIBits with BI_RGB/32bpp
// expects BGRA. Channels will be swapped (red ↔ blue) until you either:
// a) swap in the blit loop below, or
// b) tell PureDOOM to give you BGRA if a future version supports it.
// For a first boot, blue-tinted demons are still demons.
// Render — Performs lightweight software upscale and blits to the screen
// ---------------------------------------------------------------------------
#define DOOM_W 320
#define DOOM_H 200
@@ -239,55 +243,82 @@ static void poll_input(void)
#define DISP_W (DOOM_W * SCALE)
#define DISP_H (DOOM_H * SCALE)
static BITMAPINFO g_bmi;
static uint32_t g_blit_buf[DOOM_W * DOOM_H]; // BGRA-swapped copy
static uint32_t g_blit_buf[DISP_W * DISP_H];
static void render_frame(const uint8_t* rgba)
{
// Swap R <-> B so Win32 GDI sees correct colours
const uint32_t* src = (const uint32_t*)rgba;
for (int i = 0; i < DOOM_W * DOOM_H; i++) {
uint32_t p = src[i];
// RGBA -> BGRA: swap byte 0 (R) and byte 2 (B)
g_blit_buf[i] = (p & 0xFF00FF00u)
// Software upscale loop mapping 320x200 buffer directly to scaled window buffer
for (int y = 0; y < DOOM_H; y++) {
for (int x = 0; x < DOOM_W; x++) {
uint32_t p = src[y * DOOM_W + x];
// RGBA -> BGRA conversion (standard for linear x86/64 framebuffers)
uint32_t bgra = (p & 0xFF00FF00u)
| ((p & 0x00FF0000u) >> 16)
| ((p & 0x000000FFu) << 16);
// Populate SCALE * SCALE pixel block
for (int sy = 0; sy < SCALE; sy++) {
for (int sx = 0; sx < SCALE; sx++) {
g_blit_buf[(y * SCALE + sy) * DISP_W + (x * SCALE + sx)] = bgra;
}
}
}
}
PAINTSTRUCT ps;
HDC hdc = BeginPaint((HWND)0, &ps);
StretchDIBits(
hdc,
0, 0, DISP_W, DISP_H, // destination — scaled up
0, 0, DOOM_W, DOOM_H, // source — native 320×200
g_blit_buf,
&g_bmi,
DIB_RGB_COLORS,
SRCCOPY
);
EndPaint((HWND)0, &ps);
SysScreenBlit(0, 0, DISP_W, DISP_H, g_blit_buf);
}
static int build_argv_from_command_line(const char* cmdline, char* storage, size_t storageSize,
const char** argv, int maxArgv) {
if (!storage || storageSize == 0 || !argv || maxArgv <= 1) return 0;
if (!cmdline || cmdline[0] == '\0') cmdline = "doom";
size_t len = strlen(cmdline);
if (len >= storageSize) len = storageSize - 1;
memcpy(storage, cmdline, len);
storage[len] = '\0';
int argc = 0;
char* p = storage;
while (*p && argc < maxArgv - 1) {
while (*p == ' ' || *p == '\t') p++;
if (!*p) break;
if (*p == '"') {
p++;
argv[argc++] = p;
while (*p && *p != '"') p++;
if (*p) *p++ = '\0';
} else {
argv[argc++] = p;
while (*p && *p != ' ' && *p != '\t') p++;
if (*p) *p++ = '\0';
}
}
argv[argc] = 0;
if (argc == 0) {
argv[0] = "doom";
argv[1] = 0;
argc = 1;
}
return argc;
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
int main(const char* cmdline)
int main(void)
{
(void)cmdline;
const char* cmdline = SysGetCommandLine();
g_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(g_stdout, "doom: starting\n", 15, 0, 0);
SysWriteConsole("doom: starting\n", 15);
// Prepare the BMI header once (top-down, like video.c)
memset(&g_bmi, 0, sizeof(g_bmi));
g_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
g_bmi.bmiHeader.biWidth = DOOM_W;
g_bmi.bmiHeader.biHeight = -DOOM_H; // negative = top-down
g_bmi.bmiHeader.biPlanes = 1;
g_bmi.bmiHeader.biBitCount = 32;
g_bmi.bmiHeader.biCompression = BI_RGB;
// Wire up PureDOOM
// Wire up PureDOOM core mechanics
doom_set_print(doom_print_wrapper);
doom_set_malloc(doom_malloc_wrapper, doom_free_wrapper);
doom_set_file_io(
@@ -312,19 +343,20 @@ int main(const char* cmdline)
doom_set_default_int("key_use", DOOM_KEY_E);
doom_set_default_int("mouse_move", 0);
// No extra args — PureDOOM will scan for doom1.wad / doom.wad
const char* args[] = { "doom" };
doom_init(1, (char**)args, 0);
char argvStorage[512];
const char* argv[32];
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
doom_init(argc, argv, 0);
WriteConsoleA(g_stdout, "doom: entering game loop\n", 25, 0, 0);
SysWriteConsole("doom: entering game loop\n", 25);
// Game loop — identical shape to video.c's Bink loop
// Core execution loop
while (1) {
poll_input();
doom_update();
render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */));
}
// Unreachable: doom_exit() -> ExitProcess() handles clean shutdown
ExitProcess(0);
SysExitProcess(0);
return 0;
}
+20 -3
View File
@@ -669,11 +669,15 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
uint64_t offset, void *buf,
size_t size, size_t *bytesRead) {
(void)fs;
if (!bytesRead) return STATUS_INVALID_PARAMETER;
*bytesRead = 0;
if (!fileCtx || !buf) return STATUS_INVALID_PARAMETER;
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
FAT32_VOL *v = f->Vol;
if (offset >= f->FileSize) { *bytesRead = 0; return STATUS_SUCCESS; }
if (offset >= f->FileSize || size == 0) return STATUS_SUCCESS;
if (offset + size > f->FileSize) size = (size_t)(f->FileSize - offset);
size_t wanted = size;
// Single allocation for the largest possible coalesced run.
uint8_t *runBuf = (uint8_t *)MmAllocatePool(MAX_RUN_SECTORS * 512u);
@@ -681,6 +685,7 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
size_t totalRead = 0;
uint8_t *out = (uint8_t *)buf;
KSTATUS readStatus = STATUS_SUCCESS;
uint32_t skipClusters = (uint32_t)(offset / v->BytesPerCluster);
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
uint32_t cluster = f->StartCluster;
@@ -701,8 +706,11 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
runSectors += v->SectorsPerCluster;
}
if (!KSUCCESS(ReadSectors(v, ClusterToLba(v, runStart), runSectors, runBuf)))
KSTATUS rs = ReadSectors(v, ClusterToLba(v, runStart), runSectors, runBuf);
if (!KSUCCESS(rs)) {
readStatus = rs;
break;
}
uint32_t runBytes = runSectors * 512u;
size_t avail = runBytes - offsetInCluster;
@@ -716,8 +724,17 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
cluster = next;
}
if (totalRead != wanted) {
// The directory entry says the file has more data than the cluster chain
// or disk read actually supplied. Clear the caller's remainder so stale
// memory cannot be interpreted as file contents.
memset(out, 0, wanted - totalRead);
}
MmFreePool(runBuf);
*bytesRead = totalRead;
if (totalRead != wanted)
return KSUCCESS(readStatus) ? STATUS_DEVICE_ERROR : readStatus;
return STATUS_SUCCESS;
}
+3 -2
View File
@@ -48,12 +48,13 @@ void itoa(int num, char* str) {
}
}
void _start(const char* args)
void _start(void)
{
const char* args = SysGetCommandLine();
uint32_t h = 0;
uint32_t s = 0;
for (;;) {
while (true) {
SysDebugPrint("[init] starting\n");
s = SysCreateProcess(args, &h, NULL);
if (s != STATUS_SUCCESS) {
+21 -2
View File
@@ -5,6 +5,7 @@
#include <io_file.h>
#include <mm_virt.h>
#include <ob_object.h>
#include <ke_userenv.h>
#define MAX_DRIVES 8
@@ -148,6 +149,23 @@ __declspec(dllexport) KSTATUS IoSetCwd(const char* path) {
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
memcpy(proc->CurrentDirectory, resolved, len + 1);
// Keep the user-visible PEB mirror in sync. The PEB's CWD buffer is a
// fixed SYS_CWD_MAX allocation made at process creation, so the pointer
// remains stable for personalities that cache SYS_PEB*.
if (proc->UserCwdBase && proc->UserCwdSize) {
size_t copyLen = len;
if (copyLen + 1 > proc->UserCwdSize) copyLen = proc->UserCwdSize - 1;
memcpy((void*)proc->UserCwdBase, resolved, copyLen);
((char*)proc->UserCwdBase)[copyLen] = '\0';
SYS_PEB* peb = (SYS_PEB*)proc->UserPebBase;
if (peb && peb->Magic == SYS_PEB_MAGIC) {
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
peb->CurrentDirectoryLength = copyLen;
}
}
KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_SUCCESS;
@@ -209,8 +227,9 @@ __declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
// IoReadFile - sequential read at current offset
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf, size_t size, size_t* bytesRead) {
if (!file || !buf) return STATUS_INVALID_PARAMETER;
if (file->Offset >= file->Size) { *bytesRead = 0; return STATUS_SUCCESS; }
if (bytesRead) *bytesRead = 0;
if (!file || !buf || !bytesRead) return STATUS_INVALID_PARAMETER;
if (file->Offset >= file->Size || size == 0) return STATUS_SUCCESS;
// Clamp to remaining bytes.
size_t remaining = (size_t)(file->Size - file->Offset);
+2 -2
View File
@@ -36,7 +36,7 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) {
}
// KePanic
__declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
__declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...) {
// Silence! Stop the world!
_disable();
@@ -44,7 +44,7 @@ __declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
// We call PrintChar directly so we can set a red foreground.
// (DbgPrint uses green; we want this visually distinct.)
static const char banner[] = "\n*** KERNEL PANIC ***\n";
for (const char *p = banner; *p; p++)
for (const char* p = banner; *p; p++)
PrintChar(*p, 0xFF2222, 0x000000);
// Now print the caller's message through the normal vprintf path.
+1 -1
View File
@@ -6,7 +6,7 @@
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
__declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...);
__declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...);
// ---------------------------------------------------------------------------
// Fault dispatch - called from the three IDT fault stubs (ke_syscall.asm).
+130 -1
View File
@@ -5,6 +5,7 @@
#include <ke_thread.h>
#include <ke_sched.h>
#include <ke_event.h>
#include <ke_userenv.h>
#include <mm_virt.h>
#include <ob_object.h>
@@ -84,12 +85,27 @@ static void ProcessDestroy(OBJECT* obj) {
}
proc->ModuleCount = 0;
// Release the user command line buffer.
// Release the user-mode process environment.
if (proc->UserPebBase && proc->UserPebSize) {
MmFreeUserMemory((void *)proc->UserPebBase, proc->UserPebSize);
proc->UserPebBase = 0;
proc->UserPebSize = 0;
}
if (proc->UserImagePathBase && proc->UserImagePathSize) {
MmFreeUserMemory((void *)proc->UserImagePathBase, proc->UserImagePathSize);
proc->UserImagePathBase = 0;
proc->UserImagePathSize = 0;
}
if (proc->UserCmdlineBase && proc->UserCmdlineSize) {
MmFreeUserMemory((void *)proc->UserCmdlineBase, proc->UserCmdlineSize);
proc->UserCmdlineBase = 0;
proc->UserCmdlineSize = 0;
}
if (proc->UserCwdBase && proc->UserCwdSize) {
MmFreeUserMemory((void *)proc->UserCwdBase, proc->UserCwdSize);
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
}
// Release the exit event.
if (proc->ExitEvent) {
@@ -129,9 +145,17 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->ExitCode = 0;
proc->ImageBase = 0;
proc->ImageSize = 0;
proc->UserPebBase = 0;
proc->UserPebSize = 0;
proc->UserImagePathBase = 0;
proc->UserImagePathSize = 0;
proc->UserCmdlineBase = 0;
proc->UserCmdlineSize = 0;
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
proc->ModuleCount = 0;
proc->CurrentDirectory[0] = '\0';
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
// Slot 0 is the invalid handle - leave it NULL.
memset(proc->Handles, 0, sizeof(proc->Handles));
@@ -152,6 +176,111 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// KeSetupUserProcessEnvironment
// ---------------------------------------------------------------------------
static KSTATUS CopyEnvironmentString(const char* src, uintptr_t* baseOut, size_t* sizeOut) {
if (!baseOut || !sizeOut) return STATUS_INVALID_PARAMETER;
if (!src) src = "";
size_t len = strlen(src) + 1;
char* dst = (char*)MmAllocateUserMemory(len);
if (!dst) return STATUS_NO_MEMORY;
memcpy(dst, src, len);
*baseOut = (uintptr_t)dst;
*sizeOut = len;
return STATUS_SUCCESS;
}
static KSTATUS CopyEnvironmentStringFixed(const char* src, size_t capacity,
uintptr_t* baseOut, size_t* sizeOut) {
if (!baseOut || !sizeOut || capacity == 0) return STATUS_INVALID_PARAMETER;
if (!src) src = "";
char* dst = (char*)MmAllocateUserMemory(capacity);
if (!dst) return STATUS_NO_MEMORY;
memset(dst, 0, capacity);
size_t len = strlen(src);
if (len >= capacity) len = capacity - 1;
memcpy(dst, src, len);
dst[len] = '\0';
*baseOut = (uintptr_t)dst;
*sizeOut = capacity;
return STATUS_SUCCESS;
}
static void FreePartialProcessEnvironment(PROCESS* proc) {
if (!proc) return;
if (proc->UserPebBase && proc->UserPebSize) {
MmFreeUserMemory((void*)proc->UserPebBase, proc->UserPebSize);
proc->UserPebBase = 0;
proc->UserPebSize = 0;
}
if (proc->UserImagePathBase && proc->UserImagePathSize) {
MmFreeUserMemory((void*)proc->UserImagePathBase, proc->UserImagePathSize);
proc->UserImagePathBase = 0;
proc->UserImagePathSize = 0;
}
if (proc->UserCmdlineBase && proc->UserCmdlineSize) {
MmFreeUserMemory((void*)proc->UserCmdlineBase, proc->UserCmdlineSize);
proc->UserCmdlineBase = 0;
proc->UserCmdlineSize = 0;
}
if (proc->UserCwdBase && proc->UserCwdSize) {
MmFreeUserMemory((void*)proc->UserCwdBase, proc->UserCwdSize);
proc->UserCwdBase = 0;
proc->UserCwdSize = 0;
}
}
KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
const char* imagePath,
const char* commandLine,
const char* currentDirectory) {
if (!proc || !imagePath) return STATUS_INVALID_PARAMETER;
const char* finalCommandLine = (commandLine && commandLine[0]) ? commandLine : imagePath;
const char* finalCwd = currentDirectory ? currentDirectory : proc->CurrentDirectory;
FreePartialProcessEnvironment(proc);
KSTATUS s = CopyEnvironmentString(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize);
if (!KSUCCESS(s)) goto fail;
s = CopyEnvironmentString(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize);
if (!KSUCCESS(s)) goto fail;
s = CopyEnvironmentStringFixed(finalCwd, SYS_CWD_MAX, &proc->UserCwdBase, &proc->UserCwdSize);
if (!KSUCCESS(s)) goto fail;
SYS_PEB* peb = (SYS_PEB*)MmAllocateUserMemory(sizeof(SYS_PEB));
if (!peb) { s = STATUS_NO_MEMORY; goto fail; }
memset(peb, 0, sizeof(SYS_PEB));
peb->Magic = SYS_PEB_MAGIC;
peb->Version = SYS_ENV_VERSION;
peb->ProcessId = proc->ProcessId;
peb->ImagePath = (const char*)proc->UserImagePathBase;
peb->ImagePathLength = proc->UserImagePathSize ? proc->UserImagePathSize - 1 : 0;
peb->CommandLine = (const char*)proc->UserCmdlineBase;
peb->CommandLineLength = proc->UserCmdlineSize ? proc->UserCmdlineSize - 1 : 0;
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
peb->CurrentDirectoryLength = strlen(finalCwd);
proc->UserPebBase = (uintptr_t)peb;
proc->UserPebSize = sizeof(SYS_PEB);
return STATUS_SUCCESS;
fail:
FreePartialProcessEnvironment(proc);
return s;
}
// ---------------------------------------------------------------------------
// KeExitProcess
// ---------------------------------------------------------------------------
+9 -6
View File
@@ -7,6 +7,7 @@
#include <ke_spinlock.h>
#include <ke_event.h>
#include <stddef.h>
#include <stdint.h>
#define PROCESS_NAME_MAX 64
#define HANDLE_TABLE_SIZE 256
@@ -26,11 +27,6 @@ typedef struct PROCESS {
uint32_t ProcessId;
char Name[PROCESS_NAME_MAX]; /* UTF-8 */
// Windows-style flat command line, stored at launch by SysCreateProcess.
// The initial thread receives this pointer as its UserArg so _start can
// parse it into argc/argv without an extra syscall.
char Cmdline[512];
char CurrentDirectory[512];
SPINLOCK CwdLock;
@@ -50,9 +46,15 @@ typedef struct PROCESS {
uintptr_t ImageBase;
size_t ImageSize;
/* User command line buffer passed to the initial thread. */
/* User-mode process environment. The initial thread sees this through GS->TEB->PEB. */
uintptr_t UserPebBase;
size_t UserPebSize;
uintptr_t UserImagePathBase;
size_t UserImagePathSize;
uintptr_t UserCmdlineBase;
size_t UserCmdlineSize;
uintptr_t UserCwdBase;
size_t UserCwdSize;
/* Per-process loaded modules (user processes only). */
PROCESS_MODULE Modules[PROCESS_MODULE_MAX];
@@ -66,6 +68,7 @@ typedef struct PROCESS {
} PROCESS;
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
KSTATUS KeSetupUserProcessEnvironment(PROCESS* process, const char* imagePath, const char* commandLine, const char* currentDirectory);
void KeExitProcess(PROCESS* process, int exitCode);
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
+3 -1
View File
@@ -4,12 +4,14 @@
; sizeof(OBJECT) = 16 (type:4, refcount:4, destroy:8)
; THREAD.SavedContext offset = 88
; THREAD.TssRsp0 offset = 104
; THREAD.Teb offset = 112
; THREAD.FpuContext offset = 168
; TSS64.RSP0 offset = 4
;
THREAD_SavedContext EQU 88
THREAD_TssRsp0 EQU 104
THREAD_Teb EQU 112
THREAD_FpuContext EQU 160
THREAD_FpuContext EQU 168
TSS_RSP0_OFFSET EQU 4
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
+34 -34
View File
@@ -756,18 +756,18 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
if (!KSUCCESS(s)) return s;
uint64_t size = IoGetFileSize(f);
void* mem = MmAllocateUserMemory(size);
void* mem = MmAllocatePool(size);
if (!mem) { IoCloseFile(f); return STATUS_NO_MEMORY; }
size_t got;
s = IoReadFile(f, mem, size, &got);
IoCloseFile(f);
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
// Create the process object.
PROCESS* proc;
s = KeCreateProcess(path, &proc);
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
if (!KSUCCESS(s)) { MmFreePool(mem); return s; }
// Inherit CWD from parent
PROCESS* parent = KeGetCurrentProcess();
@@ -778,27 +778,13 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
KeReleaseSpinlock(&parent->CwdLock, flags);
}
// Store the command line in the process so _start can pick it up.
// If no cmdline was supplied default to just the image path.
if (cmdline && cmdline[0])
strncpy(proc->Cmdline, cmdline, sizeof(proc->Cmdline) - 1);
else
strncpy(proc->Cmdline, path, sizeof(proc->Cmdline) - 1);
proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0';
// proc->Cmdline is kernel pool - CPL=3 code can't read it.
// Copy into user memory so the initial thread can access it directly.
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
if (!userCmdline) { MmFreeUserMemory(mem, size); ObDereferenceObject(&proc->Header); return STATUS_NO_MEMORY; }
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
proc->UserCmdlineBase = (uintptr_t)userCmdline;
proc->UserCmdlineSize = cmdlineLen;
s = KeSetupUserProcessEnvironment(proc, path, cmdline, proc->CurrentDirectory);
if (!KSUCCESS(s)) { MmFreePool(mem); ObDereferenceObject(&proc->Header); return s; }
// Map the PE image into memory.
uintptr_t imageBase = 0;
s = LdrLoadImage(mem, size, &imageBase, true);
MmFreeUserMemory(mem, size);
MmFreePool(mem);
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
s = LdrResolveImportsForProcess(proc, imageBase);
@@ -808,27 +794,41 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
return s;
}
// Spin up the initial thread. Pass userCmdline as UserArg so _start
// receives it directly in RCX without an extra syscall.
proc->ImageBase = imageBase;
proc->ImageSize = LdrGetImageSize(imageBase);
// Give the caller a handle before making the child runnable. Otherwise a
// fast crash/exit can destroy the PROCESS while the syscall is still trying
// to finish wiring it up.
PROCESS* caller = KeGetCurrentProcess();
if (!caller) {
MmFreeUserMemory((void*)imageBase, proc->ImageSize);
ObDereferenceObject(&proc->Header);
return STATUS_FAILURE;
}
uint32_t handle;
s = KeAllocateHandle(caller, &proc->Header, &handle);
if (!KSUCCESS(s)) {
KeCloseHandle(caller, handle);
proc->ImageBase = 0;
proc->ImageSize = 0;
MmFreeUserMemory((void*)imageBase, proc->ImageSize);
ObDereferenceObject(&proc->Header);
return s;
}
// Spin up the initial thread last. KeCreateThread queues the thread before
// returning, so everything visible from exit/fault/wait paths must already
// be valid at this point.
THREAD* thread;
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
s = KeCreateThread(proc, entryPoint, (uintptr_t)userCmdline,
PriorityNormal, &thread);
s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread);
if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
ObDereferenceObject(&proc->Header);
return s;
}
proc->ImageBase = imageBase;
proc->ImageSize = LdrGetImageSize(imageBase);
// Give the caller a handle to the new process so they can wait on it.
PROCESS* caller = KeGetCurrentProcess();
if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; }
uint32_t handle;
s = KeAllocateHandle(caller, &proc->Header, &handle);
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
*handleOut = handle;
return STATUS_SUCCESS;
+34 -9
View File
@@ -4,6 +4,7 @@
#include <ke_thread.h>
#include <ke_sched.h>
#include <ke_event.h>
#include <ke_userenv.h>
#include <mm_virt.h>
#include <ob_object.h>
@@ -35,7 +36,12 @@ static void ThreadDestroy(OBJECT* obj) {
thread->KernelStackBase = (void *)0;
}
// Assumes UserStackBase was added to THREAD struct in ke_thread.h.
if (thread->Teb && thread->TebSize) {
MmFreeUserMemory(thread->Teb, thread->TebSize);
thread->Teb = (void*)0;
thread->TebSize = 0;
}
if (thread->UserStackBase) {
MmFreeUserMemory(thread->UserStackBase, USTACK_SIZE);
thread->UserStackBase = (void *)0;
@@ -43,7 +49,7 @@ static void ThreadDestroy(OBJECT* obj) {
}
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out) {
THREAD_PRIORITY priority, THREAD** out) {
if (!owner || !out) return STATUS_INVALID_PARAMETER;
if (priority >= PriorityCount) return STATUS_INVALID_PARAMETER;
@@ -71,6 +77,7 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
void* ustack = MmAllocateUserMemory(USTACK_SIZE);
if (!ustack) {
MmFreePool(kstack);
thread->KernelStackBase = (void*)0;
ObDereferenceObject(&thread->Header);
return STATUS_NO_MEMORY;
}
@@ -79,14 +86,25 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
// i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness
// this magic number will hunt me in my dreams
// Initialize the dummy TEB
// offset 0x10 is StackLimit, which __chkstk checks.
uint64_t* teb = (uint64_t*)ustack;
teb[0] = 0; // ExceptionList (GS:0x00)
teb[1] = userStackTop; // StackBase
teb[2] = (uintptr_t)ustack; // StackLimit
SYS_TEB* teb = (SYS_TEB*)MmAllocateUserMemory(sizeof(SYS_TEB));
if (!teb) {
MmFreeUserMemory(ustack, USTACK_SIZE);
thread->UserStackBase = (void*)0;
MmFreePool(kstack);
thread->KernelStackBase = (void*)0;
ObDereferenceObject(&thread->Header);
return STATUS_NO_MEMORY;
}
memset(teb, 0, sizeof(SYS_TEB));
teb->Peb = (SYS_PEB*)owner->UserPebBase;
teb->StackBase = (void*)userStackTop;
teb->StackLimit = ustack;
teb->Self = teb;
teb->ThreadId = thread->ThreadId;
teb->Magic = SYS_TEB_MAGIC;
thread->Teb = teb;
thread->TebSize = sizeof(SYS_TEB);
thread->UserEntry = entry;
thread->UserArg = arg;
@@ -114,9 +132,15 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
EVENT* exitEvent;
KSTATUS s = KeEventCreate(EventManualReset, 0, &exitEvent);
if (!KSUCCESS(s)) {
if (thread->Teb && thread->TebSize) MmFreeUserMemory(thread->Teb, thread->TebSize);
thread->Teb = (void*)0;
thread->TebSize = 0;
MmFreeUserMemory(ustack, USTACK_SIZE);
thread->UserStackBase = (void*)0;
MmFreePool(kstack);
thread->KernelStackBase = (void*)0;
ObDereferenceObject(&owner->Header);
thread->Owner = (PROCESS*)0;
ObDereferenceObject(&thread->Header);
return s;
}
@@ -162,7 +186,7 @@ __declspec(dllexport) void KeExitThread(THREAD* thread, int code) {
extern void KeKernelThreadEntry(void);
__declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out) {
THREAD_PRIORITY priority, THREAD** out) {
// Kernel threads are owned by the idle process. We need a PROCESS* for
// the owner field; grab it from the idle/init thread that already exists.
THREAD* idleThread = KeGetCurrentThread(); // safe to call before scheduler starts
@@ -182,6 +206,7 @@ __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
thread->SchedLockFlags = 0x202ULL;
thread->UserStackBase = (void*)0; // no user stack
thread->Teb = (void*)0; // no TEB; GS_BASE will be 0 in kernel ctx
thread->TebSize = 0;
void* kstack = MmAllocatePool(KSTACK_SIZE);
if (!kstack) {
+4 -3
View File
@@ -59,8 +59,9 @@ typedef struct THREAD {
// Updated on every context switch so the CPU loads the right kernel stack.
uintptr_t TssRsp0;
// Thread Environment Block
// Thread Environment Block. GS base is loaded from this pointer.
void* Teb;
size_t TebSize;
// Ring-3 entry point and argument for newly created threads.
uintptr_t UserEntry;
@@ -79,14 +80,14 @@ typedef struct THREAD {
} THREAD;
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out);
THREAD_PRIORITY priority, THREAD** out);
// Create a kernel-mode-only thread. fn is called directly in ring 0 with
// arg; when fn returns the thread exits automatically. No user stack or TEB
// is allocated. The thread is owned by the idle process and runs at the
// given priority.
__declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out);
THREAD_PRIORITY priority, THREAD** out);
// Mark a thread dead and deschedule it permanently.
__declspec(dllexport) void KeExitThread(THREAD* thread, int code);
+35
View File
@@ -0,0 +1,35 @@
#pragma once
// ke_userenv.h - kernel view of the user-mode process/thread environment.
// This is this OS' ABI, not a Windows compatibility structure.
#include <stdint.h>
#include <stddef.h>
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
#define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 512u
typedef struct SYS_PEB {
uint32_t Magic;
uint32_t Version;
uint32_t ProcessId;
const char* ImagePath;
size_t ImagePathLength;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
} SYS_PEB;
typedef struct SYS_TEB {
SYS_PEB* Peb;
void* StackBase;
void* StackLimit;
struct SYS_TEB* Self;
uint32_t ThreadId;
uint32_t Magic;
} SYS_TEB;
+17 -26
View File
@@ -250,8 +250,8 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
uint64_t size = IoGetFileSize(f);
DbgPrint("LaunchUserProcess: IoGetFileSize: %llu\n", size);
void* mem = MmAllocateUserMemory(size);
DbgPrint("LaunchUserProcess: MmAllocateUserMemory: 0x%llX\n", mem);
void* mem = MmAllocatePool(size);
DbgPrint("LaunchUserProcess: MmAllocatePool: 0x%llX\n", mem);
if (!mem) {
IoCloseFile(f);
return 0;
@@ -262,7 +262,7 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s);
IoCloseFile(f);
if (!KSUCCESS(s)) {
MmFreeUserMemory(mem, size);
MmFreePool(mem);
return 0;
}
@@ -273,35 +273,23 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
s = KeCreateProcess(filePath, &proc);
if (!KSUCCESS(s)) {
DbgPrint("Failed to create process. System halted.\n");
MmFreeUserMemory(mem, size);
MmFreePool(mem);
return 0;
}
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath);
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, (args && args[0]) ? args : filePath);
if (args && args[0])
strncpy(proc->Cmdline, args, sizeof(proc->Cmdline) - 1);
else
strncpy(proc->Cmdline, filePath, sizeof(proc->Cmdline) - 1);
proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0';
// proc->Cmdline is kernel pool (ffffc000...) - not readable at CPL=3.
// Copy into user memory before handing the pointer to the initial thread.
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
if (!userCmdline) {
DbgPrint("LaunchUserProcess: Failed to allocate user cmdline buffer.\n");
MmFreeUserMemory(mem, size);
s = KeSetupUserProcessEnvironment(proc, filePath, args, proc->CurrentDirectory);
if (!KSUCCESS(s)) {
DbgPrint("LaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
MmFreePool(mem);
ObDereferenceObject(&proc->Header);
return 0;
}
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
proc->UserCmdlineBase = (uintptr_t)userCmdline;
proc->UserCmdlineSize = cmdlineLen;
// Parse the raw PE file and map it into executable memory.
uintptr_t imageBase = 0;
s = LdrLoadImage(mem, size, &imageBase, true);
MmFreeUserMemory(mem, size);
MmFreePool(mem);
if (!KSUCCESS(s)) {
DbgPrint("LaunchUserProcess: LdrLoadImage failed with status: %d\n", s);
ObDereferenceObject(&proc->Header);
@@ -316,19 +304,22 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
return 0;
}
proc->ImageBase = imageBase;
proc->ImageSize = LdrGetImageSize(imageBase);
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint);
// Create the main thread, pointing it to the PE Entry Point.
s = KeCreateThread(proc, (uintptr_t)entryPoint, (uintptr_t)userCmdline, PriorityNormal, &thread);
// Create the main thread last. KeCreateThread queues it before returning.
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread);
if (!KSUCCESS(s)) {
DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s);
proc->ImageBase = 0;
proc->ImageSize = 0;
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
ObDereferenceObject(&proc->Header);
return 0;
}
proc->ImageBase = imageBase;
proc->ImageSize = LdrGetImageSize(imageBase);
DbgPrint("LaunchUserProcess: Thread created and queued!\n");
// Caller owns the reference returned by KeCreateProcess.
+2
View File
@@ -323,6 +323,7 @@ static void* HeapAlloc(HEAP* h, size_t size) {
HdrToFtr(best)->Magic = BLOCK_MAGIC;
HdrToFtr(best)->Size = best->Size;
memset((char *)best + HDR_SIZE, 0, size);
KeReleaseSpinlock(&h->Lock, flags);
return (char *)best + HDR_SIZE;
}
@@ -409,6 +410,7 @@ static void* HeapRealloc(HEAP* h, void* ptr, size_t new_size) {
if (!new_ptr) {
return (void*)0; // Out of memory, original block is left untouched
}
memset(new_ptr, 0, aligned_size);
// Copy the old data to the new block.
// We only copy up to the old block's usable size.
+2
View File
@@ -78,6 +78,8 @@ typedef void* pthread_mutexattr_t;
typedef void* posix_spawn_file_actions_t;
typedef void* posix_spawnattr_t;
__declspec(dllexport) const char* posix_get_command_line(void);
// ---------------------------------------------------------------------------
// dirent (used by posix_dir.c)
// ---------------------------------------------------------------------------
+28 -8
View File
@@ -22,9 +22,12 @@ static int KsToErrno(uint32_t s) {
}
}
__declspec(dllexport) int ClearConsoleScreenBuffer(void* hConsole) {
__declspec(dllexport) void clearscreen(void) {
SysScreenClear(0x00000000);
return 1;
}
__declspec(dllexport) void clearkeyboard(void) {
while (SysKbPoll()) (void)SysKbReadChar();
}
__declspec(dllexport) int open(const char* path, int flags, ...) {
@@ -154,14 +157,31 @@ __declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) {
FD_ENTRY* e = FdGet(pid, fd);
if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; }
uint64_t target;
int64_t target = 0;
switch (whence) {
case SEEK_SET: target = (uint64_t)offset; break;
case SEEK_CUR: target = SysGetFilePos(e->KHandle) + (uint64_t)offset; break;
case SEEK_END: target = SysGetFileSize(e->KHandle) + (uint64_t)offset; break;
default: errno = EINVAL; return -1;
case SEEK_SET:
target = (int64_t)offset;
break;
case SEEK_CUR: {
uint64_t cur = SysGetFilePos(e->KHandle);
if (cur > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
target = (int64_t)cur + (int64_t)offset;
break;
}
uint32_t s = SysSeekFile(e->KHandle, target);
case SEEK_END: {
uint64_t size = SysGetFileSize(e->KHandle);
if (size > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
target = (int64_t)size + (int64_t)offset;
break;
}
default:
errno = EINVAL;
return -1;
}
if (target < 0 || target > 0x7FFFFFFFLL) { errno = EINVAL; return -1; }
uint32_t s = SysSeekFile(e->KHandle, (uint64_t)target);
if (s != 0) { errno = KsToErrno(s); return -1; }
return (off_t)target;
}
+14
View File
@@ -5,6 +5,20 @@
#include "posix.h"
#include "posix_path.h"
static SYS_PEB* gProcessPeb = NULL;
static SYS_PEB* PosixPeb(void) {
if (!gProcessPeb) gProcessPeb = SysGetPeb();
return gProcessPeb;
}
__declspec(dllexport) const char* posix_get_command_line(void) {
SYS_PEB* peb = PosixPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
return peb->CommandLine;
}
// pid -> proc handle table (user-mode side, for waitpid)
#define PROC_TABLE_SIZE 32
typedef struct {
+12 -12
View File
@@ -41,7 +41,6 @@ typedef struct {
void* (*Fn)(void*);
void* Arg;
uint32_t Pid; // owner pid, for the retval table
uint32_t Tid; // filled in by pthread_create after KeCreateThread returns
} PTHREAD_TRAMP;
static void PthreadTrampoline(uintptr_t raw) {
@@ -49,14 +48,19 @@ static void PthreadTrampoline(uintptr_t raw) {
void* (*fn)(void*) = tr->Fn;
void* arg = tr->Arg;
uint32_t pid = tr->Pid;
uint32_t tid = tr->Tid;
uint32_t tid = SysGetCurrentTid();
// Create the join slot from inside the new thread. KeCreateThread makes the
// thread runnable before the creator regains control, so the old design
// raced on tr->Tid and could observe zero/stale memory.
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
if (!slot) slot = PthreadSlotAlloc(pid, tid);
SysFree(tr, sizeof(PTHREAD_TRAMP));
void* retval = fn(arg);
// Store retval for pthread_join. If the slot isn't there (join was called
// before the thread was even created, which shouldn't happen) just drop it.
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
// Store retval for pthread_join.
if (slot) slot->RetVal = retval;
SysExitThread(0);
@@ -77,7 +81,6 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
tr->Fn = fn;
tr->Arg = arg;
tr->Pid = pid;
tr->Tid = 0; // filled below
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
@@ -86,12 +89,9 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
SysFree(tr, sizeof(PTHREAD_TRAMP));
return EAGAIN;
}
tr->Tid = tid; // safe: trampoline reads Tid after SysCreateThread returns
// Reserve a retval slot before the trampoline can exit
PthreadSlotAlloc(pid, tid);
t->Tid = tid;
// The trampoline allocates the retval slot itself after it knows its real
// TID, avoiding the creator/new-thread race.
if (t) t->Tid = tid;
return 0;
}
+32 -3
View File
@@ -123,6 +123,27 @@ static void cmd_type(const char* filename) {
SysCloseHandle(hFile); print("\n");
}
static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
if (!cmdline || !out || outSize == 0) return false;
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return false;
size_t pos = 0;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
out[pos++] = *cmdline++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
out[pos++] = *cmdline++;
}
out[pos] = '\0';
return pos != 0;
}
// ---------------------------------------------------------------------------
// Run one command
// ---------------------------------------------------------------------------
@@ -134,13 +155,16 @@ static int runCommand(const char* cmd) {
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { SysSetCwd(cmd + 3); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { if (SysSetCwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { SysDeleteFile(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
char program[MAX_CMD];
if (!extractProgramPath(cmd, program, sizeof(program))) return 0;
uint32_t hProc;
if (SysCreateProcess(cmd, &hProc, cmd) == 0) {
if (SysCreateProcess(program, &hProc, cmd) == 0) {
SysWaitForProcess(hProc);
SysCloseHandle(hProc);
while (SysKbPoll()) (void)SysKbReadChar();
@@ -148,8 +172,13 @@ static int runCommand(const char* cmd) {
return 0;
}
int main(int argc, char** argv) {
int main(void) {
const char* cmdline = SysGetCommandLine();
char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n");
while (true) {
print("> ");
+48 -6
View File
@@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <syscall.h>
#define MAX_CMD 256
#define NAME_MAX 259
@@ -21,7 +22,8 @@ extern int unlink(const char *pathname);
extern int posix_spawn(int *pid, const char *path, const void *act, const void *attr, char *const argv[], char *const envp[]);
extern int waitpid(int pid, int *status, int options);
extern void exit(int status);
extern int ClearConsoleScreenBuffer(void* h);
extern void clearscreen(void);
extern void clearkeyboard(void);
extern void PosixFdInit(void);
static int hIn = 0; static int hOut = 1;
@@ -52,6 +54,31 @@ static void readLine(char* buf, int len) {
buf[pos] = '\0';
}
static int splitCommandLine(char* line, char** argv, int maxArgv) {
int argc = 0;
char* p = line;
while (*p && argc < maxArgv - 1) {
while (*p == ' ' || *p == '\t') p++;
if (!*p) break;
if (*p == '"') {
p++;
argv[argc++] = p;
while (*p && *p != '"') p++;
if (*p) *p++ = '\0';
} else {
argv[argc++] = p;
while (*p && *p != ' ' && *p != '\t') p++;
if (*p) *p++ = '\0';
}
}
argv[argc] = NULL;
return argc;
}
static char* trim(char* s) {
while (*s == ' ') s++;
int len = (int)strlen(s);
@@ -111,25 +138,40 @@ static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return 0;
if (strcmp(cmd, "exit") == 0) return 1;
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(0); return 0; }
if (strcmp(cmd, "clear") == 0) { clearscreen(); return 0; }
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { chdir(cmd + 3); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { if (chdir(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
int pid; char *argv[] = {(char*)cmd, NULL};
if (posix_spawn(&pid, cmd, NULL, NULL, argv, NULL) == 0) {
char cmdBuf[MAX_CMD];
strncpy(cmdBuf, cmd, MAX_CMD - 1);
cmdBuf[MAX_CMD - 1] = '\0';
char* argv[32];
int argc = splitCommandLine(cmdBuf, argv, 32);
if (argc == 0) return 0;
int pid;
if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL) == 0) {
int status; waitpid(pid, &status, 0);
clearkeyboard();
} else { print("shell: launch failed\n"); }
return 0;
}
int main() {
int main(void) {
PosixFdInit();
const char* cmdline = SysGetCommandLine();
char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n");
while (true) {
print("> ");
+12 -3
View File
@@ -3,6 +3,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <syscall.h>
#define MAX_CMD 256
static HANDLE hIn = INVALID_HANDLE_VALUE;
@@ -126,7 +127,7 @@ static int runCommand(const char* cmd) {
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { SetCurrentDirectoryA(cmd + 3); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { if (!SetCurrentDirectoryA(cmd + 3)) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
@@ -137,17 +138,25 @@ static int runCommand(const char* cmd) {
cmdBuf[MAX_CMD - 1] = '\0';
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
CloseHandle(pi.hThread);
FlushConsoleInputBuffer(hIn);
} else {
print("shell: launch failed\n");
}
return 0;
}
int main() {
int main(void) {
hIn = GetStdHandle(STD_INPUT_HANDLE);
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
const char* cmdline = SysGetCommandLine();
char lineBuf[MAX_CMD];
print("shell: command line: ");
print(cmdline);
print("\n");
print("shell: ready\n");
while (true) {
print("> ");
+50
View File
@@ -4,6 +4,56 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <intrin.h>
// ---------------------------------------------------------------------------
// User process/thread environment
// ---------------------------------------------------------------------------
// GS base points at SYS_TEB. GS:[0] is the process PEB pointer.
// This is this OS' ABI, not a Windows compatibility layout.
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
#define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 512u
typedef struct SYS_PEB {
uint32_t Magic;
uint32_t Version;
uint32_t ProcessId;
const char* ImagePath;
size_t ImagePathLength;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
} SYS_PEB;
typedef struct SYS_TEB {
SYS_PEB* Peb;
void* StackBase;
void* StackLimit;
struct SYS_TEB* Self;
uint32_t ThreadId;
uint32_t Magic;
} SYS_TEB;
static inline SYS_PEB* SysGetPeb(void) {
return (SYS_PEB*)(uintptr_t)__readgsqword(0);
}
static inline SYS_TEB* SysGetTeb(void) {
return (SYS_TEB*)(uintptr_t)__readgsqword(offsetof(SYS_TEB, Self));
}
static inline const char* SysGetCommandLine(void) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC) return "";
return peb->CommandLine ? peb->CommandLine : "";
}
// ---------------------------------------------------------------------------
// Thread & Process Management (0-7)
+40 -4
View File
@@ -5,16 +5,52 @@
#include <intrin.h>
#include "bink.h"
int main(const char* cmdline) {
static const char* commandLineSecondToken(const char* cmdline, char* out, size_t outSize) {
if (!out || outSize == 0) return "video.bk2";
strcpy(out, "video.bk2");
if (!cmdline) return out;
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return out;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"') cmdline++;
if (*cmdline == '"') cmdline++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t') cmdline++;
}
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return out;
size_t pos = 0;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
out[pos++] = *cmdline++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
out[pos++] = *cmdline++;
}
out[pos] = '\0';
return out;
}
int main(void) {
const char* cmdline = GetCommandLineA();
char videoPath[260];
const char* path = commandLineSecondToken(cmdline, videoPath, sizeof(videoPath));
void* h = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(h, "video: starting\n", 16, 0, 0);
WriteConsoleA(h, cmdline, (unsigned int)strlen(cmdline), 0, 0);
WriteConsoleA(h, "\n", 1, 0, 0);
void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0);
void* f = CreateFileA(path, 0, 0, 0, 3, 0, 0);
if (!f || f == (void*)(unsigned long long)0xFFFFFFFF) { // fuckass
WriteConsoleA(h, "video: failed to open video.bk2\n", 32, 0, 0);
WriteConsoleA(h, "video: failed to open input file\n", 33, 0, 0);
ExitProcess(1);
}
@@ -34,7 +70,7 @@ int main(const char* cmdline) {
WriteConsoleA(h, "video: ReadFile failed\n", 23, 0, 0);
ExitProcess(1);
}
WriteConsoleA(h, "video: video.bk2 loaded\n", 24, 0, 0);
WriteConsoleA(h, "video: input file loaded\n", 25, 0, 0);
CloseHandle(h);
+191 -45
View File
@@ -76,9 +76,6 @@ typedef struct _WIN32_FIND_DATAA {
#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
@@ -87,9 +84,32 @@ typedef struct _WIN32_FIND_DATAA {
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; }
// Thread IDs are not kernel handles. Keep Win32 thread pseudo-handles out of
// the low numeric handle namespace so CloseHandle/WaitForSingleObject cannot
// accidentally operate on an unrelated file/process handle with the same value.
#define WIN32_THREAD_HANDLE_TAG ((uintptr_t)1ULL << 63)
#define WIN32_MUTEX_SENTINEL ((uintptr_t)0x4D555458UL) // 'MUTX'
#define WIN32_SEMA_SENTINEL ((uintptr_t)0x53454D41UL) // 'SEMA'
static inline HANDLE TID_TO_THREAD_HANDLE(uint32_t tid) {
return (HANDLE)(WIN32_THREAD_HANDLE_TAG | (uintptr_t)tid);
}
static inline BOOL IS_THREAD_HANDLE(HANDLE h) {
return h && h != INVALID_HANDLE_VALUE && (((uintptr_t)h & WIN32_THREAD_HANDLE_TAG) != 0);
}
static inline uint32_t THREAD_HANDLE_TO_TID(HANDLE h) {
return (uint32_t)((uintptr_t)h & 0xFFFFFFFFu);
}
static inline BOOL IS_NOOP_SYNC_HANDLE(HANDLE h) {
uintptr_t v = (uintptr_t)h;
return v == WIN32_MUTEX_SENTINEL || v == WIN32_SEMA_SENTINEL;
}
// Path translator
static void TranslatePath(const char* input, char* output) {
if (input && input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) {
if (!output) return;
if (!input) { output[0] = '\0'; return; }
if (input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) {
// Translate "C:\" -> "0:/" (assuming 'C' maps to '0', etc.)
// Map drive letter to index: 'C' or 'c' -> '0'
char drive = input[0];
@@ -122,6 +142,13 @@ static void TranslatePath(const char* input, char* output) {
// ---------------------------------------------------------------------------
static DWORD gLastError = 0;
static SYS_PEB* gProcessPeb = NULL;
static SYS_PEB* Win32Peb(void) {
if (!gProcessPeb) gProcessPeb = SysGetPeb();
return gProcessPeb;
}
__declspec(dllexport) DWORD GetLastError(void) {
return gLastError;
}
@@ -162,8 +189,6 @@ __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;
}
@@ -395,7 +420,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
// 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 || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
@@ -405,11 +430,26 @@ __declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
__declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) {
(void)overlapped;
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; }
if (bytesRead) *bytesRead = 0;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(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; }
// Harden callers that accidentally look past *bytesRead. EOF/short reads
// are legal for files, but consuming stale stack/heap bytes after them makes
// the failure mode look like random game-data corruption.
if (got < (size_t)toRead)
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
if (s != 0) {
gLastError = 1;
return FALSE;
}
return TRUE;
}
@@ -418,7 +458,7 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)overlapped;
if (!hFile || !buf) { gLastError = 6; return FALSE; }
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) { gLastError = 6; return FALSE; }
size_t written = 0;
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
if (bytesWritten) *bytesWritten = (DWORD)written;
@@ -430,27 +470,41 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit
// Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t offset = 0;
int64_t delta = distHigh
? ((int64_t)(uint64_t)(uint32_t)*distHigh << 32) | (uint32_t)dist
: (int64_t)dist;
int64_t delta;
if (distHigh) {
uint64_t combined = ((uint64_t)(uint32_t)(*distHigh) << 32) | (uint32_t)dist;
delta = (int64_t)combined;
} else {
delta = (int64_t)dist;
}
int64_t target = 0;
if (method == FILE_BEGIN) {
offset = (uint64_t)delta;
target = delta;
} else if (method == FILE_CURRENT) {
uint64_t cur = SysGetFilePos(kh);
if (cur == (uint64_t)-1) { gLastError = 6; return 0xFFFFFFFFUL; }
offset = (uint64_t)((int64_t)cur + delta);
if (cur == (uint64_t)-1 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)cur + delta;
} else if (method == FILE_END) {
uint64_t sz = SysGetFileSize(kh);
offset = (uint64_t)((int64_t)sz + delta);
if (sz > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)sz + delta;
} else {
gLastError = 87; return 0xFFFFFFFFUL;
}
if (target < 0) {
gLastError = 131; // ERROR_NEGATIVE_SEEK
return 0xFFFFFFFFUL;
}
uint64_t offset = (uint64_t)target;
uint32_t s = SysSeekFile(kh, offset);
if (s != 0) { gLastError = 1; return 0xFFFFFFFFUL; }
if (distHigh) *distHigh = (long)(int32_t)(offset >> 32);
@@ -459,7 +513,7 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
// SetEndOfFile - truncate or extend the file to the current seek position.
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
@@ -482,7 +536,12 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject) { gLastError = 6; return FALSE; }
if (!hObject || hObject == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
// Our thread/sentinel sync handles are not entries in the kernel handle table.
// There is nothing to close; the thread object is reaped by the scheduler.
if (IS_THREAD_HANDLE(hObject) || IS_NOOP_SYNC_HANDLE(hObject)) return TRUE;
SysCloseHandle(HANDLE_TO_KH(hObject));
return TRUE;
}
@@ -607,9 +666,9 @@ __declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
}
__declspec(dllexport) char* GetCommandLineA(void) {
// Return a valid but empty-ish command line.
static char cmdline[4] = "";
return cmdline;
SYS_PEB* peb = Win32Peb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
return (char*)peb->CommandLine;
}
// ---------------------------------------------------------------------------
@@ -633,16 +692,51 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
0 /* normal priority */, &tid);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid;
return KH_TO_HANDLE(tid);
return TID_TO_THREAD_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
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
if (IS_THREAD_HANDLE(hHandle)) {
uint32_t tid = THREAD_HANDLE_TO_TID(hHandle);
uint32_t s = SysWaitThread(tid);
if (s != 0 && s != 0x0022) { gLastError = 6; return 0xFFFFFFFFUL; }
return WAIT_OBJECT_0;
}
if (IS_NOOP_SYNC_HANDLE(hHandle)) return WAIT_OBJECT_0;
uint32_t kh = HANDLE_TO_KH(hHandle);
if (kh) {
uint32_t s = SysWaitForProcess(kh);
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
}
return WAIT_OBJECT_0;
}
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
if (!commandLine || !out || outSize == 0) return FALSE;
while (*commandLine == ' ' || *commandLine == '\t') commandLine++;
if (*commandLine == '\0') return FALSE;
size_t pos = 0;
if (*commandLine == '"') {
commandLine++;
while (*commandLine && *commandLine != '"' && pos + 1 < outSize)
out[pos++] = *commandLine++;
} else {
while (*commandLine && *commandLine != ' ' && *commandLine != '\t' && pos + 1 < outSize)
out[pos++] = *commandLine++;
}
out[pos] = '\0';
return pos != 0;
}
// ---------------------------------------------------------------------------
@@ -669,9 +763,9 @@ __declspec(dllexport) BOOL CreateProcessA(
const char* lpCurrentDirectory,
STARTUPINFOA* lpStartupInfo,
PROCESS_INFORMATION* lpProcessInformation) {
(void)lpCommandLine; (void)lpProcessAttributes; (void)lpThreadAttributes;
(void)lpProcessAttributes; (void)lpThreadAttributes;
(void)bInheritHandles; (void)dwCreationFlags; (void)lpEnvironment;
(void)lpCurrentDirectory; (void)lpStartupInfo;
(void)lpStartupInfo;
// lpProcessInformation is mandatory
if (!lpProcessInformation) {
@@ -679,27 +773,56 @@ __declspec(dllexport) BOOL CreateProcessA(
return FALSE;
}
// Standard Win32 behavior: use lpCommandLine if lpApplicationName is NULL
char appPath[MAX_PATH];
const char* targetPath = lpApplicationName;
if (!targetPath) {
targetPath = lpCommandLine;
}
// If both are NULL, then it's truly an invalid parameter
if (!targetPath) {
if (!ExtractApplicationPathA(lpCommandLine, appPath, sizeof(appPath))) {
gLastError = 87;
return FALSE;
}
targetPath = appPath;
}
// Build the cmdline: prefer lpCommandLine if given, otherwise fall back to path
const char* cmdline = lpCommandLine ? lpCommandLine : targetPath;
const char* cmdline = (lpCommandLine && lpCommandLine[0]) ? lpCommandLine : targetPath;
char translatedPath[MAX_PATH];
TranslatePath(targetPath, translatedPath);
char savedCwd[SYS_CWD_MAX];
bool changedCwd = false;
savedCwd[0] = '\0';
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
char translatedCwd[MAX_PATH];
uint32_t gs = SysGetCwd(savedCwd, sizeof(savedCwd));
if (gs != 0) { gLastError = 3; return FALSE; }
TranslatePath(lpCurrentDirectory, translatedCwd);
gs = SysSetCwd(translatedCwd);
if (gs != 0) { gLastError = 3; return FALSE; }
changedCwd = true;
}
uint32_t kh = 0;
// Call the syscall.dll stub
// Call the syscall.dll stub. The kernel snapshots the current CWD into
// the child process, so the temporary lpCurrentDirectory switch can be
// restored immediately after creation.
uint32_t s = SysCreateProcess(translatedPath, &kh, cmdline);
if (changedCwd) {
if (savedCwd[0]) {
char restoreCwd[SYS_CWD_MAX + 3];
restoreCwd[0] = '0'; restoreCwd[1] = ':'; restoreCwd[2] = '/';
size_t rlen = strlen(savedCwd);
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
memcpy(restoreCwd + 3, savedCwd, rlen);
restoreCwd[3 + rlen] = '\0';
SysSetCwd(restoreCwd);
} else {
SysSetCwd("0:/");
}
}
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return FALSE;
@@ -715,7 +838,7 @@ __declspec(dllexport) BOOL CreateProcessA(
__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
if (!kh || IS_THREAD_HANDLE(hProcess)) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
int code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
@@ -1078,7 +1201,7 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
return (HANDLE)(uintptr_t)0x4D555458UL; // 'MUTX' sentinel
return (HANDLE)WIN32_MUTEX_SENTINEL;
}
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
@@ -1091,7 +1214,7 @@ __declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
const char* lpName) {
(void)lpSemaphoreAttributes; (void)lInitialCount;
(void)lMaximumCount; (void)lpName;
return (HANDLE)(uintptr_t)0x53454D41UL; // 'SEMA' sentinel
return (HANDLE)WIN32_SEMA_SENTINEL;
}
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
@@ -1303,17 +1426,40 @@ __declspec(dllexport) unsigned int GetWindowsDirectoryA(char* lpBuffer, unsigned
return needed - 1;
}
static const char* LookupStaticEnvA(const char* name) {
if (!name) return NULL;
if (strcmp(name, "HOME") == 0) return ".";
if (strcmp(name, "USERPROFILE") == 0) return ".";
if (strcmp(name, "HOMEDRIVE") == 0) return "C:";
if (strcmp(name, "HOMEPATH") == 0) return "\\";
return NULL;
}
__declspec(dllexport) DWORD GetEnvironmentVariableA(const char* lpName, char* lpBuffer,
DWORD nSize) {
(void)lpName; (void)lpBuffer; (void)nSize;
const char* value = LookupStaticEnvA(lpName);
if (!value) {
if (lpBuffer && nSize) lpBuffer[0] = '\0';
gLastError = 203; // ERROR_ENVVAR_NOT_FOUND
return 0;
}
DWORD len = (DWORD)strlen(value);
if (!lpBuffer || nSize == 0) return len;
if (len + 1 > nSize) {
if (nSize) lpBuffer[0] = '\0';
gLastError = 122; // ERROR_INSUFFICIENT_BUFFER
return len + 1;
}
memcpy(lpBuffer, value, len + 1);
return len;
}
__declspec(dllexport) char* GetEnvironmentStrings(void) {
// Return a double-NUL-terminated empty environment block.
static char emptyEnv[2] = { '\0', '\0' };
return emptyEnv;
// Double-NUL-terminated environment block.
static char env[] = "HOME=.\0USERPROFILE=.\0HOMEDRIVE=C:\0HOMEPATH=\\\0\0";
return env;
}
__declspec(dllexport) BOOL FreeEnvironmentStringsA(char* penv) {
+13 -6
View File
@@ -1,5 +1,7 @@
# NT-Lite -- an x86-64 kernel
## The Monster From ~~Redmond~~ Kraków
Boots. Runs processes. Mimics Windows to some extent.
## what is it?
@@ -17,7 +19,7 @@ The codebase is roughly 20k lines of C and assembly.
- preemptive scheduler with priority levels
- processes and threads
- VFS with IRPs
- drive-letter paths such as `0:\apps\shell.exe`
- drive-letter paths such as `0:/apps/shell.exe`
- INI-based boot and driver configuration
- PE import resolution and dynamic driver loading
- partial Win32 personality
@@ -52,7 +54,7 @@ Currently shipping: AHCI (interrupts), IDE (PIO/DMA), and FAT32.
All file access flows through the VFS layer. That keeps disk access, filesystem parsing, and path translation separate instead of spreading storage logic across the rest of the kernel.
Paths are not interpreted as Windows paths by default. A drive such as `0:\` is a mounted namespace, while the Win32 and POSIX personalities translate their own path styles into the same underlying VFS.
Paths are not interpreted as Windows paths by default. A drive such as `0:/` is a mounted namespace, while the Win32 and POSIX personalities translate their own path styles into the same underlying VFS.
### processes, threads, and syscalls
@@ -84,9 +86,9 @@ The Win32 personality supports programs that expect drive letters, familiar proc
## paths
```text
"apps\shell.exe" -> current working directory
"0:\apps\shell.exe" -> drive 0
"2:\data\config.ini" -> drive 2
"apps/shell.exe" -> current working directory
"0:/apps/shell.exe" -> drive 0
"2:/data/config.ini" -> drive 2
```
Drives 0-7 are available. A drive only works if a filesystem is registered on it. The Win32 personality translates drive letters to drive numbers, while the POSIX personality maps drives onto a tree.
@@ -290,6 +292,9 @@ A: Learning. Fun.
Q: Does it run Doom?
A: `doom.exe`
Q: How often does it crash?
A: Yes.
Q: Is the Win32 layer real?
A: Real enough to load binaries that expect it. Not real enough to run Notepad.
@@ -299,7 +304,7 @@ A: No. But IPC is free.
Q: Is this finished?
A: No.
Will it corrupt my data?
Q: Will it corrupt my data?
A: It shouldn't.
Q: Why is the path syntax so weird?
@@ -313,3 +318,5 @@ A: This document.
Q: Can I use this in production?
A: Don't.
#### Projekt dedykowany dla szanownego Pana profesora Leszka Wójcika przez którego znienawidziłem Unixa.