revolutionary crash dump, msvcrt.dll redirect, text editor, some win32 functions
This commit is contained in:
1 parent
923e8792b4
commit
603fbdfab1
9 files changed
+1432
-160
No files matched your search
@@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
+297
-41
@@ -3,10 +3,12 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <mm_virt.h>
|
||||
#include <printf.h>
|
||||
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 <unknown>\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: <null>\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=<unreadable>\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(" <no user RSP captured>\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 : <unreadable>\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);
|
||||
}
|
||||
+56
-27
@@ -1,27 +1,56 @@
|
||||
#ifndef KE_DEBUG_H
|
||||
#define KE_DEBUG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
+206
-44
@@ -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
|
||||
|
||||
+86
-14
@@ -8,11 +8,16 @@
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+269
-26
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user