From 603fbdfab1d6da1341f307a4475930f5855f6849 Mon Sep 17 00:00:00 2001 From: Maksymilian Wilczynski Date: Sat, 23 May 2026 21:57:36 +0200 Subject: [PATCH] revolutionary crash dump, msvcrt.dll redirect, text editor, some win32 functions --- OVMF/OVMF_VARS_4M_d.fd | Bin 540672 -> 540672 bytes build.bat | 26 +- code/edit/edit.c | 491 +++++++++++++++++++++++++++++++++++++ code/kernel/ke_debug.c | 338 +++++++++++++++++++++---- code/kernel/ke_debug.h | 83 +++++-- code/kernel/ke_syscall.asm | 250 +++++++++++++++---- code/kernel/ldr_image.c | 100 ++++++-- code/libc.c | 9 + code/win32/kernel32.c | 295 ++++++++++++++++++++-- 9 files changed, 1432 insertions(+), 160 deletions(-) create mode 100644 code/edit/edit.c diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index c009ed37c635c569f393840a97f3c676c1f1f210..f0ac529422d060331ab45e0fae2f992c64af862e 100644 GIT binary patch delta 218 zcmZo@P;3CAEsR;3ll_96wy)7-yu`KnUG6Sc#;nZ~gSrJ6vmsOtgvy0bc@QceLKQ%$ zLI_m^p^70?34|(zP-PIR970tnul mkdir "obj\shell_native" 2>nul mkdir "obj\async_apc" 2>nul mkdir "obj\async_cancel" 2>nul -mkdir "obj\httpget" 2>nul +mkdir "obj\edit" 2>nul +mkdir "obj\httpget" 2>nul mkdir "obj\tcpserve" 2>nul -mkdir "obj\udp" 2>nul -mkdir "obj\resolve" 2>nul -mkdir "obj\ping" 2>nul -mkdir "obj\doom" 2>nul -mkdir "obj\video" 2>nul -mkdir "bin" 2>nul +mkdir "obj\udp" 2>nul +mkdir "obj\resolve" 2>nul +mkdir "obj\ping" 2>nul +mkdir "obj\doom" 2>nul +mkdir "obj\video" 2>nul +mkdir "bin" 2>nul set CFLAGS=/nologo /W3 /WX /Od /GS- /Gs999999 /Gz /Gy /EHsc- /GR- /Oi /Zi /std:c17 /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /I"%SRC%\kernel" /I"%SRC%\hal" /I"%SRC%\syscall" set KERNEL_CFLAGS=%CFLAGS% @@ -204,6 +205,14 @@ for %%f in (%SRC%\async_cancel\*.c) do ( link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ /OUT:"bin\async_cancel.exe" /PDB:"bin\async_cancel.pdb" bin\syscall.lib obj\libc.obj obj\async_cancel\*.obj || exit /b 1 +:: ---- Compile edit ---- +echo Compiling edit... +for %%f in (%SRC%\edit\*.c) do ( + cl %CFLAGS% /c /Fo"obj\edit\%%~nf.obj" "%%f" || exit /b 1 +) +link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ + /OUT:"bin\edit.exe" /PDB:"bin\edit.pdb" bin\syscall.lib obj\libc.obj obj\edit\*.obj || exit /b 1 + :: ---- Compile httpget ---- echo Compiling httpget... for %%f in (%SRC%\httpget\*.c) do ( @@ -312,11 +321,12 @@ copy /y "bin\shell_posix.exe" "%DEST%\apps\" copy /y "bin\shell_native.exe" "%DEST%\apps\" copy /y "bin\async_apc.exe" "%DEST2%\apps\" copy /y "bin\async_cancel.exe" "%DEST2%\apps\" +copy /y "bin\edit.exe" "%DEST%\apps\" copy /y "bin\httpget.exe" "%DEST%\apps\" copy /y "bin\tcpserve.exe" "%DEST%\apps\" copy /y "bin\udp.exe" "%DEST%\apps\" copy /y "bin\resolve.exe" "%DEST%\apps\" -copy /y "bin\ping.exe" "%DEST%\apps\" +copy /y "bin\ping.exe" "%DEST%\apps\" copy /y "bin\doom.exe" "%DEST%\apps\" copy /y "bin\video.exe" "%DEST%\apps\" copy /y "%SRC%\boot.ini" "%DEST%\" diff --git a/code/edit/edit.c b/code/edit/edit.c new file mode 100644 index 0000000..dba61bb --- /dev/null +++ b/code/edit/edit.c @@ -0,0 +1,491 @@ +// edit.c - tiny native text editor for nt-lite +// Controls: +// arrows/home/end/pageup/pagedown move +// enter/backspace/delete/tab edit +// ctrl+s save +// ctrl+q quit (press twice if dirty) + +#include +#include +#include +#include +#include + +#define GLYPH_W 8 +#define GLYPH_H 16 + +#define MAX_LINES 512 +#define MAX_LINE 256 +#define PATH_MAX2 260 +#define MSG_MAX 160 + +#define COL_BG 0x000000u +#define COL_FG 0xC8C8C8u +#define COL_DIM 0x808080u +#define COL_STATUS 0x303030u +#define COL_STATUSF 0xFFFFFFu +#define COL_CURSOR 0xE0E0E0u +#define COL_CURSORF 0x000000u + +#define MOD_LSHIFT (1u << 0) +#define MOD_RSHIFT (1u << 1) +#define MOD_LCTRL (1u << 2) +#define MOD_RCTRL (1u << 3) +#define MOD_CAPS (1u << 6) +#define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT) +#define MOD_CTRL (MOD_LCTRL | MOD_RCTRL) + +#define SC_ESC 0x01u +#define SC_BACKSPACE 0x0Eu +#define SC_TAB 0x0Fu +#define SC_Q 0x10u +#define SC_S 0x1Fu +#define SC_ENTER 0x1Cu +#define SC_HOME 0x47u +#define SC_UP 0x48u +#define SC_PGUP 0x49u +#define SC_LEFT 0x4Bu +#define SC_RIGHT 0x4Du +#define SC_END 0x4Fu +#define SC_DOWN 0x50u +#define SC_PGDN 0x51u +#define SC_DEL 0x53u + +typedef struct LINE { + int len; + char text[MAX_LINE]; +} LINE; + +static LINE gLines[MAX_LINES]; +static int gLineCount = 1; +static int gCx = 0; +static int gCy = 0; +static int gTop = 0; +static int gLeft = 0; +static int gCols = 80; +static int gRows = 25; +static bool gDirty = false; +static bool gQuitArmed = false; +static char gFilename[PATH_MAX2] = "untitled.txt"; +static char gMessage[MSG_MAX] = "ready"; + +static const char gUnshifted[128] = { + 0, 0, '1','2','3','4','5','6','7','8','9','0','-','=','\b','\t', + 'q','w','e','r','t','y','u','i','o','p','[',']','\n', 0, 'a','s', + 'd','f','g','h','j','k','l',';','\'','`', 0, '\\','z','x','c','v', + 'b','n','m',',','.','/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.' +}; + +static const char gShifted[128] = { + 0, 0, '!','@','#','$','%','^','&','*','(',')','_','+','\b','\t', + 'Q','W','E','R','T','Y','U','I','O','P','{','}','\n', 0, 'A','S', + 'D','F','G','H','J','K','L',':','"','~', 0, '|','Z','X','C','V', + 'B','N','M','<','>','?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.' +}; + +static void put_cell(int col, int row, char c, uint32_t fg, uint32_t bg) { + SysScreenPutChar(c ? c : ' ', col * GLYPH_W, row * GLYPH_H, fg, bg); +} + +static void clear_row(int row, uint32_t bg) { + for (int x = 0; x < gCols; x++) put_cell(x, row, ' ', COL_FG, bg); +} + +static void draw_text(int col, int row, const char* s, uint32_t fg, uint32_t bg) { + while (*s && col < gCols) put_cell(col++, row, *s++, fg, bg); +} + +static void msg_set(const char* s) { + size_t n = strlen(s); + if (n >= sizeof(gMessage)) n = sizeof(gMessage) - 1; + memcpy(gMessage, s, n); + gMessage[n] = 0; +} + +static void append_char(char* b, size_t cap, size_t* p, char c) { + if (*p + 1 < cap) b[(*p)++] = c; + b[*p < cap ? *p : cap - 1] = 0; +} + +static void append_str(char* b, size_t cap, size_t* p, const char* s) { + while (*s) append_char(b, cap, p, *s++); +} + +static void append_u32(char* b, size_t cap, size_t* p, uint32_t v) { + char tmp[12]; + int n = 0; + if (v == 0) { append_char(b, cap, p, '0'); return; } + while (v && n < (int)sizeof(tmp)) { + tmp[n++] = (char)('0' + (v % 10u)); + v /= 10u; + } + while (n > 0) append_char(b, cap, p, tmp[--n]); +} + +static char key_to_char(uint8_t sc, uint32_t mods) { + if (sc >= 128) return 0; + bool shifted = (mods & MOD_SHIFT) != 0; + if (mods & MOD_CAPS) { + char base = gUnshifted[sc]; + if (base >= 'a' && base <= 'z') shifted = !shifted; + } + return shifted ? gShifted[sc] : gUnshifted[sc]; +} + +static void clamp_cursor(void) { + if (gLineCount < 1) gLineCount = 1; + if (gCy < 0) gCy = 0; + if (gCy >= gLineCount) gCy = gLineCount - 1; + if (gCx < 0) gCx = 0; + if (gCx > gLines[gCy].len) gCx = gLines[gCy].len; +} + +static void ensure_visible(void) { + int textRows = gRows - 2; + if (textRows < 1) textRows = 1; + + if (gCy < gTop) gTop = gCy; + if (gCy >= gTop + textRows) gTop = gCy - textRows + 1; + if (gTop < 0) gTop = 0; + + if (gCx < gLeft) gLeft = gCx; + if (gCx >= gLeft + gCols) gLeft = gCx - gCols + 1; + if (gLeft < 0) gLeft = 0; +} + +static void render(void) { + int pxW = 0, pxH = 0; + if (SysScreenGetSize(&pxW, &pxH) == 0 && pxW > 0 && pxH > 0) { + gCols = pxW / GLYPH_W; + gRows = pxH / GLYPH_H; + if (gCols < 20) gCols = 20; + if (gRows < 5) gRows = 5; + } + + clamp_cursor(); + ensure_visible(); + + char status[256]; + size_t p = 0; + append_str(status, sizeof(status), &p, " edit "); + append_str(status, sizeof(status), &p, gFilename); + append_str(status, sizeof(status), &p, gDirty ? " * " : " "); + append_str(status, sizeof(status), &p, "Ctrl+S save Ctrl+Q quit Ln "); + append_u32(status, sizeof(status), &p, (uint32_t)(gCy + 1)); + append_str(status, sizeof(status), &p, ", Col "); + append_u32(status, sizeof(status), &p, (uint32_t)(gCx + 1)); + + clear_row(0, COL_STATUS); + draw_text(0, 0, status, COL_STATUSF, COL_STATUS); + + int textRows = gRows - 2; + for (int sy = 0; sy < textRows; sy++) { + int row = sy + 1; + int li = gTop + sy; + clear_row(row, COL_BG); + if (li >= gLineCount) continue; + + LINE* ln = &gLines[li]; + for (int sx = 0; sx < gCols; sx++) { + int ci = gLeft + sx; + char c = (ci < ln->len) ? ln->text[ci] : ' '; + bool atCursor = (li == gCy && ci == gCx); + put_cell(sx, row, c, atCursor ? COL_CURSORF : COL_FG, + atCursor ? COL_CURSOR : COL_BG); + } + } + + clear_row(gRows - 1, COL_STATUS); + draw_text(0, gRows - 1, gMessage, COL_STATUSF, COL_STATUS); +} + +static void insert_char_at_cursor(char c) { + LINE* ln = &gLines[gCy]; + if (ln->len >= MAX_LINE - 1) { msg_set("line too long"); return; } + memmove(&ln->text[gCx + 1], &ln->text[gCx], (size_t)(ln->len - gCx + 1)); + ln->text[gCx] = c; + ln->len++; + gCx++; + gDirty = true; +} + +static void newline_at_cursor(void) { + if (gLineCount >= MAX_LINES) { msg_set("too many lines"); return; } + + for (int i = gLineCount; i > gCy + 1; i--) gLines[i] = gLines[i - 1]; + + LINE* cur = &gLines[gCy]; + LINE* next = &gLines[gCy + 1]; + int tail = cur->len - gCx; + if (tail > 0) memcpy(next->text, &cur->text[gCx], (size_t)tail); + next->len = tail; + next->text[next->len] = 0; + + cur->len = gCx; + cur->text[cur->len] = 0; + + gLineCount++; + gCy++; + gCx = 0; + gDirty = true; +} + +static void backspace_at_cursor(void) { + if (gCx > 0) { + LINE* ln = &gLines[gCy]; + memmove(&ln->text[gCx - 1], &ln->text[gCx], (size_t)(ln->len - gCx + 1)); + ln->len--; + gCx--; + gDirty = true; + return; + } + + if (gCy <= 0) return; + + LINE* prev = &gLines[gCy - 1]; + LINE* cur = &gLines[gCy]; + if (prev->len + cur->len >= MAX_LINE) { msg_set("join would make line too long"); return; } + + int oldLen = prev->len; + memcpy(&prev->text[prev->len], cur->text, (size_t)cur->len + 1); + prev->len += cur->len; + + for (int i = gCy; i < gLineCount - 1; i++) gLines[i] = gLines[i + 1]; + gLineCount--; + gCy--; + gCx = oldLen; + gDirty = true; +} + +static void delete_at_cursor(void) { + LINE* ln = &gLines[gCy]; + if (gCx < ln->len) { + memmove(&ln->text[gCx], &ln->text[gCx + 1], (size_t)(ln->len - gCx)); + ln->len--; + gDirty = true; + return; + } + + if (gCy + 1 >= gLineCount) return; + + LINE* next = &gLines[gCy + 1]; + if (ln->len + next->len >= MAX_LINE) { msg_set("join would make line too long"); return; } + + memcpy(&ln->text[ln->len], next->text, (size_t)next->len + 1); + ln->len += next->len; + for (int i = gCy + 1; i < gLineCount - 1; i++) gLines[i] = gLines[i + 1]; + gLineCount--; + gDirty = true; +} + +static void move_left(void) { + if (gCx > 0) gCx--; + else if (gCy > 0) { gCy--; gCx = gLines[gCy].len; } +} + +static void move_right(void) { + if (gCx < gLines[gCy].len) gCx++; + else if (gCy + 1 < gLineCount) { gCy++; gCx = 0; } +} + +static void move_up(void) { + if (gCy > 0) gCy--; + if (gCx > gLines[gCy].len) gCx = gLines[gCy].len; +} + +static void move_down(void) { + if (gCy + 1 < gLineCount) gCy++; + if (gCx > gLines[gCy].len) gCx = gLines[gCy].len; +} + +static void save_file(void) { + uint32_t h = 0; + uint32_t st = SysCreateFile(gFilename, &h); + if (st != 0) st = SysOpenFile(gFilename, &h); + if (st != 0) { msg_set("save failed: cannot open file"); return; } + + SysTruncateFile(h, 0); + SysSeekFile(h, 0); + + for (int i = 0; i < gLineCount; i++) { + size_t written = 0; + if (gLines[i].len) { + st = SysWriteFile(h, gLines[i].text, (size_t)gLines[i].len, &written); + if (st != 0) { SysCloseHandle(h); msg_set("save failed: write error"); return; } + } + if (i + 1 < gLineCount) { + const char nl = '\n'; + st = SysWriteFile(h, &nl, 1, &written); + if (st != 0) { SysCloseHandle(h); msg_set("save failed: write error"); return; } + } + } + + SysCloseHandle(h); + gDirty = false; + msg_set("saved"); +} + +static void reset_buffer(void) { + gLineCount = 1; + gLines[0].len = 0; + gLines[0].text[0] = 0; + gCx = gCy = gTop = gLeft = 0; + gDirty = false; +} + +static void load_file(void) { + reset_buffer(); + + uint32_t h = 0; + if (SysOpenFile(gFilename, &h) != 0) { + msg_set("new file"); + return; + } + + char buf[512]; + size_t got = 0; + bool truncated = false; + while (SysReadFile(h, buf, sizeof(buf), &got) == 0 && got > 0) { + for (size_t i = 0; i < got; i++) { + char c = buf[i]; + if (c == '\r') continue; + if (c == '\n') { + if (gLineCount < MAX_LINES) { + gLineCount++; + gLines[gLineCount - 1].len = 0; + gLines[gLineCount - 1].text[0] = 0; + } else truncated = true; + continue; + } + if (c < 0x20 && c != '\t') continue; + LINE* ln = &gLines[gLineCount - 1]; + if (ln->len + 1 < MAX_LINE) { + ln->text[ln->len++] = (c == '\t') ? ' ' : c; + ln->text[ln->len] = 0; + } else truncated = true; + } + } + SysCloseHandle(h); + msg_set(truncated ? "loaded, truncated long file" : "loaded"); +} + +static const char* skip_spaces(const char* s) { + while (s && (*s == ' ' || *s == '\t')) s++; + return s; +} + +static const char* skip_token(const char* s) { + s = skip_spaces(s); + if (!s || !*s) return s; + if (*s == '"') { + s++; + while (*s && *s != '"') s++; + if (*s == '"') s++; + return s; + } + while (*s && *s != ' ' && *s != '\t') s++; + return s; +} + +static bool read_token(const char* s, char* out, size_t cap) { + s = skip_spaces(s); + if (!s || !*s || cap == 0) return false; + size_t n = 0; + if (*s == '"') { + s++; + while (*s && *s != '"') { + if (n + 1 < cap) out[n++] = *s; + s++; + } + } else { + while (*s && *s != ' ' && *s != '\t') { + if (n + 1 < cap) out[n++] = *s; + s++; + } + } + out[n] = 0; + return n != 0; +} + +static void parse_filename_from_cmdline(void) { + SYS_PEB* peb = SysGetPeb(); + const char* cmd = peb ? peb->CommandLine : NULL; + const char* arg = skip_token(cmd); + if (read_token(arg, gFilename, sizeof(gFilename))) return; + strcpy(gFilename, "untitled.txt"); +} + +static void editor_loop(void) { + while (true) { + render(); + + uint8_t sc = SysKbReadScancode(); + uint32_t mods = SysKbGetModifiers(); + bool ctrl = (mods & MOD_CTRL) != 0; + + if (ctrl && sc == SC_Q) { + if (gDirty && !gQuitArmed) { + gQuitArmed = true; + msg_set("unsaved changes: press Ctrl+Q again to quit"); + continue; + } + break; + } + + gQuitArmed = false; + + if (ctrl && sc == SC_S) { save_file(); continue; } + if (sc == SC_ESC) { msg_set("Ctrl+Q quits, Ctrl+S saves"); continue; } + + switch (sc) { + case SC_LEFT: move_left(); break; + case SC_RIGHT: move_right(); break; + case SC_UP: move_up(); break; + case SC_DOWN: move_down(); break; + case SC_HOME: gCx = 0; break; + case SC_END: gCx = gLines[gCy].len; break; + case SC_PGUP: { + int n = gRows - 2; + if (n < 1) n = 1; + gCy -= n; + clamp_cursor(); + break; + } + case SC_PGDN: { + int n = gRows - 2; + if (n < 1) n = 1; + gCy += n; + clamp_cursor(); + break; + } + case SC_BACKSPACE: backspace_at_cursor(); break; + case SC_DEL: delete_at_cursor(); break; + case SC_ENTER: newline_at_cursor(); break; + case SC_TAB: + for (int i = 0; i < 4; i++) insert_char_at_cursor(' '); + break; + default: { + char c = key_to_char(sc, mods); + if (!ctrl && c >= 0x20 && c <= 0x7E) insert_char_at_cursor(c); + break; + } + } + } +} + +static void print(const char* s) { + SysWriteConsole(s, strlen(s)); +} + +int main(void) { + parse_filename_from_cmdline(); + load_file(); + SysScreenClear(COL_BG); + editor_loop(); + SysScreenClear(COL_BG); + print("edit: goodbye\n"); + SysExitProcess(0); + return 0; +} diff --git a/code/kernel/ke_debug.c b/code/kernel/ke_debug.c index 674737b..935f030 100644 --- a/code/kernel/ke_debug.c +++ b/code/kernel/ke_debug.c @@ -3,10 +3,12 @@ #include #include #include +#include #include #include #include #include +#include #include int _fltused; // fuckass @@ -84,40 +86,259 @@ static const char* KiFaultVectorName(int vector) { } } -// Walk the process module list to find which image owns rip. -// Returns the module name string, or "unknown" if rip is outside all regions. -static const char* KiResolveModule(PROCESS* proc, uintptr_t rip) { - // Check the main executable image first. - if (proc->ImageBase && proc->ImageSize && - rip >= proc->ImageBase && rip < proc->ImageBase + proc->ImageSize) - return proc->Name; +// Walk the process module list to find which image owns an address. +// Returns the module name string, or "unknown" if the address is outside all +// known image ranges. Base/size are optional and are useful for offset dumps. +static const char* KiResolveModuleEx(PROCESS* proc, uintptr_t addr, + uintptr_t* baseOut, size_t* sizeOut) { + if (baseOut) *baseOut = 0; + if (sizeOut) *sizeOut = 0; + + if (!proc) return "unknown"; + + if (proc->ImageBase && proc->ImageSize && + addr >= proc->ImageBase && addr < proc->ImageBase + proc->ImageSize) { + if (baseOut) *baseOut = proc->ImageBase; + if (sizeOut) *sizeOut = proc->ImageSize; + return proc->Name; + } - // Walk the per-process module table (DLLs / shared libraries). for (uint32_t i = 0; i < proc->ModuleCount; i++) { uintptr_t base = proc->Modules[i].Base; size_t size = proc->Modules[i].Size; - if (base && size && rip >= base && rip < base + size) + if (base && size && addr >= base && addr < base + size) { + if (baseOut) *baseOut = base; + if (sizeOut) *sizeOut = size; return proc->Modules[i].Name; + } } return "unknown"; } +static const char* KiResolveModule(PROCESS* proc, uintptr_t addr) { + return KiResolveModuleEx(proc, addr, (uintptr_t*)0, (size_t*)0); +} + +static void KiPrintAddress(PROCESS* proc, const char* label, uintptr_t addr) { + uintptr_t base = 0; + size_t size = 0; + const char* mod = KiResolveModuleEx(proc, addr, &base, &size); + + if (base) { + printf("%s%016llx <%s+0x%llx>\n", + label, + (unsigned long long)addr, + mod, + (unsigned long long)(addr - base)); + } else { + printf("%s%016llx \n", + label, + (unsigned long long)addr); + } +} + +static int KiUserReadU64(uintptr_t addr, uint64_t* out) { + if (!out) return 0; + if (!addr) return 0; + if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint64_t)))) + return 0; + *out = *(const uint64_t*)addr; + return 1; +} + +static int KiUserReadU8(uintptr_t addr, uint8_t* out) { + if (!out) return 0; + if (!addr) return 0; + if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint8_t)))) + return 0; + *out = *(const uint8_t*)addr; + return 1; +} + +static void KiDumpBytes(PROCESS* proc, const char* label, uintptr_t center) { + if (!center) { + printf("%s: \n", label); + return; + } + + printf("%s around ", label); + KiPrintAddress(proc, "", center); + + uintptr_t start = center >= 16 ? center - 16 : 0; + for (int row = 0; row < 2; row++) { + uintptr_t addr = start + (uintptr_t)row * 16u; + printf(" %016llx:", (unsigned long long)addr); + for (int i = 0; i < 16; i++) { + uint8_t b = 0; + if (KiUserReadU8(addr + (uintptr_t)i, &b)) + printf(" %02x", b); + else + printf(" ??"); + } + printf("\n"); + } +} + +static int KiUserReadI32(uintptr_t addr, int32_t* out) { + if (!out) return 0; + if (!addr) return 0; + if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(int32_t)))) + return 0; + *out = *(const int32_t*)addr; + return 1; +} + +static void KiDumpIndirectCallSlot(PROCESS* proc, uintptr_t ret) { + if (!ret) return; + + // Most x64 import/function-pointer calls are one of: + // FF 15 disp32 call qword ptr [rip+disp32] + // 48 FF 15 disp32 rex.w call qword ptr [rip+disp32] + // The saved return address points immediately after the instruction. + uintptr_t insn = 0; + int32_t disp = 0; + uint8_t b0 = 0, b1 = 0, b2 = 0; + + if (KiUserReadU8(ret - 6, &b0) && + KiUserReadU8(ret - 5, &b1) && + b0 == 0xff && b1 == 0x15 && + KiUserReadI32(ret - 4, &disp)) { + insn = ret - 6; + } else if (KiUserReadU8(ret - 7, &b0) && + KiUserReadU8(ret - 6, &b1) && + KiUserReadU8(ret - 5, &b2) && + b0 == 0x48 && b1 == 0xff && b2 == 0x15 && + KiUserReadI32(ret - 4, &disp)) { + insn = ret - 7; + } else { + printf("No RIP-relative indirect call pattern found before return address.\n"); + return; + } + + uintptr_t slot = ret + (intptr_t)disp; + uint64_t target = 0; + + printf("Decoded null indirect call:\n"); + KiPrintAddress(proc, " insn=", insn); + KiPrintAddress(proc, " slot=", slot); + + if (KiUserReadU64(slot, &target)) { + KiPrintAddress(proc, " *slot=", (uintptr_t)target); + } else { + printf(" *slot=\n"); + } + + KiDumpBytes(proc, "Bytes near indirect slot", slot); +} + +static void KiDumpModules(PROCESS* proc) { + if (!proc) return; + + printf("Images:\n"); + if (proc->ImageBase && proc->ImageSize) { + printf(" %-31s %016llx-%016llx size=%llx\n", + proc->Name, + (unsigned long long)proc->ImageBase, + (unsigned long long)(proc->ImageBase + proc->ImageSize), + (unsigned long long)proc->ImageSize); + } + + for (uint32_t i = 0; i < proc->ModuleCount; i++) { + uintptr_t base = proc->Modules[i].Base; + size_t size = proc->Modules[i].Size; + printf(" %-31s %016llx-%016llx size=%llx\n", + proc->Modules[i].Name, + (unsigned long long)base, + (unsigned long long)(base + size), + (unsigned long long)size); + } +} + +static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) { + printf("User stack @ %016llx:\n", (unsigned long long)rsp); + if (!rsp) { + printf(" \n"); + return; + } + + for (int i = 0; i < 12; i++) { + uintptr_t slot = rsp + (uintptr_t)i * 8u; + uint64_t value = 0; + if (KiUserReadU64(slot, &value)) { + uintptr_t base = 0; + size_t size = 0; + const char* mod = KiResolveModuleEx(proc, (uintptr_t)value, &base, &size); + if (base) { + printf(" [%02d] %016llx : %016llx <%s+0x%llx>\n", + i, + (unsigned long long)slot, + (unsigned long long)value, + mod, + (unsigned long long)((uintptr_t)value - base)); + } else { + printf(" [%02d] %016llx : %016llx\n", + i, + (unsigned long long)slot, + (unsigned long long)value); + } + } else { + printf(" [%02d] %016llx : \n", + i, + (unsigned long long)slot); + } + } +} + +static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) { + if (!f) return; + + printf("Regs:\n"); + printf(" RAX=%016llx RBX=%016llx RCX=%016llx RDX=%016llx\n", + (unsigned long long)f->Rax, (unsigned long long)f->Rbx, + (unsigned long long)f->Rcx, (unsigned long long)f->Rdx); + printf(" RSI=%016llx RDI=%016llx RBP=%016llx RSP=%016llx\n", + (unsigned long long)f->Rsi, (unsigned long long)f->Rdi, + (unsigned long long)f->Rbp, (unsigned long long)f->Rsp); + printf(" R8 =%016llx R9 =%016llx R10=%016llx R11=%016llx\n", + (unsigned long long)f->R8, (unsigned long long)f->R9, + (unsigned long long)f->R10, (unsigned long long)f->R11); + printf(" R12=%016llx R13=%016llx R14=%016llx R15=%016llx\n", + (unsigned long long)f->R12, (unsigned long long)f->R13, + (unsigned long long)f->R14, (unsigned long long)f->R15); + printf(" CS=%04llx SS=%04llx RFLAGS=%016llx\n", + (unsigned long long)f->Cs, + (unsigned long long)f->Ss, + (unsigned long long)f->Rflags); + + if (f->Rip == 0) { + uint64_t ret = 0; + if (KiUserReadU64((uintptr_t)f->Rsp, &ret)) { + printf("Likely null indirect call. Return address from [RSP]:\n"); + KiPrintAddress(proc, " ret=", (uintptr_t)ret); + KiDumpBytes(proc, "Bytes near return address", (uintptr_t)ret); + KiDumpIndirectCallSlot(proc, (uintptr_t)ret); + } else { + printf("Likely null indirect call, but [RSP] is unreadable.\n"); + } + } else { + KiDumpBytes(proc, "Bytes near RIP", (uintptr_t)f->Rip); + } + + KiDumpUserStack(proc, (uintptr_t)f->Rsp); +} + static void KiPrintCrashChar(char c) { PrintChar(c, 0xFFAA00, 0x000000); // amber - distinct from green debug / red panic } -_Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2) { - THREAD* thread = KeGetCurrentThread(); - PROCESS* proc = thread ? thread->Owner : (PROCESS*)0; - - // Snapshot everything we need before touching process state. +static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip, + uintptr_t errCode, uintptr_t cr2) { const char* faultName = KiFaultVectorName(vector); const char* procName = proc ? proc->Name : "unknown"; uint32_t pid = proc ? proc->ProcessId : 0; const char* modName = proc ? KiResolveModule(proc, rip) : "unknown"; - // Amber crash banner. static const char banner[] = "\n*** APPLICATION CRASH ***\n"; for (const char* p = banner; *p; p++) KiPrintCrashChar(*p); @@ -139,20 +360,67 @@ _Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, u modName, (unsigned long long)rip, (unsigned long long)errCode); +} - // Restore normal (green) output for subsequent kernel messages. +static _Noreturn void KiFinishFaultedProcess(PROCESS* proc, THREAD* thread) { printf_set_putchar(KiKernelPutChar); - // Kill the process and exit this thread. - // KeExitThread sets ThreadDead then calls KeYield; the scheduler will - // never return here, so the process crash cannot affect anything else. if (proc) KeExitProcess(proc, -1); if (thread) KeExitThread(thread, -1); - // Unreachable - KeExitThread never returns for a dead thread. while (1) __halt(); } +_Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2) { + THREAD* thread = KeGetCurrentThread(); + PROCESS* proc = thread ? thread->Owner : (PROCESS*)0; + + KiPrintCrashHeader(proc, vector, rip, errCode, cr2); + KiDumpModules(proc); + + KiFinishFaultedProcess(proc, thread); +} + +__declspec(dllexport) _Noreturn void KeFaultDispatchFrame(const KFAULT_FRAME* frame) { + THREAD* thread = KeGetCurrentThread(); + PROCESS* proc = thread ? thread->Owner : (PROCESS*)0; + + if (!frame || frame->Magic != 0x3146544C5541464BULL) { + KePanic("PANIC: bad KFAULT_FRAME %016llx\n", + (unsigned long long)(uintptr_t)frame); + } + + if ((frame->Cs & 3) == 0) { + switch ((int)frame->Vector) { + case 0: + KePanic("PANIC: #DE Divide Error | RIP: %016llx\n", + (unsigned long long)frame->Rip); + case 13: + KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n", + (unsigned long long)frame->Rip, + (unsigned long long)frame->ErrorCode); + case 14: + KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n", + (unsigned long long)frame->Rip, + (unsigned long long)frame->ErrorCode, + (unsigned long long)frame->Cr2); + default: + KePanic("PANIC: Exception #%llu | RIP: %016llx\n", + (unsigned long long)frame->Vector, + (unsigned long long)frame->Rip); + } + } + + KiPrintCrashHeader(proc, (int)frame->Vector, + (uintptr_t)frame->Rip, + (uintptr_t)frame->ErrorCode, + (uintptr_t)frame->Cr2); + KiDumpFaultFrame(proc, frame); + KiDumpModules(proc); + + KiFinishFaultedProcess(proc, thread); +} + // --------------------------------------------------------------------------- // KeFaultDispatch - single C entry point for all three IDT fault stubs. // @@ -163,25 +431,13 @@ _Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, u __declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2, uintptr_t cs) { - if ((cs & 3) == 0) { - // Kernel-mode fault: unrecoverable. - switch (vector) { - case 0: - KePanic("PANIC: #DE Divide Error | RIP: %016llx\n", - (unsigned long long)rip); - case 13: - KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n", - (unsigned long long)rip, (unsigned long long)errCode); - case 14: - KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n", - (unsigned long long)rip, (unsigned long long)errCode, - (unsigned long long)cr2); - default: - KePanic("PANIC: Exception #%d | RIP: %016llx\n", - vector, (unsigned long long)rip); - } - } - - // User-mode fault: recoverable - kill the process, resume the scheduler. - KeBugCheckProcess(vector, rip, errCode, cr2); + KFAULT_FRAME frame; + memset(&frame, 0, sizeof(frame)); + frame.Magic = 0x3146544C5541464BULL; + frame.Vector = (uint64_t)vector; + frame.ErrorCode = errCode; + frame.Cr2 = cr2; + frame.Rip = rip; + frame.Cs = cs; + KeFaultDispatchFrame(&frame); } diff --git a/code/kernel/ke_debug.h b/code/kernel/ke_debug.h index 9de1e3f..0498aad 100644 --- a/code/kernel/ke_debug.h +++ b/code/kernel/ke_debug.h @@ -1,27 +1,56 @@ -#ifndef KE_DEBUG_H -#define KE_DEBUG_H - -#include - -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, ...); - -// --------------------------------------------------------------------------- -// Fault dispatch - called from the three IDT fault stubs (ke_syscall.asm). -// -// vector : CPU exception number (0=#DE, 13=#GP, 14=#PF) -// rip : faulting instruction pointer (from the CPU exception frame) -// errCode : CPU error code (0 for #DE) -// cr2 : value of CR2 at fault time (meaningful only for #PF) -// cs : CS from the exception frame; RPL bits determine ring -// -// Kernel-mode faults (cs & 3 == 0) call KePanic and halt. -// User-mode faults (cs & 3 == 3) kill the offending process and yield. -// --------------------------------------------------------------------------- -__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, - uintptr_t errCode, uintptr_t cr2, - uintptr_t cs); - -#endif // KE_DEBUG_H +#ifndef KE_DEBUG_H +#define KE_DEBUG_H + +#include +#include + +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, ...); + +// --------------------------------------------------------------------------- +// Fault dispatch - called from the IDT fault stubs (ke_syscall.asm). +// +// The old scalar entry only reported RIP/error/CR2, which is not enough for +// debugging null indirect calls. The frame entry snapshots user GPRs plus the +// CPU exception frame, including user RSP. For a CALL through a null function +// pointer, RIP is 0 and [RSP] is usually the return address after the bad call; +// that is the useful caller site. +// --------------------------------------------------------------------------- +typedef struct KFAULT_FRAME { + uint64_t Magic; + uint64_t Vector; + uint64_t ErrorCode; + uint64_t Cr2; + uint64_t Rip; + uint64_t Cs; + uint64_t Rflags; + uint64_t Rsp; + uint64_t Ss; + + uint64_t Rax; + uint64_t Rcx; + uint64_t Rdx; + uint64_t Rbx; + uint64_t Rbp; + uint64_t Rsi; + uint64_t Rdi; + uint64_t R8; + uint64_t R9; + uint64_t R10; + uint64_t R11; + uint64_t R12; + uint64_t R13; + uint64_t R14; + uint64_t R15; +} KFAULT_FRAME; + +__declspec(dllexport) _Noreturn void KeFaultDispatchFrame(const KFAULT_FRAME* frame); + +// Compatibility wrapper for any older assembly/object still calling this. +__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip, + uintptr_t errCode, uintptr_t cr2, + uintptr_t cs); + +#endif // KE_DEBUG_H diff --git a/code/kernel/ke_syscall.asm b/code/kernel/ke_syscall.asm index 487391c..74f5e54 100644 --- a/code/kernel/ke_syscall.asm +++ b/code/kernel/ke_syscall.asm @@ -7,6 +7,7 @@ EXTERN KeSyscallDispatch:PROC EXTERN KiEnterTrapFrame:PROC EXTERN KiExitTrapFrame:PROC +EXTERN KeFaultDispatchFrame:PROC KTRAP_FRAME_SIZE EQU 240 KTRAP_FRAME_MAGIC EQU 4650545241544B31h @@ -143,76 +144,237 @@ KeSyscallEntry PROC KeSyscallEntry ENDP ; --------------------------------------------------------------------------- -; Fault stubs - thin wrappers that collect the CPU exception frame and -; forward to KeFaultDispatch (C), which handles both kernel panics and -; user-process bugchecks. +; Fault stubs - snapshot a full user fault frame before entering C. ; -; KeFaultDispatch signature (MS x64): -; void KeFaultDispatch(int vector, // rcx -; UINT64 rip, // rdx -; UINT64 errCode, // r8 -; UINT64 cr2, // r9 -; UINT64 cs) // [rsp+20h] (5th arg, on stack) +; For null indirect calls/jumps the CPU reports RIP=0, which is almost useless +; by itself. The captured user RSP lets the C crash dumper read [RSP], which is +; usually the return address immediately after the bad CALL. The GPR snapshot +; also shows which register held the null target. +; +; KeFaultDispatchFrame signature (MS x64): +; void KeFaultDispatchFrame(const KFAULT_FRAME* frame) ; rcx ; --------------------------------------------------------------------------- -EXTERN KeFaultDispatch:PROC -; Allocation for a 5-argument call (MS x64): -; 32 bytes shadow space (0x00..0x1F) -; 8 bytes for 5th arg (0x20..0x27) -; Total = 0x28 bytes. -; After "and rsp,-16" rsp%16==0; sub 0x28 (40) → rsp%16==8, -; so the subsequent "call" push makes rsp 16-byte aligned at callee entry. ✓ -FAULT_FRAME_SIZE EQU 28h +KFAULT_MAGIC EQU 3146544C5541464Bh ; "KFAULTF1" little-ish +KFAULT_SIZE EQU 0C0h + +KF_Magic EQU 000h +KF_Vector EQU 008h +KF_ErrorCode EQU 010h +KF_Cr2 EQU 018h +KF_Rip EQU 020h +KF_Cs EQU 028h +KF_Rflags EQU 030h +KF_Rsp EQU 038h +KF_Ss EQU 040h +KF_Rax EQU 048h +KF_Rcx EQU 050h +KF_Rdx EQU 058h +KF_Rbx EQU 060h +KF_Rbp EQU 068h +KF_Rsi EQU 070h +KF_Rdi EQU 078h +KF_R8 EQU 080h +KF_R9 EQU 088h +KF_R10 EQU 090h +KF_R11 EQU 098h +KF_R12 EQU 0A0h +KF_R13 EQU 0A8h +KF_R14 EQU 0B0h +KF_R15 EQU 0B8h + +FAULT_CALL_SHADOW EQU 28h ; --------------------------------------------------------- ; Divide Error (#0) - NO error code pushed by CPU -; Exception frame at entry: [rsp]=RIP [rsp+8]=CS [rsp+16]=RFLAGS +; User exception frame: [E]=RIP [E+8]=CS [E+16]=RFLAGS [E+24]=RSP [E+32]=SS ; --------------------------------------------------------- KeFaultDE PROC - mov rdx, [rsp] ; Arg2: rip - xor r8, r8 ; Arg3: errCode = 0 (no error code for #DE) - xor r9, r9 ; Arg4: cr2 = 0 (not a page fault) - mov r10, [rsp+8] ; cs (save before we clobber rsp) - mov ecx, 0 ; Arg1: vector = 0 + push rax ; [rsp+8] after next push = original RAX + push r11 ; [rsp] = original R11 + mov r11, rsp ; saved pair; CPU frame starts at [r11+16] + sub rsp, KFAULT_SIZE + mov rax, rsp ; rax = KFAULT_FRAME* + mov [rax + KF_R10], r10 + mov r10, KFAULT_MAGIC + mov [rax + KF_Magic], r10 + mov qword ptr [rax + KF_Vector], 0 + mov qword ptr [rax + KF_ErrorCode], 0 + mov qword ptr [rax + KF_Cr2], 0 + + mov [rax + KF_Rcx], rcx + mov [rax + KF_Rdx], rdx + mov [rax + KF_Rbx], rbx + mov [rax + KF_Rbp], rbp + mov [rax + KF_Rsi], rsi + mov [rax + KF_Rdi], rdi + mov [rax + KF_R8], r8 + mov [rax + KF_R9], r9 + mov r10, [r11] + mov [rax + KF_R11], r10 + mov r10, [r11+8] + mov [rax + KF_Rax], r10 + mov [rax + KF_R12], r12 + mov [rax + KF_R13], r13 + mov [rax + KF_R14], r14 + mov [rax + KF_R15], r15 + + lea rdx, [r11+16] + mov r10, [rdx] + mov [rax + KF_Rip], r10 + mov r10, [rdx+8] + mov [rax + KF_Cs], r10 + mov r10, [rdx+16] + mov [rax + KF_Rflags], r10 + + mov r10, [rax + KF_Cs] + test r10, 3 + jz de_kernel_frame + mov r10, [rdx+24] + mov [rax + KF_Rsp], r10 + mov r10, [rdx+32] + mov [rax + KF_Ss], r10 + jmp de_call + +de_kernel_frame: + mov qword ptr [rax + KF_Rsp], 0 + mov qword ptr [rax + KF_Ss], 0 + +de_call: and rsp, -16 - sub rsp, FAULT_FRAME_SIZE - mov [rsp+20h], r10 ; Arg5: cs - call KeFaultDispatch ; _Noreturn - never returns + sub rsp, FAULT_CALL_SHADOW + mov rcx, rax + call KeFaultDispatchFrame KeFaultDE ENDP ; --------------------------------------------------------- ; General Protection Fault (#13) - error code pushed by CPU -; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS +; User exception frame: [E]=ERR [E+8]=RIP [E+16]=CS [E+24]=RFLAGS [E+32]=RSP [E+40]=SS ; --------------------------------------------------------- KeFaultGP PROC - mov r8, [rsp] ; Arg3: errCode - mov rdx, [rsp+8] ; Arg2: rip - xor r9, r9 ; Arg4: cr2 = 0 (not a page fault) - mov r10, [rsp+16] ; cs - mov ecx, 13 ; Arg1: vector = 13 + push rax + push r11 + mov r11, rsp + sub rsp, KFAULT_SIZE + mov rax, rsp + mov [rax + KF_R10], r10 + mov r10, KFAULT_MAGIC + mov [rax + KF_Magic], r10 + mov qword ptr [rax + KF_Vector], 13 + mov qword ptr [rax + KF_Cr2], 0 + + mov [rax + KF_Rcx], rcx + mov [rax + KF_Rdx], rdx + mov [rax + KF_Rbx], rbx + mov [rax + KF_Rbp], rbp + mov [rax + KF_Rsi], rsi + mov [rax + KF_Rdi], rdi + mov [rax + KF_R8], r8 + mov [rax + KF_R9], r9 + mov r10, [r11] + mov [rax + KF_R11], r10 + mov r10, [r11+8] + mov [rax + KF_Rax], r10 + mov [rax + KF_R12], r12 + mov [rax + KF_R13], r13 + mov [rax + KF_R14], r14 + mov [rax + KF_R15], r15 + + lea rdx, [r11+16] + mov r10, [rdx] + mov [rax + KF_ErrorCode], r10 + mov r10, [rdx+8] + mov [rax + KF_Rip], r10 + mov r10, [rdx+16] + mov [rax + KF_Cs], r10 + mov r10, [rdx+24] + mov [rax + KF_Rflags], r10 + + mov r10, [rax + KF_Cs] + test r10, 3 + jz gp_kernel_frame + mov r10, [rdx+32] + mov [rax + KF_Rsp], r10 + mov r10, [rdx+40] + mov [rax + KF_Ss], r10 + jmp gp_call + +gp_kernel_frame: + mov qword ptr [rax + KF_Rsp], 0 + mov qword ptr [rax + KF_Ss], 0 + +gp_call: and rsp, -16 - sub rsp, FAULT_FRAME_SIZE - mov [rsp+20h], r10 ; Arg5: cs - call KeFaultDispatch + sub rsp, FAULT_CALL_SHADOW + mov rcx, rax + call KeFaultDispatchFrame KeFaultGP ENDP ; --------------------------------------------------------- ; Page Fault (#14) - error code pushed by CPU -; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS +; User exception frame: [E]=ERR [E+8]=RIP [E+16]=CS [E+24]=RFLAGS [E+32]=RSP [E+40]=SS ; --------------------------------------------------------- KeFaultPF PROC - mov r8, [rsp] ; Arg3: errCode - mov rdx, [rsp+8] ; Arg2: rip - mov r9, cr2 ; Arg4: cr2 (faulting virtual address) - mov r10, [rsp+16] ; cs - mov ecx, 14 ; Arg1: vector = 14 + push rax + push r11 + mov r11, rsp + sub rsp, KFAULT_SIZE + mov rax, rsp + mov [rax + KF_R10], r10 + mov r10, KFAULT_MAGIC + mov [rax + KF_Magic], r10 + mov qword ptr [rax + KF_Vector], 14 + + mov [rax + KF_Rcx], rcx + mov [rax + KF_Rdx], rdx + mov [rax + KF_Rbx], rbx + mov [rax + KF_Rbp], rbp + mov [rax + KF_Rsi], rsi + mov [rax + KF_Rdi], rdi + mov [rax + KF_R8], r8 + mov [rax + KF_R9], r9 + mov r10, cr2 + mov [rax + KF_Cr2], r10 + mov r10, [r11] + mov [rax + KF_R11], r10 + mov r10, [r11+8] + mov [rax + KF_Rax], r10 + mov [rax + KF_R12], r12 + mov [rax + KF_R13], r13 + mov [rax + KF_R14], r14 + mov [rax + KF_R15], r15 + + lea rdx, [r11+16] + mov r10, [rdx] + mov [rax + KF_ErrorCode], r10 + mov r10, [rdx+8] + mov [rax + KF_Rip], r10 + mov r10, [rdx+16] + mov [rax + KF_Cs], r10 + mov r10, [rdx+24] + mov [rax + KF_Rflags], r10 + + mov r10, [rax + KF_Cs] + test r10, 3 + jz pf_kernel_frame + mov r10, [rdx+32] + mov [rax + KF_Rsp], r10 + mov r10, [rdx+40] + mov [rax + KF_Ss], r10 + jmp pf_call + +pf_kernel_frame: + mov qword ptr [rax + KF_Rsp], 0 + mov qword ptr [rax + KF_Ss], 0 + +pf_call: and rsp, -16 - sub rsp, FAULT_FRAME_SIZE - mov [rsp+20h], r10 ; Arg5: cs - call KeFaultDispatch + sub rsp, FAULT_CALL_SHADOW + mov rcx, rax + call KeFaultDispatchFrame KeFaultPF ENDP EXTERN KeIrqDispatch:PROC diff --git a/code/kernel/ldr_image.c b/code/kernel/ldr_image.c index 006c265..d824652 100644 --- a/code/kernel/ldr_image.c +++ b/code/kernel/ldr_image.c @@ -8,11 +8,16 @@ #include #include +int _stricmp(const char* a, const char* b); + // --------------------------------------------------------------------------- // PE32+ structures (just what we need) // --------------------------------------------------------------------------- #define PE_SIGNATURE 0x00004550UL // 'PE\0\0' #define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b #pragma pack(push, 1) typedef struct { @@ -117,6 +122,25 @@ typedef struct { static MODULE_ENTRY gModules[MAX_MODULES]; static uint32_t gModuleCount = 0; + +static const char* KiCanonicalModuleName(const char* name) { + if (!name) return name; + + if (!_stricmp(name, "kernel32.dll") || + !_stricmp(name, "user32.dll") || + !_stricmp(name, "gdi32.dll") || + !_stricmp(name, "ole32.dll") || + !_stricmp(name, "winmm.dll")) { + return "win32.dll"; + } + + if (!_stricmp(name, "msvcrt.dll")) { + return "posix.dll"; + } + + return name; +} + static uintptr_t KiFindModuleBaseInTable(const MODULE_ENTRY* modules, uint32_t count, const char* name) { for (uint32_t i = 0; i < count; i++) { if (_stricmp(modules[i].Name, name) == 0) return modules[i].Base; @@ -152,7 +176,7 @@ void LdrRegisterModule(const char* name, uintptr_t base) { } static uintptr_t KiFindGlobalModuleBase(const char* name) { - return KiFindModuleBaseInTable(gModules, gModuleCount, name); + return KiFindModuleBaseInTable(gModules, gModuleCount, KiCanonicalModuleName(name)); } static uintptr_t KiFindProcessModuleBaseInTable( @@ -161,7 +185,7 @@ static uintptr_t KiFindProcessModuleBaseInTable( const char* name ) { for (uint32_t i = 0; i < count; i++) { - if (_stricmp(modules[i].Name, name) == 0) { + if (_stricmp(modules[i].Name, KiCanonicalModuleName(name)) == 0) { return modules[i].Base; } } @@ -208,12 +232,12 @@ static void* KiGetExportAtBase(uintptr_t base, const char* functionName) { // Export lookup // --------------------------------------------------------------------------- void* LdrGetExport(const char* moduleName, const char* functionName) { - uintptr_t base = KiFindGlobalModuleBase(moduleName); + uintptr_t base = KiFindGlobalModuleBase(KiCanonicalModuleName(moduleName)); return KiGetExportAtBase(base, functionName); } void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const char* functionName) { - uintptr_t base = KiFindProcessOrGlobalModuleBase(process, moduleName); + uintptr_t base = KiFindProcessOrGlobalModuleBase(process, KiCanonicalModuleName(moduleName)); return KiGetExportAtBase(base, functionName); } @@ -304,13 +328,8 @@ static KSTATUS LdrResolveImportsCommon( while (imp->Name) { const char* dllName = (const char *)(imageBase + imp->Name); - // Win32 compatibility remap - if (!_stricmp(dllName, "kernel32.dll") || - !_stricmp(dllName, "user32.dll") || - !_stricmp(dllName, "ole32.dll") || - !_stricmp(dllName, "winmm.dll")) { - dllName = "win32.dll"; - } + //Compatibility remap. + dllName = KiCanonicalModuleName(dllName); // // Ensure dependency is loaded into the process @@ -397,6 +416,28 @@ KSTATUS LdrResolveImportsForProcess(PROCESS* process, uintptr_t imageBase) { ); } +static void KiDebugLibtccAllocatorSlot(const char* phase, const char* filename, uintptr_t imageBase) { + if (!filename || !imageBase) return; + if (_stricmp(filename, "libtcc.dll") != 0) return; + + uint64_t slot = *(uint64_t*)(imageBase + 0x4A010); + uint64_t expectedDefault = (uint64_t)(imageBase + 0x25F0); + + DbgPrint("LDR libtcc %s: base=%p slot[+4A010]=%p expected-default=%p\n", + phase, + (void*)imageBase, + (void*)(uintptr_t)slot, + (void*)(uintptr_t)expectedDefault); + + DbgPrint("LDR libtcc data[+4A000..+4A028]: %016llx %016llx %016llx %016llx %016llx %016llx\n", + (unsigned long long)*(uint64_t*)(imageBase + 0x4A000), + (unsigned long long)*(uint64_t*)(imageBase + 0x4A008), + (unsigned long long)*(uint64_t*)(imageBase + 0x4A010), + (unsigned long long)*(uint64_t*)(imageBase + 0x4A018), + (unsigned long long)*(uint64_t*)(imageBase + 0x4A020), + (unsigned long long)*(uint64_t*)(imageBase + 0x4A028)); +} + static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) { if (!process || !name || !base) return STATUS_INVALID_PARAMETER; @@ -419,6 +460,8 @@ static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uint KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) { if (!process || !filename) return STATUS_INVALID_PARAMETER; + filename = KiCanonicalModuleName(filename); + for (uint32_t i = 0; i < process->ModuleCount; i++) { if (_stricmp(process->Modules[i].Name, filename) == 0) { if (baseOut) *baseOut = process->Modules[i].Base; @@ -462,12 +505,16 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_ return s; } + KiDebugLibtccAllocatorSlot("after load/reloc before imports", filename, imageBase); + s = LdrResolveImportsForProcess(process, imageBase); if (!KSUCCESS(s)) { MmFreeUserMemory((void*)imageBase); return s; } + KiDebugLibtccAllocatorSlot("after imports", filename, imageBase); + s = LdrRegisterProcessModule(process, filename, imageBase); if (!KSUCCESS(s)) { MmFreeUserMemory((void*)imageBase); @@ -481,11 +528,36 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_ // LdrLoadImage - load a PE from a raw buffer into virtual space. KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user) { if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER; + if (size < 0x40) return STATUS_FAILURE; - uint32_t peOffset = *(uint32_t *)((uint8_t *)buffer + 0x3C); - PE_HEADER* pe = (PE_HEADER *)((uint8_t *)buffer + peOffset); - if (pe->Signature != PE_SIGNATURE) return STATUS_FAILURE; + const uint8_t* bytes = (const uint8_t*)buffer; + uint32_t peOffset = *(const uint32_t *)(bytes + 0x3C); + if ((size_t)peOffset > size || size - (size_t)peOffset < 24) return STATUS_FAILURE; + uint32_t signature = *(const uint32_t *)(bytes + peOffset); + if (signature != PE_SIGNATURE) return STATUS_FAILURE; + + uint16_t machine = *(const uint16_t *)(bytes + peOffset + 4); + uint16_t optionalSize = *(const uint16_t *)(bytes + peOffset + 20); + if (optionalSize < sizeof(OPT_HEADER64) || + (size_t)peOffset + 24 + optionalSize > size) { + return STATUS_FAILURE; + } + + uint16_t optionalMagic = *(const uint16_t *)(bytes + peOffset + 24); + if (machine == IMAGE_FILE_MACHINE_I386 || optionalMagic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + DbgPrint("LdrLoadImage: hard error: 32-bit PE images are not supported (machine=0x%X magic=0x%X)\n", + machine, optionalMagic); + return STATUS_NOT_SUPPORTED; + } + + if (machine != IMAGE_FILE_MACHINE_AMD64 || optionalMagic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + DbgPrint("LdrLoadImage: unsupported PE image (machine=0x%X magic=0x%X)\n", + machine, optionalMagic); + return STATUS_NOT_SUPPORTED; + } + + PE_HEADER* pe = (PE_HEADER *)(bytes + peOffset); OPT_HEADER64* opt = &pe->OptionalHeader; size_t imageSize = opt->SizeOfImage; diff --git a/code/libc.c b/code/libc.c index de38fcd..3a39c2e 100644 --- a/code/libc.c +++ b/code/libc.c @@ -91,6 +91,15 @@ char* strchr(const char* s, int c) { return NULL; } +char* strrchr(const char* s, int c) { + char ch = (char)c; + const char* last = NULL; + do { + if (*s == ch) last = s; + } while (*s++); + return (char*)last; +} + void* memmove(void* dest, const void* src, size_t n) { uint8_t* d = (uint8_t *)dest; const uint8_t* s = (const uint8_t *)src; diff --git a/code/win32/kernel32.c b/code/win32/kernel32.c index 09ea325..d30ac9e 100644 --- a/code/win32/kernel32.c +++ b/code/win32/kernel32.c @@ -43,6 +43,29 @@ typedef struct { LONG x; LONG y; } POINT; #define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1) +#define MEM_COMMIT 0x00001000UL +#define MEM_RESERVE 0x00002000UL +#define MEM_RELEASE 0x00008000UL +#define MEM_FREE 0x00010000UL +#define MEM_PRIVATE 0x00020000UL +#define PAGE_NOACCESS 0x01UL +#define PAGE_READONLY 0x02UL +#define PAGE_READWRITE 0x04UL +#define PAGE_WRITECOPY 0x08UL +#define PAGE_EXECUTE 0x10UL +#define PAGE_EXECUTE_READ 0x20UL +#define PAGE_EXECUTE_READWRITE 0x40UL + +typedef struct _MEMORY_BASIC_INFORMATION { + void* BaseAddress; + void* AllocationBase; + DWORD AllocationProtect; + size_t RegionSize; + DWORD State; + DWORD Protect; + DWORD Type; +} MEMORY_BASIC_INFORMATION; + // File-find structures typedef struct _FILETIME { DWORD dwLowDateTime; @@ -110,12 +133,12 @@ static void TranslatePath(const char* input, char* output) { 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' + // Translate "C:\" -> "0:/". Native nt-lite paths such as + // "0:/system/foo.dll" are already drive-indexed and pass through. char drive = input[0]; - if (drive >= 'a') drive -= ('a' - 'A'); // To uppercase - - output[0] = (char)('0' + (drive - 'C')); + if (drive >= 'a' && drive <= 'z') drive -= ('a' - 'A'); // To uppercase + + output[0] = (drive >= '0' && drive <= '7') ? drive : (char)('0' + (drive - 'C')); output[1] = ':'; output[2] = '/'; @@ -137,11 +160,88 @@ static void TranslatePath(const char* input, char* output) { } } +static DWORD gLastError = 0; + +static BOOL KiFileExistsA(const char* path) { + if (!path || !*path) return FALSE; + char native[MAX_PATH]; + TranslatePath(path, native); + uint32_t h = 0; + if (SysOpenFile(native, &h) != 0) return FALSE; + SysCloseHandle(h); + return TRUE; +} + +static DWORD KiCopySearchPathResult(char* out, DWORD outChars, const char* text, char** filePart) { + DWORD len = (DWORD)strlen(text); + if (out && outChars) { + DWORD copy = (len < outChars - 1) ? len : outChars - 1; + memcpy(out, text, copy); + out[copy] = '\0'; + if (filePart) { + char* slash1 = strrchr(out, '/'); + char* slash2 = strrchr(out, '\\'); + char* slash = slash1 > slash2 ? slash1 : slash2; + *filePart = slash ? slash + 1 : out; + } + } + return len; +} + +__declspec(dllexport) DWORD SearchPathA(const char* lpPath, const char* lpFileName, + const char* lpExtension, DWORD nBufferLength, + char* lpBuffer, char** lpFilePart) { + if (!lpFileName || !*lpFileName) { gLastError = 87; return 0; } + + char candidate[MAX_PATH]; + size_t nameLen = strlen(lpFileName); + bool hasExt = (strrchr(lpFileName, '.') != NULL); + bool rooted = (lpFileName[0] == '/' || lpFileName[0] == '\\' || + (lpFileName[0] && lpFileName[1] == ':')); + + if (rooted || strchr(lpFileName, '/') || strchr(lpFileName, '\\')) { + strncpy(candidate, lpFileName, sizeof(candidate) - 1); + candidate[sizeof(candidate) - 1] = '\0'; + if (!hasExt && lpExtension && nameLen + strlen(lpExtension) + 1 < sizeof(candidate)) + strcat(candidate, lpExtension); + if (KiFileExistsA(candidate)) + return KiCopySearchPathResult(lpBuffer, nBufferLength, candidate, lpFilePart); + } + + const char* paths = lpPath && *lpPath ? lpPath : "."; + const char* p = paths; + while (*p) { + char dir[MAX_PATH]; + size_t di = 0; + while (*p && *p != ';' && di + 1 < sizeof(dir)) dir[di++] = *p++; + while (*p == ';') p++; + dir[di] = '\0'; + + size_t pos = 0; + if (strcmp(dir, ".") != 0) { + while (dir[pos] && pos + 1 < sizeof(candidate)) { candidate[pos] = dir[pos]; pos++; } + if (pos && candidate[pos - 1] != '/' && candidate[pos - 1] != '\\' && pos + 1 < sizeof(candidate)) + candidate[pos++] = '/'; + } + for (size_t i = 0; lpFileName[i] && pos + 1 < sizeof(candidate); i++) candidate[pos++] = lpFileName[i]; + if (!hasExt && lpExtension) { + for (size_t i = 0; lpExtension[i] && pos + 1 < sizeof(candidate); i++) candidate[pos++] = lpExtension[i]; + } + candidate[pos] = '\0'; + + if (KiFileExistsA(candidate)) + return KiCopySearchPathResult(lpBuffer, nBufferLength, candidate, lpFilePart); + } + + gLastError = 2; // ERROR_FILE_NOT_FOUND + if (lpBuffer && nBufferLength) lpBuffer[0] = '\0'; + if (lpFilePart) *lpFilePart = NULL; + return 0; +} + // --------------------------------------------------------------------------- // GetLastError / SetLastError (single-threaded stub) // --------------------------------------------------------------------------- -static DWORD gLastError = 0; - static SYS_PEB* gProcessPeb = NULL; static SYS_PEB* Win32Peb(void) { @@ -215,6 +315,49 @@ __declspec(dllexport) void* GetProcessHeap(void) { return (void*)1; } +// --------------------------------------------------------------------------- +// Virtual memory compatibility +// --------------------------------------------------------------------------- +__declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize, + DWORD flAllocationType, DWORD flProtect) { + (void)lpAddress; (void)flAllocationType; (void)flProtect; + if (!dwSize) return NULL; + void* ptr = SysAlloc(dwSize); + if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY + return ptr; +} + +__declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwFreeType) { + (void)dwSize; (void)dwFreeType; + if (lpAddress) SysFree(lpAddress); + return TRUE; +} + +__declspec(dllexport) BOOL VirtualProtect(void* lpAddress, size_t dwSize, + DWORD flNewProtect, DWORD* lpflOldProtect) { + (void)lpAddress; (void)dwSize; (void)flNewProtect; + if (lpflOldProtect) *lpflOldProtect = PAGE_EXECUTE_READWRITE; + return TRUE; +} + +__declspec(dllexport) size_t VirtualQuery(const void* lpAddress, + MEMORY_BASIC_INFORMATION* lpBuffer, + size_t dwLength) { + if (!lpAddress || !lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION)) { + gLastError = 87; // ERROR_INVALID_PARAMETER + return 0; + } + uintptr_t base = (uintptr_t)lpAddress & ~(uintptr_t)0xFFF; + lpBuffer->BaseAddress = (void*)base; + lpBuffer->AllocationBase = (void*)base; + lpBuffer->AllocationProtect = PAGE_EXECUTE_READWRITE; + lpBuffer->RegionSize = 0x1000; + lpBuffer->State = MEM_COMMIT; + lpBuffer->Protect = PAGE_EXECUTE_READWRITE; + lpBuffer->Type = MEM_PRIVATE; + return sizeof(MEMORY_BASIC_INFORMATION); +} + // --- Win32 API --- // Updated GetAsyncKeyState implementation @@ -335,6 +478,11 @@ __declspec(dllexport) int WideCharToMultiByte(unsigned int cp, DWORD flags, __declspec(dllexport) DWORD GetACP(void) { return 1252; } // Windows-1252 __declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US +__declspec(dllexport) BOOL IsDBCSLeadByteEx(unsigned int CodePage, BYTE TestChar) { + (void)CodePage; (void)TestChar; + return FALSE; +} + // --------------------------------------------------------------------------- // File I/O // --------------------------------------------------------------------------- @@ -599,32 +747,104 @@ __declspec(dllexport) HANDLE GetCurrentThread(void) { __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. +// Tiny TLS table; enough for MSVCRT/TCC compatibility paths that probe TLS. +#define WIN32_TLS_SLOTS 64 +static void* gTlsSlots[WIN32_TLS_SLOTS]; +static uint64_t gTlsBitmap = 1; // reserve slot 0 + +__declspec(dllexport) DWORD TlsAlloc(void) { + for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) { + uint64_t bit = 1ULL << i; + if (!(gTlsBitmap & bit)) { gTlsBitmap |= bit; gTlsSlots[i] = NULL; return i; } + } + gLastError = 8; + return 0xFFFFFFFFUL; +} + +__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) { + if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; } + gTlsSlots[dwTlsIndex] = NULL; + gTlsBitmap &= ~(1ULL << dwTlsIndex); + return TRUE; +} + +__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) { + if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; } + gLastError = 0; + return gTlsSlots[dwTlsIndex]; +} + +__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) { + if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; } + gTlsSlots[dwTlsIndex] = lpTlsValue; + return TRUE; +} + +// Pseudo HMODULEs for callers that probe already-linked personality DLLs. +#define WIN32_HMODULE_EXE ((HANDLE)(uintptr_t)0x00400000UL) +#define WIN32_HMODULE_WIN32 ((HANDLE)(uintptr_t)0x57333232UL) // W322 + +static int KiStricmpA(const char* a, const char* b) { + while (*a && (((unsigned char)*a | 0x20) == ((unsigned char)*b | 0x20))) { a++; b++; } + return ((unsigned char)*a | 0x20) - ((unsigned char)*b | 0x20); +} + +static BOOL KiIsWin32DllNameA(const char* name) { + return name && ( + !KiStricmpA(name, "kernel32.dll") || !KiStricmpA(name, "user32.dll") || + !KiStricmpA(name, "gdi32.dll") || !KiStricmpA(name, "ole32.dll") || + !KiStricmpA(name, "winmm.dll") || !KiStricmpA(name, "win32.dll")); +} + +static void KiDbgWriteA(const char* s) { + if (s) SysWriteConsole(s, strlen(s)); +} + __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 + if (!lpModuleName) return WIN32_HMODULE_EXE; + if (KiIsWin32DllNameA(lpModuleName)) return WIN32_HMODULE_WIN32; + gLastError = 126; // ERROR_MOD_NOT_FOUND return NULL; } -// LoadLibraryA - no dynamic loader; always fail. +__declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName) { + return lpModuleName ? NULL : WIN32_HMODULE_EXE; +} + __declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) { - (void)lpLibFileName; + if (KiIsWin32DllNameA(lpLibFileName)) return WIN32_HMODULE_WIN32; gLastError = 2; // ERROR_FILE_NOT_FOUND return NULL; } +// Minimal GetProcAddress for code that probes kernel32 functions already present +// in this win32.dll image. Full dynamic DLL loading remains a kernel-loader job. +__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule); +__declspec(dllexport) void Sleep(DWORD dwMilliseconds); +__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) { + if (hModule != WIN32_HMODULE_WIN32 || !lpProcName) { gLastError = 127; return NULL; } + + // Ordinal probes are represented as small integer pointers. Do not treat + // them as strings while debugging misses. + if ((uintptr_t)lpProcName < 0x10000) { + KiDbgWriteA("GetProcAddress ordinal miss\n"); + gLastError = 127; + return NULL; + } + +#define GP(name) if (strcmp(lpProcName, #name) == 0) return (void*)&name + GP(GetLastError); GP(SetLastError); GP(LoadLibraryA); GP(GetProcAddress); + GP(FreeLibrary); GP(VirtualAlloc); GP(VirtualFree); GP(VirtualProtect); + GP(VirtualQuery); GP(GetTickCount); GP(GetTickCount64); GP(Sleep); + GP(TlsAlloc); GP(TlsFree); GP(TlsGetValue); GP(TlsSetValue); +#undef GP + KiDbgWriteA("GetProcAddress miss: "); + KiDbgWriteA(lpProcName); + KiDbgWriteA("\n"); + gLastError = 127; // ERROR_PROC_NOT_FOUND + return NULL; +} + __declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) { (void)hLibModule; return TRUE; @@ -633,8 +853,19 @@ __declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) { __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule, char* lpFilename, DWORD nSize) { (void)hModule; - if (lpFilename && nSize) lpFilename[0] = '\0'; - return 0; + if (!lpFilename || !nSize) return 0; + + SYS_PEB* peb = Win32Peb(); + const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath) + ? peb->ImagePath + : ""; + + DWORD len = (DWORD)strlen(src); + DWORD copy = (len < nSize - 1) ? len : nSize - 1; + memcpy(lpFilename, src, copy); + lpFilename[copy] = '\0'; + if (copy < len) gLastError = 122; // ERROR_INSUFFICIENT_BUFFER + return copy; } __declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) { @@ -1565,6 +1796,18 @@ typedef struct _RUNTIME_FUNCTION { unsigned int UnwindInfoAddress; } RUNTIME_FUNCTION; +__declspec(dllexport) BOOL RtlAddFunctionTable(RUNTIME_FUNCTION* FunctionTable, + DWORD EntryCount, + unsigned long long BaseAddress) { + (void)FunctionTable; (void)EntryCount; (void)BaseAddress; + return TRUE; +} + +__declspec(dllexport) BOOL RtlDeleteFunctionTable(RUNTIME_FUNCTION* FunctionTable) { + (void)FunctionTable; + return TRUE; +} + // We have no runtime function table, so RtlLookupFunctionEntry always fails. __declspec(dllexport) RUNTIME_FUNCTION* RtlLookupFunctionEntry(unsigned long long ControlPc,