revolutionary crash dump, msvcrt.dll redirect, text editor, some win32 functions
This commit is contained in:
1 parent
923e8792b4
commit
603fbdfab1
9 files changed
+1405
-133
No files matched your search
Binary file not shown.
@@ -26,6 +26,7 @@ mkdir "obj\shell_posix" 2>nul
|
|||||||
mkdir "obj\shell_native" 2>nul
|
mkdir "obj\shell_native" 2>nul
|
||||||
mkdir "obj\async_apc" 2>nul
|
mkdir "obj\async_apc" 2>nul
|
||||||
mkdir "obj\async_cancel" 2>nul
|
mkdir "obj\async_cancel" 2>nul
|
||||||
|
mkdir "obj\edit" 2>nul
|
||||||
mkdir "obj\httpget" 2>nul
|
mkdir "obj\httpget" 2>nul
|
||||||
mkdir "obj\tcpserve" 2>nul
|
mkdir "obj\tcpserve" 2>nul
|
||||||
mkdir "obj\udp" 2>nul
|
mkdir "obj\udp" 2>nul
|
||||||
@@ -204,6 +205,14 @@ for %%f in (%SRC%\async_cancel\*.c) do (
|
|||||||
link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
|
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
|
/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 ----
|
:: ---- Compile httpget ----
|
||||||
echo Compiling httpget...
|
echo Compiling httpget...
|
||||||
for %%f in (%SRC%\httpget\*.c) do (
|
for %%f in (%SRC%\httpget\*.c) do (
|
||||||
@@ -312,6 +321,7 @@ copy /y "bin\shell_posix.exe" "%DEST%\apps\"
|
|||||||
copy /y "bin\shell_native.exe" "%DEST%\apps\"
|
copy /y "bin\shell_native.exe" "%DEST%\apps\"
|
||||||
copy /y "bin\async_apc.exe" "%DEST2%\apps\"
|
copy /y "bin\async_apc.exe" "%DEST2%\apps\"
|
||||||
copy /y "bin\async_cancel.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\httpget.exe" "%DEST%\apps\"
|
||||||
copy /y "bin\tcpserve.exe" "%DEST%\apps\"
|
copy /y "bin\tcpserve.exe" "%DEST%\apps\"
|
||||||
copy /y "bin\udp.exe" "%DEST%\apps\"
|
copy /y "bin\udp.exe" "%DEST%\apps\"
|
||||||
|
|||||||
@@ -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 <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <hal_api.h>
|
#include <hal_api.h>
|
||||||
|
#include <ke_debug.h>
|
||||||
#include <ke_spinlock.h>
|
#include <ke_spinlock.h>
|
||||||
#include <ke_process.h>
|
#include <ke_process.h>
|
||||||
#include <ke_thread.h>
|
#include <ke_thread.h>
|
||||||
#include <ke_sched.h>
|
#include <ke_sched.h>
|
||||||
|
#include <mm_virt.h>
|
||||||
#include <printf.h>
|
#include <printf.h>
|
||||||
int _fltused; // fuckass
|
int _fltused; // fuckass
|
||||||
|
|
||||||
@@ -84,40 +86,259 @@ static const char* KiFaultVectorName(int vector) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk the process module list to find which image owns rip.
|
// Walk the process module list to find which image owns an address.
|
||||||
// Returns the module name string, or "unknown" if rip is outside all regions.
|
// Returns the module name string, or "unknown" if the address is outside all
|
||||||
static const char* KiResolveModule(PROCESS* proc, uintptr_t rip) {
|
// known image ranges. Base/size are optional and are useful for offset dumps.
|
||||||
// Check the main executable image first.
|
static const char* KiResolveModuleEx(PROCESS* proc, uintptr_t addr,
|
||||||
if (proc->ImageBase && proc->ImageSize &&
|
uintptr_t* baseOut, size_t* sizeOut) {
|
||||||
rip >= proc->ImageBase && rip < proc->ImageBase + proc->ImageSize)
|
if (baseOut) *baseOut = 0;
|
||||||
return proc->Name;
|
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++) {
|
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
|
||||||
uintptr_t base = proc->Modules[i].Base;
|
uintptr_t base = proc->Modules[i].Base;
|
||||||
size_t size = proc->Modules[i].Size;
|
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 proc->Modules[i].Name;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return "unknown";
|
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) {
|
static void KiPrintCrashChar(char c) {
|
||||||
PrintChar(c, 0xFFAA00, 0x000000); // amber - distinct from green debug / red panic
|
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) {
|
static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
|
||||||
THREAD* thread = KeGetCurrentThread();
|
uintptr_t errCode, uintptr_t cr2) {
|
||||||
PROCESS* proc = thread ? thread->Owner : (PROCESS*)0;
|
|
||||||
|
|
||||||
// Snapshot everything we need before touching process state.
|
|
||||||
const char* faultName = KiFaultVectorName(vector);
|
const char* faultName = KiFaultVectorName(vector);
|
||||||
const char* procName = proc ? proc->Name : "unknown";
|
const char* procName = proc ? proc->Name : "unknown";
|
||||||
uint32_t pid = proc ? proc->ProcessId : 0;
|
uint32_t pid = proc ? proc->ProcessId : 0;
|
||||||
const char* modName = proc ? KiResolveModule(proc, rip) : "unknown";
|
const char* modName = proc ? KiResolveModule(proc, rip) : "unknown";
|
||||||
|
|
||||||
// Amber crash banner.
|
|
||||||
static const char banner[] = "\n*** APPLICATION CRASH ***\n";
|
static const char banner[] = "\n*** APPLICATION CRASH ***\n";
|
||||||
for (const char* p = banner; *p; p++)
|
for (const char* p = banner; *p; p++)
|
||||||
KiPrintCrashChar(*p);
|
KiPrintCrashChar(*p);
|
||||||
@@ -139,20 +360,67 @@ _Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, u
|
|||||||
modName,
|
modName,
|
||||||
(unsigned long long)rip,
|
(unsigned long long)rip,
|
||||||
(unsigned long long)errCode);
|
(unsigned long long)errCode);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore normal (green) output for subsequent kernel messages.
|
static _Noreturn void KiFinishFaultedProcess(PROCESS* proc, THREAD* thread) {
|
||||||
printf_set_putchar(KiKernelPutChar);
|
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 (proc) KeExitProcess(proc, -1);
|
||||||
if (thread) KeExitThread(thread, -1);
|
if (thread) KeExitThread(thread, -1);
|
||||||
|
|
||||||
// Unreachable - KeExitThread never returns for a dead thread.
|
|
||||||
while (1) __halt();
|
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.
|
// 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,
|
__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
|
||||||
uintptr_t errCode, uintptr_t cr2,
|
uintptr_t errCode, uintptr_t cr2,
|
||||||
uintptr_t cs) {
|
uintptr_t cs) {
|
||||||
if ((cs & 3) == 0) {
|
KFAULT_FRAME frame;
|
||||||
// Kernel-mode fault: unrecoverable.
|
memset(&frame, 0, sizeof(frame));
|
||||||
switch (vector) {
|
frame.Magic = 0x3146544C5541464BULL;
|
||||||
case 0:
|
frame.Vector = (uint64_t)vector;
|
||||||
KePanic("PANIC: #DE Divide Error | RIP: %016llx\n",
|
frame.ErrorCode = errCode;
|
||||||
(unsigned long long)rip);
|
frame.Cr2 = cr2;
|
||||||
case 13:
|
frame.Rip = rip;
|
||||||
KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n",
|
frame.Cs = cs;
|
||||||
(unsigned long long)rip, (unsigned long long)errCode);
|
KeFaultDispatchFrame(&frame);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
+38
-9
@@ -2,6 +2,7 @@
|
|||||||
#define KE_DEBUG_H
|
#define KE_DEBUG_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
|
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
|
||||||
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
|
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
|
||||||
@@ -9,17 +10,45 @@ __declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
|
|||||||
__declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...);
|
__declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Fault dispatch - called from the three IDT fault stubs (ke_syscall.asm).
|
// Fault dispatch - called from the IDT fault stubs (ke_syscall.asm).
|
||||||
//
|
//
|
||||||
// vector : CPU exception number (0=#DE, 13=#GP, 14=#PF)
|
// The old scalar entry only reported RIP/error/CR2, which is not enough for
|
||||||
// rip : faulting instruction pointer (from the CPU exception frame)
|
// debugging null indirect calls. The frame entry snapshots user GPRs plus the
|
||||||
// errCode : CPU error code (0 for #DE)
|
// CPU exception frame, including user RSP. For a CALL through a null function
|
||||||
// cr2 : value of CR2 at fault time (meaningful only for #PF)
|
// pointer, RIP is 0 and [RSP] is usually the return address after the bad call;
|
||||||
// cs : CS from the exception frame; RPL bits determine ring
|
// that is the useful caller site.
|
||||||
//
|
|
||||||
// Kernel-mode faults (cs & 3 == 0) call KePanic and halt.
|
|
||||||
// User-mode faults (cs & 3 == 3) kill the offending process and yield.
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
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,
|
__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
|
||||||
uintptr_t errCode, uintptr_t cr2,
|
uintptr_t errCode, uintptr_t cr2,
|
||||||
uintptr_t cs);
|
uintptr_t cs);
|
||||||
|
|||||||
+206
-44
@@ -7,6 +7,7 @@
|
|||||||
EXTERN KeSyscallDispatch:PROC
|
EXTERN KeSyscallDispatch:PROC
|
||||||
EXTERN KiEnterTrapFrame:PROC
|
EXTERN KiEnterTrapFrame:PROC
|
||||||
EXTERN KiExitTrapFrame:PROC
|
EXTERN KiExitTrapFrame:PROC
|
||||||
|
EXTERN KeFaultDispatchFrame:PROC
|
||||||
|
|
||||||
KTRAP_FRAME_SIZE EQU 240
|
KTRAP_FRAME_SIZE EQU 240
|
||||||
KTRAP_FRAME_MAGIC EQU 4650545241544B31h
|
KTRAP_FRAME_MAGIC EQU 4650545241544B31h
|
||||||
@@ -143,76 +144,237 @@ KeSyscallEntry PROC
|
|||||||
KeSyscallEntry ENDP
|
KeSyscallEntry ENDP
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
; ---------------------------------------------------------------------------
|
||||||
; Fault stubs - thin wrappers that collect the CPU exception frame and
|
; Fault stubs - snapshot a full user fault frame before entering C.
|
||||||
; forward to KeFaultDispatch (C), which handles both kernel panics and
|
|
||||||
; user-process bugchecks.
|
|
||||||
;
|
;
|
||||||
; KeFaultDispatch signature (MS x64):
|
; For null indirect calls/jumps the CPU reports RIP=0, which is almost useless
|
||||||
; void KeFaultDispatch(int vector, // rcx
|
; by itself. The captured user RSP lets the C crash dumper read [RSP], which is
|
||||||
; UINT64 rip, // rdx
|
; usually the return address immediately after the bad CALL. The GPR snapshot
|
||||||
; UINT64 errCode, // r8
|
; also shows which register held the null target.
|
||||||
; UINT64 cr2, // r9
|
;
|
||||||
; UINT64 cs) // [rsp+20h] (5th arg, on stack)
|
; KeFaultDispatchFrame signature (MS x64):
|
||||||
|
; void KeFaultDispatchFrame(const KFAULT_FRAME* frame) ; rcx
|
||||||
; ---------------------------------------------------------------------------
|
; ---------------------------------------------------------------------------
|
||||||
EXTERN KeFaultDispatch:PROC
|
|
||||||
|
|
||||||
; Allocation for a 5-argument call (MS x64):
|
KFAULT_MAGIC EQU 3146544C5541464Bh ; "KFAULTF1" little-ish
|
||||||
; 32 bytes shadow space (0x00..0x1F)
|
KFAULT_SIZE EQU 0C0h
|
||||||
; 8 bytes for 5th arg (0x20..0x27)
|
|
||||||
; Total = 0x28 bytes.
|
KF_Magic EQU 000h
|
||||||
; After "and rsp,-16" rsp%16==0; sub 0x28 (40) → rsp%16==8,
|
KF_Vector EQU 008h
|
||||||
; so the subsequent "call" push makes rsp 16-byte aligned at callee entry. ✓
|
KF_ErrorCode EQU 010h
|
||||||
FAULT_FRAME_SIZE EQU 28h
|
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
|
; 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
|
KeFaultDE PROC
|
||||||
mov rdx, [rsp] ; Arg2: rip
|
push rax ; [rsp+8] after next push = original RAX
|
||||||
xor r8, r8 ; Arg3: errCode = 0 (no error code for #DE)
|
push r11 ; [rsp] = original R11
|
||||||
xor r9, r9 ; Arg4: cr2 = 0 (not a page fault)
|
mov r11, rsp ; saved pair; CPU frame starts at [r11+16]
|
||||||
mov r10, [rsp+8] ; cs (save before we clobber rsp)
|
sub rsp, KFAULT_SIZE
|
||||||
mov ecx, 0 ; Arg1: vector = 0
|
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
|
and rsp, -16
|
||||||
sub rsp, FAULT_FRAME_SIZE
|
sub rsp, FAULT_CALL_SHADOW
|
||||||
mov [rsp+20h], r10 ; Arg5: cs
|
mov rcx, rax
|
||||||
call KeFaultDispatch ; _Noreturn - never returns
|
call KeFaultDispatchFrame
|
||||||
KeFaultDE ENDP
|
KeFaultDE ENDP
|
||||||
|
|
||||||
; ---------------------------------------------------------
|
; ---------------------------------------------------------
|
||||||
; General Protection Fault (#13) - error code pushed by CPU
|
; 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
|
KeFaultGP PROC
|
||||||
mov r8, [rsp] ; Arg3: errCode
|
push rax
|
||||||
mov rdx, [rsp+8] ; Arg2: rip
|
push r11
|
||||||
xor r9, r9 ; Arg4: cr2 = 0 (not a page fault)
|
mov r11, rsp
|
||||||
mov r10, [rsp+16] ; cs
|
sub rsp, KFAULT_SIZE
|
||||||
mov ecx, 13 ; Arg1: vector = 13
|
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
|
and rsp, -16
|
||||||
sub rsp, FAULT_FRAME_SIZE
|
sub rsp, FAULT_CALL_SHADOW
|
||||||
mov [rsp+20h], r10 ; Arg5: cs
|
mov rcx, rax
|
||||||
call KeFaultDispatch
|
call KeFaultDispatchFrame
|
||||||
KeFaultGP ENDP
|
KeFaultGP ENDP
|
||||||
|
|
||||||
; ---------------------------------------------------------
|
; ---------------------------------------------------------
|
||||||
; Page Fault (#14) - error code pushed by CPU
|
; 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
|
KeFaultPF PROC
|
||||||
mov r8, [rsp] ; Arg3: errCode
|
push rax
|
||||||
mov rdx, [rsp+8] ; Arg2: rip
|
push r11
|
||||||
mov r9, cr2 ; Arg4: cr2 (faulting virtual address)
|
mov r11, rsp
|
||||||
mov r10, [rsp+16] ; cs
|
sub rsp, KFAULT_SIZE
|
||||||
mov ecx, 14 ; Arg1: vector = 14
|
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
|
and rsp, -16
|
||||||
sub rsp, FAULT_FRAME_SIZE
|
sub rsp, FAULT_CALL_SHADOW
|
||||||
mov [rsp+20h], r10 ; Arg5: cs
|
mov rcx, rax
|
||||||
call KeFaultDispatch
|
call KeFaultDispatchFrame
|
||||||
KeFaultPF ENDP
|
KeFaultPF ENDP
|
||||||
|
|
||||||
EXTERN KeIrqDispatch:PROC
|
EXTERN KeIrqDispatch:PROC
|
||||||
|
|||||||
+86
-14
@@ -8,11 +8,16 @@
|
|||||||
#include <mm_virt.h>
|
#include <mm_virt.h>
|
||||||
#include <ke_debug.h>
|
#include <ke_debug.h>
|
||||||
|
|
||||||
|
int _stricmp(const char* a, const char* b);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// PE32+ structures (just what we need)
|
// PE32+ structures (just what we need)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
#define PE_SIGNATURE 0x00004550UL // 'PE\0\0'
|
#define PE_SIGNATURE 0x00004550UL // 'PE\0\0'
|
||||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
#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)
|
#pragma pack(push, 1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -117,6 +122,25 @@ typedef struct {
|
|||||||
static MODULE_ENTRY gModules[MAX_MODULES];
|
static MODULE_ENTRY gModules[MAX_MODULES];
|
||||||
static uint32_t gModuleCount = 0;
|
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) {
|
static uintptr_t KiFindModuleBaseInTable(const MODULE_ENTRY* modules, uint32_t count, const char* name) {
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
if (_stricmp(modules[i].Name, name) == 0) return modules[i].Base;
|
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) {
|
static uintptr_t KiFindGlobalModuleBase(const char* name) {
|
||||||
return KiFindModuleBaseInTable(gModules, gModuleCount, name);
|
return KiFindModuleBaseInTable(gModules, gModuleCount, KiCanonicalModuleName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintptr_t KiFindProcessModuleBaseInTable(
|
static uintptr_t KiFindProcessModuleBaseInTable(
|
||||||
@@ -161,7 +185,7 @@ static uintptr_t KiFindProcessModuleBaseInTable(
|
|||||||
const char* name
|
const char* name
|
||||||
) {
|
) {
|
||||||
for (uint32_t i = 0; i < count; i++) {
|
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;
|
return modules[i].Base;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,12 +232,12 @@ static void* KiGetExportAtBase(uintptr_t base, const char* functionName) {
|
|||||||
// Export lookup
|
// Export lookup
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
void* LdrGetExport(const char* moduleName, const char* functionName) {
|
void* LdrGetExport(const char* moduleName, const char* functionName) {
|
||||||
uintptr_t base = KiFindGlobalModuleBase(moduleName);
|
uintptr_t base = KiFindGlobalModuleBase(KiCanonicalModuleName(moduleName));
|
||||||
return KiGetExportAtBase(base, functionName);
|
return KiGetExportAtBase(base, functionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const char* 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);
|
return KiGetExportAtBase(base, functionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,13 +328,8 @@ static KSTATUS LdrResolveImportsCommon(
|
|||||||
while (imp->Name) {
|
while (imp->Name) {
|
||||||
const char* dllName = (const char *)(imageBase + imp->Name);
|
const char* dllName = (const char *)(imageBase + imp->Name);
|
||||||
|
|
||||||
// Win32 compatibility remap
|
//Compatibility remap.
|
||||||
if (!_stricmp(dllName, "kernel32.dll") ||
|
dllName = KiCanonicalModuleName(dllName);
|
||||||
!_stricmp(dllName, "user32.dll") ||
|
|
||||||
!_stricmp(dllName, "ole32.dll") ||
|
|
||||||
!_stricmp(dllName, "winmm.dll")) {
|
|
||||||
dllName = "win32.dll";
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ensure dependency is loaded into the process
|
// 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) {
|
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) {
|
||||||
if (!process || !name || !base) return STATUS_INVALID_PARAMETER;
|
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) {
|
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) {
|
||||||
if (!process || !filename) return STATUS_INVALID_PARAMETER;
|
if (!process || !filename) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
filename = KiCanonicalModuleName(filename);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < process->ModuleCount; i++) {
|
for (uint32_t i = 0; i < process->ModuleCount; i++) {
|
||||||
if (_stricmp(process->Modules[i].Name, filename) == 0) {
|
if (_stricmp(process->Modules[i].Name, filename) == 0) {
|
||||||
if (baseOut) *baseOut = process->Modules[i].Base;
|
if (baseOut) *baseOut = process->Modules[i].Base;
|
||||||
@@ -462,12 +505,16 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KiDebugLibtccAllocatorSlot("after load/reloc before imports", filename, imageBase);
|
||||||
|
|
||||||
s = LdrResolveImportsForProcess(process, imageBase);
|
s = LdrResolveImportsForProcess(process, imageBase);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
MmFreeUserMemory((void*)imageBase);
|
MmFreeUserMemory((void*)imageBase);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KiDebugLibtccAllocatorSlot("after imports", filename, imageBase);
|
||||||
|
|
||||||
s = LdrRegisterProcessModule(process, filename, imageBase);
|
s = LdrRegisterProcessModule(process, filename, imageBase);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
MmFreeUserMemory((void*)imageBase);
|
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.
|
// LdrLoadImage - load a PE from a raw buffer into virtual space.
|
||||||
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user) {
|
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user) {
|
||||||
if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER;
|
if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER;
|
||||||
|
if (size < 0x40) return STATUS_FAILURE;
|
||||||
|
|
||||||
uint32_t peOffset = *(uint32_t *)((uint8_t *)buffer + 0x3C);
|
const uint8_t* bytes = (const uint8_t*)buffer;
|
||||||
PE_HEADER* pe = (PE_HEADER *)((uint8_t *)buffer + peOffset);
|
uint32_t peOffset = *(const uint32_t *)(bytes + 0x3C);
|
||||||
if (pe->Signature != PE_SIGNATURE) return STATUS_FAILURE;
|
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;
|
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||||
size_t imageSize = opt->SizeOfImage;
|
size_t imageSize = opt->SizeOfImage;
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,15 @@ char* strchr(const char* s, int c) {
|
|||||||
return NULL;
|
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) {
|
void* memmove(void* dest, const void* src, size_t n) {
|
||||||
uint8_t* d = (uint8_t *)dest;
|
uint8_t* d = (uint8_t *)dest;
|
||||||
const uint8_t* s = (const uint8_t *)src;
|
const uint8_t* s = (const uint8_t *)src;
|
||||||
|
|||||||
+268
-25
@@ -43,6 +43,29 @@ typedef struct { LONG x; LONG y; } POINT;
|
|||||||
|
|
||||||
#define INVALID_HANDLE_VALUE ((HANDLE)(intptr_t)-1)
|
#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
|
// File-find structures
|
||||||
typedef struct _FILETIME {
|
typedef struct _FILETIME {
|
||||||
DWORD dwLowDateTime;
|
DWORD dwLowDateTime;
|
||||||
@@ -110,12 +133,12 @@ static void TranslatePath(const char* input, char* output) {
|
|||||||
if (!input) { output[0] = '\0'; return; }
|
if (!input) { output[0] = '\0'; return; }
|
||||||
|
|
||||||
if (input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) {
|
if (input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) {
|
||||||
// Translate "C:\" -> "0:/" (assuming 'C' maps to '0', etc.)
|
// Translate "C:\" -> "0:/". Native nt-lite paths such as
|
||||||
// Map drive letter to index: 'C' or 'c' -> '0'
|
// "0:/system/foo.dll" are already drive-indexed and pass through.
|
||||||
char drive = input[0];
|
char drive = input[0];
|
||||||
if (drive >= 'a') drive -= ('a' - 'A'); // To uppercase
|
if (drive >= 'a' && drive <= 'z') drive -= ('a' - 'A'); // To uppercase
|
||||||
|
|
||||||
output[0] = (char)('0' + (drive - 'C'));
|
output[0] = (drive >= '0' && drive <= '7') ? drive : (char)('0' + (drive - 'C'));
|
||||||
output[1] = ':';
|
output[1] = ':';
|
||||||
output[2] = '/';
|
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)
|
// GetLastError / SetLastError (single-threaded stub)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static DWORD gLastError = 0;
|
|
||||||
|
|
||||||
static SYS_PEB* gProcessPeb = NULL;
|
static SYS_PEB* gProcessPeb = NULL;
|
||||||
|
|
||||||
static SYS_PEB* Win32Peb(void) {
|
static SYS_PEB* Win32Peb(void) {
|
||||||
@@ -215,6 +315,49 @@ __declspec(dllexport) void* GetProcessHeap(void) {
|
|||||||
return (void*)1;
|
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 ---
|
// --- Win32 API ---
|
||||||
|
|
||||||
// Updated GetAsyncKeyState implementation
|
// 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 GetACP(void) { return 1252; } // Windows-1252
|
||||||
__declspec(dllexport) DWORD GetOEMCP(void) { return 437; } // DOS Latin US
|
__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
|
// File I/O
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -599,32 +747,104 @@ __declspec(dllexport) HANDLE GetCurrentThread(void) {
|
|||||||
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
|
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
|
||||||
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
|
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
|
||||||
|
|
||||||
// GetModuleHandleA/W - no PE map; return NULL so callers that check can fail
|
// Tiny TLS table; enough for MSVCRT/TCC compatibility paths that probe TLS.
|
||||||
// gracefully. GetModuleHandleA(NULL) on Windows returns the exe base; we
|
#define WIN32_TLS_SLOTS 64
|
||||||
// return a recognisable non-NULL sentinel so code that just passes it to
|
static void* gTlsSlots[WIN32_TLS_SLOTS];
|
||||||
// GetProcAddress doesn't immediately crash.
|
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) {
|
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
|
||||||
return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL;
|
if (!lpModuleName) return WIN32_HMODULE_EXE;
|
||||||
}
|
if (KiIsWin32DllNameA(lpModuleName)) return WIN32_HMODULE_WIN32;
|
||||||
|
gLastError = 126; // ERROR_MOD_NOT_FOUND
|
||||||
__declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName) {
|
|
||||||
return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetProcAddress - no dynamic linker; always fail.
|
|
||||||
__declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) {
|
|
||||||
(void)hModule; (void)lpProcName;
|
|
||||||
gLastError = 127; // ERROR_PROC_NOT_FOUND
|
|
||||||
return NULL;
|
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) {
|
__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
|
||||||
(void)lpLibFileName;
|
if (KiIsWin32DllNameA(lpLibFileName)) return WIN32_HMODULE_WIN32;
|
||||||
gLastError = 2; // ERROR_FILE_NOT_FOUND
|
gLastError = 2; // ERROR_FILE_NOT_FOUND
|
||||||
return NULL;
|
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) {
|
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
|
||||||
(void)hLibModule;
|
(void)hLibModule;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -633,8 +853,19 @@ __declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
|
|||||||
__declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
|
__declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
|
||||||
char* lpFilename, DWORD nSize) {
|
char* lpFilename, DWORD nSize) {
|
||||||
(void)hModule;
|
(void)hModule;
|
||||||
if (lpFilename && nSize) lpFilename[0] = '\0';
|
if (!lpFilename || !nSize) return 0;
|
||||||
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) {
|
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
|
||||||
@@ -1565,6 +1796,18 @@ typedef struct _RUNTIME_FUNCTION {
|
|||||||
unsigned int UnwindInfoAddress;
|
unsigned int UnwindInfoAddress;
|
||||||
} RUNTIME_FUNCTION;
|
} 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.
|
// We have no runtime function table, so RtlLookupFunctionEntry always fails.
|
||||||
__declspec(dllexport) RUNTIME_FUNCTION*
|
__declspec(dllexport) RUNTIME_FUNCTION*
|
||||||
RtlLookupFunctionEntry(unsigned long long ControlPc,
|
RtlLookupFunctionEntry(unsigned long long ControlPc,
|
||||||
|
|||||||
Reference in new issue
Block a user