build.bat can create the image if missing, split the shell into native, posix and win32, clean up the build by moving executables into directories, the bootloader is no longer responsible to resolve drivers, fix ResolvePath, every process gets a copy of a module (dll), the child always inherits the cwd, fix function exports in posix, add unlink and exit to posix, add path translator to win32, first bugcheck.
This commit is contained in:
1 parent
0c7f461c6d
commit
f80e66ecde
38 files changed
+1620
-462
No files matched your search
@@ -9,8 +9,9 @@
|
||||
|
||||
typedef struct BOOT_MODULE {
|
||||
char Name[32]; /* e.g. "hal.dll", "ide.sys" */
|
||||
uintptr_t VirtBase; /* where it was mapped */
|
||||
size_t ImageSize;
|
||||
uintptr_t VirtBase; /* Virtual address (mapped) */
|
||||
uintptr_t PhysBase; /* Raw physical address (file) */
|
||||
size_t ImageSize; /* File size / Image size */
|
||||
} BOOT_MODULE;
|
||||
|
||||
typedef struct BOOT_INFO {
|
||||
@@ -24,7 +25,6 @@ typedef struct BOOT_INFO {
|
||||
uint32_t FramebufferWidth;
|
||||
uint32_t FramebufferHeight;
|
||||
uint32_t FramebufferStride; /* pixels per row */
|
||||
uint32_t _pad0;
|
||||
|
||||
/* Loaded modules. */
|
||||
BOOT_MODULE Modules[MAX_BOOT_MODULES];
|
||||
@@ -41,5 +41,4 @@ typedef struct BOOT_INFO {
|
||||
|
||||
/* From boot.ini */
|
||||
uint32_t DebugEnabled;
|
||||
uint32_t _pad2;
|
||||
} BOOT_INFO;
|
||||
@@ -46,17 +46,19 @@ __declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) {
|
||||
static uint8_t ResolvePath(const char* path, char* resolved, size_t resolvedSize) {
|
||||
// Strip explicit drive prefix "N:\"
|
||||
uint8_t drive = 0;
|
||||
bool fromDriveRoot = false;
|
||||
if (path[0] >= '0' && path[0] <= '7' &&
|
||||
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
|
||||
drive = (uint8_t)(path[0] - '0');
|
||||
path = path + 3;
|
||||
fromDriveRoot = true;
|
||||
}
|
||||
|
||||
char work[CWD_MAX * 2];
|
||||
work[0] = '\0';
|
||||
|
||||
// Initialize our working base
|
||||
if (path[0] == '\\' || path[0] == '/') {
|
||||
if (path[0] == '\\' || path[0] == '/' || fromDriveRoot) {
|
||||
// Absolute path: start from the root (empty work buffer)
|
||||
// The segment loop below will automatically skip the leading separators.
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <string.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <printf.h>
|
||||
int _fltused; // fuckass
|
||||
|
||||
@@ -62,3 +65,123 @@ __declspec(dllexport) void KePrintConsole(const char* buf, size_t len) {
|
||||
for (size_t i = 0; i < len; i++)
|
||||
PrintChar(buf[i], 0xFFFFFF, 0x000000);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// KeBugCheckProcess - user-mode fault handler
|
||||
//
|
||||
// Resolves the faulting RIP to a module name, prints a yellow crash banner,
|
||||
// kills the offending process, then exits the current thread so the scheduler
|
||||
// can continue running everything else.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Return the short exception name for a vector number.
|
||||
static const char* FaultVectorName(int vector) {
|
||||
switch (vector) {
|
||||
case 0: return "#DE Divide Error";
|
||||
case 13: return "#GP General Protection";
|
||||
case 14: return "#PF Page Fault";
|
||||
default: return "Unknown Exception";
|
||||
}
|
||||
}
|
||||
|
||||
// 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* ResolveModule(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 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)
|
||||
return proc->Modules[i].Name;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static void PrintCrashChar(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.
|
||||
const char* faultName = FaultVectorName(vector);
|
||||
const char* procName = proc ? proc->Name : "unknown";
|
||||
uint32_t pid = proc ? proc->ProcessId : 0;
|
||||
const char* modName = proc ? ResolveModule(proc, rip) : "unknown";
|
||||
|
||||
// Amber crash banner.
|
||||
static const char banner[] = "\n*** APPLICATION CRASH ***\n";
|
||||
for (const char* p = banner; *p; p++)
|
||||
PrintCrashChar(*p);
|
||||
|
||||
printf_set_putchar(PrintCrashChar);
|
||||
|
||||
if (vector == 14)
|
||||
printf("Process: %s (PID %u) | Fault: %s\n"
|
||||
"Module: %s | RIP: %016llx | Code: %04llx | CR2: %016llx\n",
|
||||
procName, pid, faultName,
|
||||
modName,
|
||||
(unsigned long long)rip,
|
||||
(unsigned long long)errCode,
|
||||
(unsigned long long)cr2);
|
||||
else
|
||||
printf("Process: %s (PID %u) | Fault: %s\n"
|
||||
"Module: %s | RIP: %016llx | Code: %04llx\n",
|
||||
procName, pid, faultName,
|
||||
modName,
|
||||
(unsigned long long)rip,
|
||||
(unsigned long long)errCode);
|
||||
|
||||
// Restore normal (green) output for subsequent kernel messages.
|
||||
printf_set_putchar(kernel_putchar);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// KeFaultDispatch - single C entry point for all three IDT fault stubs.
|
||||
//
|
||||
// The asm stubs (ke_syscall.asm) collect the CPU exception frame values and
|
||||
// call here. We check the CS RPL to decide: kernel fault → KePanic (halt),
|
||||
// user fault → KeBugCheckProcess (kill process, resume scheduler).
|
||||
// ---------------------------------------------------------------------------
|
||||
__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);
|
||||
}
|
||||
+18
-2
@@ -4,8 +4,24 @@
|
||||
#include <stdint.h>
|
||||
|
||||
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, ...);
|
||||
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
|
||||
__declspec(dllexport) 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).
|
||||
//
|
||||
// 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
|
||||
@@ -50,6 +50,31 @@ static void ProcessDestroy(OBJECT* obj) {
|
||||
}
|
||||
}
|
||||
|
||||
// Release the main executable image.
|
||||
if (proc->ImageBase && proc->ImageSize) {
|
||||
MmFreeUserMemory((void *)proc->ImageBase, proc->ImageSize);
|
||||
proc->ImageBase = 0;
|
||||
proc->ImageSize = 0;
|
||||
}
|
||||
|
||||
// Release per-process modules.
|
||||
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
|
||||
if (proc->Modules[i].Base && proc->Modules[i].Size) {
|
||||
MmFreeUserMemory((void *)proc->Modules[i].Base, proc->Modules[i].Size);
|
||||
}
|
||||
proc->Modules[i].Base = 0;
|
||||
proc->Modules[i].Size = 0;
|
||||
proc->Modules[i].Name[0] = '\0';
|
||||
}
|
||||
proc->ModuleCount = 0;
|
||||
|
||||
// Release the user command line buffer.
|
||||
if (proc->UserCmdlineBase && proc->UserCmdlineSize) {
|
||||
MmFreeUserMemory((void *)proc->UserCmdlineBase, proc->UserCmdlineSize);
|
||||
proc->UserCmdlineBase = 0;
|
||||
proc->UserCmdlineSize = 0;
|
||||
}
|
||||
|
||||
// Release the exit event.
|
||||
if (proc->ExitEvent) {
|
||||
ObDereferenceObject(&proc->ExitEvent->Header);
|
||||
@@ -86,6 +111,11 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
|
||||
proc->ThreadCount = 0;
|
||||
proc->HandleLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
proc->ExitCode = 0;
|
||||
proc->ImageBase = 0;
|
||||
proc->ImageSize = 0;
|
||||
proc->UserCmdlineBase = 0;
|
||||
proc->UserCmdlineSize = 0;
|
||||
proc->ModuleCount = 0;
|
||||
|
||||
// Slot 0 is the invalid handle - leave it NULL.
|
||||
memset(proc->Handles, 0, sizeof(proc->Handles));
|
||||
|
||||
@@ -6,10 +6,20 @@
|
||||
#include <ke_list.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_event.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define PROCESS_NAME_MAX 64
|
||||
#define HANDLE_TABLE_SIZE 256
|
||||
|
||||
|
||||
#define PROCESS_MODULE_MAX 16
|
||||
|
||||
typedef struct PROCESS_MODULE {
|
||||
char Name[32];
|
||||
uintptr_t Base;
|
||||
size_t Size;
|
||||
} PROCESS_MODULE;
|
||||
|
||||
typedef struct PROCESS {
|
||||
OBJECT Header; /* must be first */
|
||||
|
||||
@@ -36,6 +46,18 @@ typedef struct PROCESS {
|
||||
|
||||
int ExitCode;
|
||||
|
||||
/* Main executable image. Used for cleanup. */
|
||||
uintptr_t ImageBase;
|
||||
size_t ImageSize;
|
||||
|
||||
/* User command line buffer passed to the initial thread. */
|
||||
uintptr_t UserCmdlineBase;
|
||||
size_t UserCmdlineSize;
|
||||
|
||||
/* Per-process loaded modules (user processes only). */
|
||||
PROCESS_MODULE Modules[PROCESS_MODULE_MAX];
|
||||
uint32_t ModuleCount;
|
||||
|
||||
/* Signaled (manual-reset) when the process exits.
|
||||
Waiters block on this via KeWaitForEvent. */
|
||||
EVENT* ExitEvent;
|
||||
@@ -43,13 +65,13 @@ typedef struct PROCESS {
|
||||
LIST_ENTRY GlobalLink; /* node in the global process list */
|
||||
} PROCESS;
|
||||
|
||||
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
|
||||
void KeExitProcess(PROCESS* process, int exitCode);
|
||||
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
|
||||
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
|
||||
void KeExitProcess(PROCESS* process, int exitCode);
|
||||
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
|
||||
|
||||
/* Handle table operations. */
|
||||
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
|
||||
void KeCloseHandle(PROCESS* process, uint32_t handle);
|
||||
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
|
||||
void KeCloseHandle(PROCESS* process, uint32_t handle);
|
||||
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle);
|
||||
|
||||
/* Return the global process list head (kernel-internal). */
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
@@ -28,7 +29,6 @@ void KeInitializeScheduler(void) {
|
||||
for (int i = 0; i < PriorityCount; i++) LIST_INIT(&gRunQueue[i]);
|
||||
gSchedLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
|
||||
extern KSTATUS KeCreateProcess(const char *, PROCESS **);
|
||||
PROCESS* idleProc;
|
||||
KeCreateProcess("idle", &idleProc);
|
||||
|
||||
|
||||
+51
-68
@@ -37,93 +37,76 @@ KeSyscallEntry PROC
|
||||
KeSyscallEntry ENDP
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Fault stubs - print a message and halt.
|
||||
; Fault stubs - thin wrappers that collect the CPU exception frame and
|
||||
; forward to KeFaultDispatch (C), which handles both kernel panics and
|
||||
; user-process bugchecks.
|
||||
;
|
||||
; 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)
|
||||
; ---------------------------------------------------------------------------
|
||||
EXTERN KePanic:PROC
|
||||
EXTERN KeFaultDispatch:PROC
|
||||
|
||||
.data
|
||||
; We define separate strings to maximize the info we can print
|
||||
StrKernel DB "Kernel Mode", 0
|
||||
StrUser DB "User Mode", 0
|
||||
|
||||
MsgFaultDE DB "PANIC: Divide Error (#0) | RIP: %016llx | Mode: %s", 0Ah, 0
|
||||
MsgFaultGP DB "PANIC: General Protection (#13) | RIP: %016llx | Code: %04x | Mode: %s", 0Ah, 0
|
||||
MsgFaultPF DB "PANIC: Page Fault (#14) | RIP: %016llx | Code: %04x | CR2: %016llx | %s", 0Ah, 0
|
||||
|
||||
.code
|
||||
; 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
|
||||
|
||||
; ---------------------------------------------------------
|
||||
; Divide Error (#0) - NO Error Code pushed by CPU
|
||||
; Stack: [rsp] = RIP, [rsp+8] = CS, [rsp+16] = RFLAGS
|
||||
; Divide Error (#0) - NO error code pushed by CPU
|
||||
; Exception frame at entry: [rsp]=RIP [rsp+8]=CS [rsp+16]=RFLAGS
|
||||
; ---------------------------------------------------------
|
||||
KeFaultDE PROC
|
||||
mov rdx, [rsp] ; Arg 2: RIP
|
||||
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
|
||||
|
||||
; Determine User/Kernel mode from CS
|
||||
mov rax, [rsp + 8] ; Read CS
|
||||
and rax, 3 ; Isolate the bottom 2 bits (RPL)
|
||||
lea r8, StrKernel
|
||||
lea r9, StrUser
|
||||
test rax, rax ; If 0, it's Kernel. If >0, it's User.
|
||||
cmovnz r8, r9 ; Arg 3: Mode String (moves r9 into r8 if Not Zero)
|
||||
|
||||
lea rcx, MsgFaultDE ; Arg 1: Format string
|
||||
|
||||
; Safely align stack and allocate shadow space for the call
|
||||
and rsp, -16 ; Force 16-byte ABI stack alignment
|
||||
sub rsp, 20h ; Allocate 32 bytes of shadow space
|
||||
call KePanic
|
||||
and rsp, -16
|
||||
sub rsp, FAULT_FRAME_SIZE
|
||||
mov [rsp+20h], r10 ; Arg5: cs
|
||||
call KeFaultDispatch ; _Noreturn - never returns
|
||||
KeFaultDE ENDP
|
||||
|
||||
; ---------------------------------------------------------
|
||||
; General Protection Fault (#13) - Error Code PUSHED
|
||||
; Stack: [rsp] = ErrCode, [rsp+8] = RIP, [rsp+16] = CS
|
||||
; General Protection Fault (#13) - error code pushed by CPU
|
||||
; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS
|
||||
; ---------------------------------------------------------
|
||||
KeFaultGP PROC
|
||||
mov r8, [rsp] ; Arg 3: Error Code
|
||||
mov rdx, [rsp + 8] ; Arg 2: RIP
|
||||
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
|
||||
|
||||
; Determine User/Kernel mode from CS
|
||||
mov rax, [rsp + 16] ; Read CS
|
||||
and rax, 3
|
||||
lea r9, StrKernel
|
||||
lea r10, StrUser
|
||||
test rax, rax
|
||||
cmovnz r9, r10 ; Arg 4: Mode String
|
||||
|
||||
lea rcx, MsgFaultGP ; Arg 1: Format string
|
||||
|
||||
and rsp, -16 ; Align stack
|
||||
sub rsp, 20h ; 32 bytes shadow space
|
||||
call KePanic
|
||||
and rsp, -16
|
||||
sub rsp, FAULT_FRAME_SIZE
|
||||
mov [rsp+20h], r10 ; Arg5: cs
|
||||
call KeFaultDispatch
|
||||
KeFaultGP ENDP
|
||||
|
||||
; ---------------------------------------------------------
|
||||
; Page Fault (#14) - Error Code PUSHED
|
||||
; Stack: [rsp] = ErrCode, [rsp+8] = RIP, [rsp+16] = CS
|
||||
; Page Fault (#14) - error code pushed by CPU
|
||||
; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS
|
||||
; ---------------------------------------------------------
|
||||
KeFaultPF PROC
|
||||
mov r8, [rsp] ; Arg 3: Error Code
|
||||
mov rdx, [rsp + 8] ; Arg 2: RIP
|
||||
mov r9, cr2 ; Arg 4: CR2 (The faulting virtual address)
|
||||
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
|
||||
|
||||
; Determine User/Kernel mode from CS
|
||||
mov rax, [rsp + 16] ; Read CS
|
||||
and rax, 3
|
||||
lea r10, StrKernel
|
||||
lea r11, StrUser
|
||||
test rax, rax
|
||||
cmovnz r10, r11 ; r10 now holds the Mode String
|
||||
|
||||
lea rcx, MsgFaultPF ; Arg 1: Format string
|
||||
|
||||
; We have 5 arguments for PF! (rcx, rdx, r8, r9, and the stack)
|
||||
; x64 ABI dictates the 5th argument goes on the stack just above the shadow space.
|
||||
and rsp, -16 ; Align stack
|
||||
sub rsp, 30h ; 32 bytes shadow space + 16 bytes for 5th arg and padding
|
||||
mov [rsp + 20h], r10 ; Arg 5: Mode String (placed exactly after shadow space)
|
||||
|
||||
call KePanic
|
||||
and rsp, -16
|
||||
sub rsp, FAULT_FRAME_SIZE
|
||||
mov [rsp+20h], r10 ; Arg5: cs
|
||||
call KeFaultDispatch
|
||||
KeFaultPF ENDP
|
||||
|
||||
EXTERN KeIrqDispatch:PROC
|
||||
|
||||
@@ -769,6 +769,15 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
s = KeCreateProcess(path, &proc);
|
||||
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
|
||||
|
||||
// Inherit CWD from parent
|
||||
PROCESS* parent = KeGetCurrentProcess();
|
||||
if (parent) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&parent->CwdLock);
|
||||
strncpy(proc->CurrentDirectory, parent->CurrentDirectory, sizeof(proc->CurrentDirectory) - 1);
|
||||
proc->CurrentDirectory[sizeof(proc->CurrentDirectory) - 1] = '\0';
|
||||
KeReleaseSpinlock(&parent->CwdLock, flags);
|
||||
}
|
||||
|
||||
// Store the command line in the process so _start can pick it up.
|
||||
// If no cmdline was supplied default to just the image path.
|
||||
if (cmdline && cmdline[0])
|
||||
@@ -781,8 +790,10 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
// Copy into user memory so the initial thread can access it directly.
|
||||
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
|
||||
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
|
||||
if (!userCmdline) { ObDereferenceObject(&proc->Header); return STATUS_NO_MEMORY; }
|
||||
if (!userCmdline) { MmFreeUserMemory(mem, size); ObDereferenceObject(&proc->Header); return STATUS_NO_MEMORY; }
|
||||
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
|
||||
proc->UserCmdlineBase = (uintptr_t)userCmdline;
|
||||
proc->UserCmdlineSize = cmdlineLen;
|
||||
|
||||
// Map the PE image into memory.
|
||||
uintptr_t imageBase = 0;
|
||||
@@ -790,13 +801,26 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
MmFreeUserMemory(mem, size);
|
||||
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
||||
|
||||
s = LdrResolveImportsForProcess(proc, imageBase);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Spin up the initial thread. Pass userCmdline as UserArg so _start
|
||||
// receives it directly in RCX without an extra syscall.
|
||||
THREAD* thread;
|
||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||
s = KeCreateThread(proc, entryPoint, (uintptr_t)userCmdline,
|
||||
PriorityNormal, &thread);
|
||||
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return s;
|
||||
}
|
||||
proc->ImageBase = imageBase;
|
||||
proc->ImageSize = LdrGetImageSize(imageBase);
|
||||
|
||||
// Give the caller a handle to the new process so they can wait on it.
|
||||
PROCESS* caller = KeGetCurrentProcess();
|
||||
@@ -809,7 +833,6 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
*handleOut = handle;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SYS_WAIT_FOR_PROCESS (51) a0=handle
|
||||
// Blocks until the target process signals its ExitEvent.
|
||||
|
||||
+100
-76
@@ -31,13 +31,17 @@ typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *);
|
||||
static const HAL_API* gKmainHal;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Phase Helper: Resolve Imports for all registered modules
|
||||
// Phase Helper: Resolve Imports for core modules
|
||||
// ---------------------------------------------------------------------------
|
||||
static void ResolveSystemImports(BOOT_INFO* bootInfo) {
|
||||
// Resolve imports for all loaded modules (drivers may import from hal.dll, win32.dll imports from syscall.dll, etc).
|
||||
// Starting at 2 to skip the kernel and HAL base images.
|
||||
for (uint32_t i = 2; i < bootInfo->ModuleCount; i++) {
|
||||
LdrResolveImports(bootInfo->Modules[i].VirtBase);
|
||||
// Only resolve imports for kernel.exe and hal.dll (typically indices 0 and 1).
|
||||
// The bootloader maps these, but drivers are now passed as raw physical files.
|
||||
uint32_t coreModuleCount = (bootInfo->ModuleCount > 2) ? 2 : bootInfo->ModuleCount;
|
||||
|
||||
for (uint32_t i = 0; i < coreModuleCount; i++) {
|
||||
if (bootInfo->Modules[i].VirtBase != 0) {
|
||||
LdrResolveImports(bootInfo->Modules[i].VirtBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,19 +67,44 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
|
||||
// boot drivers (provided by the bootloader)
|
||||
for (uint32_t i = 0; i < bootInfo->ModuleCount; i++) {
|
||||
const char* name = bootInfo->Modules[i].Name;
|
||||
BOOT_MODULE* mod = &bootInfo->Modules[i];
|
||||
|
||||
// Identify .sys files by extension
|
||||
size_t len = strlen(name);
|
||||
if (len < 4 || strcmp(name + len - 4, ".sys")) continue;
|
||||
// Skip modules that are already mapped (Kernel/HAL)
|
||||
// or have no physical data (invalid)
|
||||
if (mod->VirtBase != 0 || mod->PhysBase == 0) continue;
|
||||
|
||||
DbgPrint("InitializeDrivers: Executing DriverEntry for %s\n", name);
|
||||
DbgPrint("InitializeDrivers: Loading boot driver %s\n", mod->Name);
|
||||
|
||||
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)(LdrGetEntryPoint(bootInfo->Modules[i].VirtBase));
|
||||
// Calculate virtual address of the raw file in the physical window
|
||||
void* rawPeBuffer = (void*)(bootInfo->PhysicalMapBase + mod->PhysBase);
|
||||
|
||||
KSTATUS ds = entry(hal);
|
||||
if (!KSUCCESS(ds)) {
|
||||
DbgPrint("InitializeDrivers: Driver %s failed to init!\n", name);
|
||||
// Map the PE image into kernel space
|
||||
uintptr_t mappedBase = 0;
|
||||
KSTATUS st = LdrLoadImage(rawPeBuffer, mod->ImageSize, &mappedBase, false);
|
||||
if (!KSUCCESS(st)) {
|
||||
DbgPrint("ERROR: Failed to map driver %s (0x%X)\n", mod->Name, st);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the struct so other modules can find it
|
||||
mod->VirtBase = mappedBase;
|
||||
|
||||
// Resolve imports (can now see Kernel, HAL, and previous drivers)
|
||||
if (!KSUCCESS(LdrResolveImports(mappedBase))) {
|
||||
DbgPrint("ERROR: Failed to resolve imports for %s\n", mod->Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Register with the global module list so subsequent drivers can import from this one
|
||||
LdrRegisterModule(mod->Name, mod->VirtBase);
|
||||
|
||||
// Execute Entry Point
|
||||
DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)LdrGetEntryPoint(mappedBase);
|
||||
if (entry) {
|
||||
KSTATUS ds = entry(hal);
|
||||
if (!KSUCCESS(ds)) {
|
||||
DbgPrint("WARNING: Driver %s returned failure status 0x%X\n", mod->Name, ds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +166,13 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
uintptr_t mappedBase = 0;
|
||||
if (!KSUCCESS(LdrLoadImage(drvMem, drvSize, &mappedBase, false))) { // Load into kernel space
|
||||
DbgPrint("InitializeDrivers: Failed to load runtime driver: %s\n", runtimeKeys[i]);
|
||||
MmFreePool(drvMem);
|
||||
continue;
|
||||
}
|
||||
MmFreePool(drvMem);
|
||||
|
||||
if (!KSUCCESS(LdrResolveImports(mappedBase))) {
|
||||
DbgPrint("InitializeDrivers: Failed to resolve imports for runtime driver: %s\n", runtimeKeys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -163,41 +199,6 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
|
||||
MmFreePool(mem);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Phase Helper: Load a DLL from disk and register it
|
||||
// ---------------------------------------------------------------------------
|
||||
static void LoadAndRegisterDll(const char* filename) {
|
||||
KSTATUS s;
|
||||
FILE_OBJECT* f;
|
||||
|
||||
// Read the DLL from disk
|
||||
s = IoOpenFile(filename, &f);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
KePanic("LoadAndRegisterDll: Failed to open DLL: %s\n", filename);
|
||||
}
|
||||
|
||||
uint64_t size = IoGetFileSize(f);
|
||||
void* mem = MmAllocatePool(size); // Temporary pool for the raw file
|
||||
size_t got;
|
||||
if (!KSUCCESS(IoReadFile(f, mem, size, &got))) {
|
||||
KePanic("LoadAndRegisterDll: Failed to read %s\n", filename);
|
||||
IoCloseFile(f);
|
||||
}
|
||||
IoCloseFile(f);
|
||||
|
||||
// Map the DLL into memory
|
||||
// Passing 'true' for user-mode
|
||||
uintptr_t mappedBase = 0;
|
||||
s = LdrLoadImage(mem, size, &mappedBase, true);
|
||||
if (!KSUCCESS(s)) {
|
||||
KePanic("LoadAndRegisterDll: Failed to map %s\n", filename);
|
||||
}
|
||||
|
||||
MmFreePool(mem); // Free the raw file buffer, we only need the mapped image now
|
||||
|
||||
LdrRegisterModule(filename, mappedBase); // Register the module
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Phase Helper: Load and launch a process from the filesystem.
|
||||
// Returns the new PROCESS* with one reference owned by the caller.
|
||||
@@ -206,69 +207,102 @@ static void LoadAndRegisterDll(const char* filename) {
|
||||
static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
|
||||
KSTATUS s;
|
||||
FILE_OBJECT* f;
|
||||
|
||||
// Read the executable file into a raw buffer
|
||||
|
||||
// Read the executable file into a raw buffer.
|
||||
s = IoOpenFile(filePath, &f);
|
||||
DbgPrint("LaunchUserProcess: IoOpenFile: %d\n", s);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
|
||||
return (PROCESS*)0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint64_t size = IoGetFileSize(f);
|
||||
DbgPrint("LaunchUserProcess: IoGetFileSize: %llu\n", size);
|
||||
|
||||
void* mem = MmAllocateUserMemory(size);
|
||||
DbgPrint("LaunchUserProcess: MmAllocateUserMemory: 0x%llX\n", mem);
|
||||
if (!mem) {
|
||||
IoCloseFile(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t got;
|
||||
size_t got = 0;
|
||||
s = IoReadFile(f, mem, size, &got);
|
||||
DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s);
|
||||
IoCloseFile(f);
|
||||
DbgPrint("LaunchUserProcess: IoCloseFile\n");
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreeUserMemory(mem, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create the process object
|
||||
// Create the process object.
|
||||
PROCESS* proc;
|
||||
THREAD* thread;
|
||||
|
||||
s = KeCreateProcess(filePath, &proc);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("Failed to create process. System halted.\n");
|
||||
return (PROCESS*)0;
|
||||
MmFreeUserMemory(mem, size);
|
||||
return 0;
|
||||
}
|
||||
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath);
|
||||
|
||||
strncpy(proc->Cmdline, args, sizeof(proc->Cmdline) - 1);
|
||||
if (args && args[0])
|
||||
strncpy(proc->Cmdline, args, sizeof(proc->Cmdline) - 1);
|
||||
else
|
||||
strncpy(proc->Cmdline, filePath, sizeof(proc->Cmdline) - 1);
|
||||
proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0';
|
||||
|
||||
// proc->Cmdline is kernel pool (ffffc000...) - not readable at CPL=3.
|
||||
// Copy into user memory before handing the pointer to the initial thread.
|
||||
char* userCmdline = (char*)MmAllocateUserMemory(sizeof(proc->Cmdline));
|
||||
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
|
||||
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
|
||||
if (!userCmdline) {
|
||||
DbgPrint("LaunchUserProcess: Failed to allocate user cmdline buffer.\n");
|
||||
return (PROCESS*)0;
|
||||
MmFreeUserMemory(mem, size);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
memcpy(userCmdline, proc->Cmdline, sizeof(proc->Cmdline));
|
||||
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
|
||||
proc->UserCmdlineBase = (uintptr_t)userCmdline;
|
||||
proc->UserCmdlineSize = cmdlineLen;
|
||||
|
||||
// Parse the raw PE file and map it into executable memory
|
||||
// Parse the raw PE file and map it into executable memory.
|
||||
uintptr_t imageBase = 0;
|
||||
s = LdrLoadImage(mem, size, &imageBase, true);
|
||||
MmFreeUserMemory(mem, size);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LaunchUserProcess: LdrLoadImage failed with status: %d\n", s);
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = LdrResolveImportsForProcess(proc, imageBase);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LaunchUserProcess: Failed to resolve EXE imports for %s (status %d)\n", filePath, s);
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||
DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint);
|
||||
|
||||
// Create the main thread, pointing it to the PE Entry Point
|
||||
// Create the main thread, pointing it to the PE Entry Point.
|
||||
s = KeCreateThread(proc, (uintptr_t)entryPoint, (uintptr_t)userCmdline, PriorityNormal, &thread);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
ObDereferenceObject(&proc->Header);
|
||||
return 0;
|
||||
}
|
||||
proc->ImageBase = imageBase;
|
||||
proc->ImageSize = LdrGetImageSize(imageBase);
|
||||
DbgPrint("LaunchUserProcess: Thread created and queued!\n");
|
||||
|
||||
// Caller owns the reference returned by KeCreateProcess.
|
||||
return proc;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Kernel thread: wait for a process to exit, then log a message and shut down.
|
||||
// Takes ownership of the PROCESS* reference passed as arg; releases it on exit.
|
||||
@@ -312,15 +346,6 @@ void SystemInitThread(void* arg) {
|
||||
|
||||
ResolveSystemImports(args->bootInfo);
|
||||
|
||||
DbgPrint("SystemInitThread: Loading system DLLs...\n");
|
||||
LoadAndRegisterDll("syscall.dll");
|
||||
LoadAndRegisterDll("win32.dll");
|
||||
LoadAndRegisterDll("bink2w64.dll");
|
||||
|
||||
// Now that they are in memory and registered, we link them together
|
||||
// (e.g., win32.dll resolving its imports against syscall.dll)
|
||||
ResolveSystemImports(args->bootInfo);
|
||||
|
||||
args->hal->Screen.Clear(0x000000);
|
||||
|
||||
// Print memory status now that the system is fully alive
|
||||
@@ -330,7 +355,7 @@ void SystemInitThread(void* arg) {
|
||||
DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f));
|
||||
|
||||
// Launch User-Mode Shell (this will trigger AHCI & FAT32 which CAN safely block now)
|
||||
PROCESS* shellProc = LaunchUserProcess("init.exe", args->bootInfo->CommandLine);
|
||||
PROCESS* shellProc = LaunchUserProcess("system/init.exe", args->bootInfo->CommandLine);
|
||||
|
||||
if (shellProc) {
|
||||
KSTATUS ws = KeCreateKernelThread((uintptr_t)ProcessWatcherThread,
|
||||
@@ -340,10 +365,9 @@ void SystemInitThread(void* arg) {
|
||||
DbgPrint("SystemInitThread: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MmFreePool(args);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main Kernel Entry
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
+252
-36
@@ -3,6 +3,8 @@
|
||||
#include <string.h>
|
||||
#include <ldr_image.h>
|
||||
#include <boot_info.h>
|
||||
#include <ke_process.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
|
||||
@@ -109,39 +111,72 @@ typedef struct {
|
||||
typedef struct {
|
||||
char Name[32];
|
||||
uintptr_t Base;
|
||||
size_t Size;
|
||||
} MODULE_ENTRY;
|
||||
|
||||
static MODULE_ENTRY gModules[MAX_MODULES];
|
||||
static int gModuleCount = 0;
|
||||
static uint32_t gModuleCount = 0;
|
||||
|
||||
static uintptr_t FindModuleBaseInTable(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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void RegisterModuleEntry(MODULE_ENTRY* entry, const char* name, uintptr_t base, size_t size) {
|
||||
strncpy(entry->Name, name, 31);
|
||||
entry->Name[31] = '\0';
|
||||
entry->Base = base;
|
||||
entry->Size = size;
|
||||
}
|
||||
|
||||
void LdrRegisterModulesFromBootInfo(const BOOT_INFO* bootInfo) {
|
||||
for (uint32_t i = 0; i < bootInfo->ModuleCount && i < MAX_MODULES; i++) {
|
||||
strncpy(gModules[gModuleCount].Name, bootInfo->Modules[i].Name, 31);
|
||||
gModules[gModuleCount].Base = bootInfo->Modules[i].VirtBase;
|
||||
gModuleCount++;
|
||||
for (uint32_t i = 0; i < bootInfo->ModuleCount && gModuleCount < MAX_MODULES; i++) {
|
||||
// Only register modules that have a virtual base (Kernel, HAL)
|
||||
if (bootInfo->Modules[i].VirtBase != 0) {
|
||||
RegisterModuleEntry(&gModules[gModuleCount],
|
||||
bootInfo->Modules[i].Name,
|
||||
bootInfo->Modules[i].VirtBase,
|
||||
bootInfo->Modules[i].ImageSize);
|
||||
gModuleCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LdrRegisterModule(const char* name, uintptr_t base) {
|
||||
if (gModuleCount < MAX_MODULES) {
|
||||
strncpy(gModules[gModuleCount].Name, name, 31);
|
||||
gModules[gModuleCount].Base = base;
|
||||
RegisterModuleEntry(&gModules[gModuleCount], name, base, 0);
|
||||
gModuleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
static uintptr_t FindModuleBase(const char* name) {
|
||||
for (int i = 0; i < gModuleCount; i++) {
|
||||
if (_stricmp(gModules[i].Name, name) == 0) return gModules[i].Base;
|
||||
static uintptr_t FindGlobalModuleBase(const char* name) {
|
||||
return FindModuleBaseInTable(gModules, gModuleCount, name);
|
||||
}
|
||||
|
||||
static uintptr_t FindProcessModuleBaseInTable(
|
||||
const PROCESS_MODULE* 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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Export lookup
|
||||
// ---------------------------------------------------------------------------
|
||||
void* LdrGetExport(const char* moduleName, const char* functionName) {
|
||||
uintptr_t base = FindModuleBase(moduleName);
|
||||
static uintptr_t FindProcessOrGlobalModuleBase(PROCESS* process, const char* name) {
|
||||
if (process) {
|
||||
uintptr_t base = FindProcessModuleBaseInTable(process->Modules, process->ModuleCount, name);
|
||||
if (base) return base;
|
||||
}
|
||||
return FindGlobalModuleBase(name);
|
||||
}
|
||||
|
||||
static void* GetExportAtBase(uintptr_t base, const char* functionName) {
|
||||
if (!base) return (void *)0;
|
||||
|
||||
uint32_t peOffset = *(uint32_t *)(base + 0x3C);
|
||||
@@ -169,6 +204,26 @@ void* LdrGetExport(const char* moduleName, const char* functionName) {
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Export lookup
|
||||
// ---------------------------------------------------------------------------
|
||||
void* LdrGetExport(const char* moduleName, const char* functionName) {
|
||||
uintptr_t base = FindGlobalModuleBase(moduleName);
|
||||
return GetExportAtBase(base, functionName);
|
||||
}
|
||||
|
||||
void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const char* functionName) {
|
||||
uintptr_t base = FindProcessOrGlobalModuleBase(process, moduleName);
|
||||
return GetExportAtBase(base, functionName);
|
||||
}
|
||||
|
||||
size_t LdrGetImageSize(uintptr_t base) {
|
||||
uint32_t peOffset = *(uint32_t *)(base + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)(base + peOffset);
|
||||
if (pe->Signature != PE_SIGNATURE) return 0;
|
||||
return (size_t)pe->OptionalHeader.SizeOfImage;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Entry point lookup
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -216,43 +271,210 @@ static KSTATUS LdrApplyRelocations(uintptr_t base, uintptr_t delta) {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Import resolution
|
||||
// ---------------------------------------------------------------------------
|
||||
KSTATUS LdrResolveImports(uintptr_t imageBase) {
|
||||
typedef void* (*EXPORT_LOOKUP_FN)(void* ctx, const char* moduleName, const char* functionName);
|
||||
|
||||
static void* GlobalExportLookup(void* ctx, const char* moduleName, const char* functionName) {
|
||||
(void)ctx;
|
||||
return LdrGetExport(moduleName, functionName);
|
||||
}
|
||||
|
||||
static void* ProcessExportLookup(void* ctx, const char* moduleName, const char* functionName) {
|
||||
return LdrGetExportForProcess((PROCESS*)ctx, moduleName, functionName);
|
||||
}
|
||||
|
||||
static KSTATUS LdrResolveImportsCommon(
|
||||
PROCESS* process,
|
||||
uintptr_t imageBase,
|
||||
EXPORT_LOOKUP_FN lookup,
|
||||
void* ctx
|
||||
) {
|
||||
uint32_t peOffset = *(uint32_t *)(imageBase + 0x3C);
|
||||
PE_HEADER* pe = (PE_HEADER *)(imageBase + peOffset);
|
||||
OPT_HEADER64* opt = &pe->OptionalHeader;
|
||||
|
||||
if (opt->NumberOfRvaAndSizes < 2) return STATUS_SUCCESS; // no imports
|
||||
if (opt->NumberOfRvaAndSizes < 2)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
uint32_t impRva = opt->DataDirectory[1].VirtualAddress;
|
||||
if (!impRva) return STATUS_SUCCESS;
|
||||
if (!impRva)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
IMPORT_DESC* imp = (IMPORT_DESC *)(imageBase + impRva);
|
||||
|
||||
while (imp->Name) {
|
||||
const char* dllName = (const char *)(imageBase + imp->Name);
|
||||
if (!_stricmp(dllName, "kernel32.dll") || !_stricmp(dllName, "user32.dll")
|
||||
|| !_stricmp(dllName, "ole32.dll") || !_stricmp(dllName, "winmm.dll"))
|
||||
|
||||
// Win32 compatibility remap
|
||||
if (!_stricmp(dllName, "kernel32.dll") ||
|
||||
!_stricmp(dllName, "user32.dll") ||
|
||||
!_stricmp(dllName, "ole32.dll") ||
|
||||
!_stricmp(dllName, "winmm.dll")) {
|
||||
dllName = "win32.dll";
|
||||
uint64_t* thunk = (uint64_t *)(imageBase + imp->FirstThunk);
|
||||
uint64_t* origThunk = (uint64_t *)(imageBase + (imp->OriginalFirstThunk
|
||||
? imp->OriginalFirstThunk
|
||||
: imp->FirstThunk));
|
||||
}
|
||||
|
||||
//
|
||||
// Ensure dependency is loaded into the process
|
||||
//
|
||||
if (process) {
|
||||
uintptr_t modBase =
|
||||
FindProcessOrGlobalModuleBase(process, dllName);
|
||||
|
||||
if (!modBase) {
|
||||
KSTATUS s =
|
||||
LdrLoadModuleForProcess(process, dllName, &modBase);
|
||||
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint(
|
||||
"LdrResolveImports: Failed to load dependency %s (%d)\n",
|
||||
dllName,
|
||||
s
|
||||
);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t* thunk =
|
||||
(uint64_t *)(imageBase + imp->FirstThunk);
|
||||
|
||||
uint64_t* origThunk =
|
||||
(uint64_t *)(imageBase +
|
||||
(imp->OriginalFirstThunk
|
||||
? imp->OriginalFirstThunk
|
||||
: imp->FirstThunk));
|
||||
|
||||
while (*origThunk) {
|
||||
if (*origThunk & (1ULL << 63)) {
|
||||
// Import by ordinal - not supported.
|
||||
DbgPrint("LdrResolveImports: Ordinal imports not supported! (%s)\n", dllName);
|
||||
DbgPrint(
|
||||
"LdrResolveImports: Ordinal imports not supported! (%s)\n",
|
||||
dllName
|
||||
);
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
const char* funcName = (const char *)(imageBase + (*origThunk & 0xFFFFFFFF) + 2);
|
||||
void* addr = LdrGetExport(dllName, funcName);
|
||||
|
||||
const char* funcName =
|
||||
(const char *)(imageBase +
|
||||
(*origThunk & 0xFFFFFFFF) + 2);
|
||||
|
||||
void* addr = lookup(ctx, dllName, funcName);
|
||||
|
||||
if (!addr) {
|
||||
DbgPrint("LdrResolveImports: Function not found! (%s)\n", funcName);
|
||||
DbgPrint(
|
||||
"LdrResolveImports: Function not found! (%s!%s)\n",
|
||||
dllName,
|
||||
funcName
|
||||
);
|
||||
return STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
*thunk = (uint64_t)addr;
|
||||
|
||||
thunk++;
|
||||
origThunk++;
|
||||
}
|
||||
|
||||
imp++;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS LdrResolveImports(uintptr_t imageBase) {
|
||||
return LdrResolveImportsCommon(
|
||||
(PROCESS*)0,
|
||||
imageBase,
|
||||
GlobalExportLookup,
|
||||
(void*)0
|
||||
);
|
||||
}
|
||||
|
||||
KSTATUS LdrResolveImportsForProcess(PROCESS* process, uintptr_t imageBase) {
|
||||
return LdrResolveImportsCommon(
|
||||
process,
|
||||
imageBase,
|
||||
ProcessExportLookup,
|
||||
process
|
||||
);
|
||||
}
|
||||
|
||||
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) {
|
||||
if (!process || !name || !base) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
for (uint32_t i = 0; i < process->ModuleCount; i++) {
|
||||
if (_stricmp(process->Modules[i].Name, name) == 0) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (process->ModuleCount >= PROCESS_MODULE_MAX) return STATUS_NO_MEMORY;
|
||||
|
||||
PROCESS_MODULE* mod = &process->Modules[process->ModuleCount++];
|
||||
strncpy(mod->Name, name, 31);
|
||||
mod->Name[31] = '\0';
|
||||
mod->Base = base;
|
||||
mod->Size = LdrGetImageSize(base);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) {
|
||||
if (!process || !filename) return STATUS_INVALID_PARAMETER;
|
||||
|
||||
for (uint32_t i = 0; i < process->ModuleCount; i++) {
|
||||
if (_stricmp(process->Modules[i].Name, filename) == 0) {
|
||||
if (baseOut) *baseOut = process->Modules[i].Base;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
FILE_OBJECT* f;
|
||||
KSTATUS s = IoOpenFile(filename, &f);
|
||||
if (!KSUCCESS(s)) {
|
||||
char path[260];
|
||||
memcpy(path, "0:/system/", 10);
|
||||
memcpy(path + 10, filename, strnlen(filename, 248) + 1);
|
||||
path[259] = '\0';
|
||||
s = IoOpenFile(path, &f);
|
||||
if (!KSUCCESS(s)) {
|
||||
DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t fileSize = IoGetFileSize(f);
|
||||
void* mem = MmAllocatePool((size_t)fileSize);
|
||||
if (!mem) {
|
||||
IoCloseFile(f);
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
size_t got = 0;
|
||||
s = IoReadFile(f, mem, (size_t)fileSize, &got);
|
||||
IoCloseFile(f);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreePool(mem);
|
||||
return s;
|
||||
}
|
||||
|
||||
uintptr_t imageBase = 0;
|
||||
s = LdrLoadImage(mem, (size_t)fileSize, &imageBase, true);
|
||||
MmFreePool(mem);
|
||||
if (!KSUCCESS(s)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s = LdrResolveImportsForProcess(process, imageBase);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
return s;
|
||||
}
|
||||
|
||||
s = LdrRegisterProcessModule(process, filename, imageBase);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreeUserMemory((void*)imageBase, LdrGetImageSize(imageBase));
|
||||
return s;
|
||||
}
|
||||
|
||||
if (baseOut) *baseOut = imageBase;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -271,7 +493,6 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
|
||||
if (!mem) return STATUS_NO_MEMORY;
|
||||
memset(mem, 0, imageSize);
|
||||
|
||||
// Copy headers and sections
|
||||
memcpy(mem, buffer, opt->SizeOfHeaders);
|
||||
SECTION_HEADER* sec = (SECTION_HEADER *)((uint8_t *)&pe->OptionalHeader + pe->SizeOfOptionalHeader);
|
||||
for (int i = 0; i < pe->NumberOfSections; i++) {
|
||||
@@ -280,21 +501,16 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
|
||||
if (sec[i].SizeOfRawData) {
|
||||
memcpy(dst, src, sec[i].SizeOfRawData < sec[i].VirtualSize ? sec[i].SizeOfRawData : sec[i].VirtualSize);
|
||||
}
|
||||
|
||||
|
||||
uint64_t prot = PAGE_PRESENT | PAGE_WRITE | (user ? PAGE_USER : 0);
|
||||
MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress, sec[i].VirtualSize, prot);
|
||||
}
|
||||
|
||||
// Apply Relocations
|
||||
uintptr_t delta = (uintptr_t)mem - opt->ImageBase;
|
||||
if (delta != 0) {
|
||||
LdrApplyRelocations((uintptr_t)mem, delta);
|
||||
}
|
||||
|
||||
// Resolve Imports
|
||||
KSTATUS status = LdrResolveImports((uintptr_t)mem);
|
||||
if (status != STATUS_SUCCESS) return status;
|
||||
|
||||
*baseOut = (uintptr_t)mem;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
+12
-1
@@ -10,6 +10,9 @@
|
||||
// On success, *baseOut receives the virtual base address.
|
||||
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user);
|
||||
|
||||
// Return the SizeOfImage recorded in the PE header for an already-mapped image.
|
||||
size_t LdrGetImageSize(uintptr_t base);
|
||||
|
||||
// Look up a named export in a loaded module.
|
||||
// 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll").
|
||||
void* LdrGetExport(const char* moduleName, const char* functionName);
|
||||
@@ -17,9 +20,17 @@ void* LdrGetExport(const char* moduleName, const char* functionName);
|
||||
// Get the entry point address of a loaded image.
|
||||
uintptr_t LdrGetEntryPoint(uintptr_t base);
|
||||
|
||||
// Resolve the import table of an already-loaded image.
|
||||
// Resolve the import table of an already-loaded image using the global registry.
|
||||
KSTATUS LdrResolveImports(uintptr_t imageBase);
|
||||
|
||||
// Resolve imports using a process-local module registry.
|
||||
struct PROCESS;
|
||||
KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase);
|
||||
|
||||
// Load a DLL for a specific process, resolve it against that process's
|
||||
// loaded modules, and register it in the process-local registry.
|
||||
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
|
||||
|
||||
// Called by KernelMain to register all modules from BOOT_INFO so that
|
||||
// LdrGetExport can find them.
|
||||
struct BOOT_INFO; // forward declare for the compiler to shut the fuck up
|
||||
|
||||
@@ -19,7 +19,7 @@ typedef enum OBJECT_TYPE {
|
||||
|
||||
typedef struct OBJECT {
|
||||
OBJECT_TYPE Type;
|
||||
volatile long RefCount; // interlocked; long = 4 bytes on MSVC x64
|
||||
volatile long RefCount; // interlocked
|
||||
void (*Destroy)(struct OBJECT* self);
|
||||
} OBJECT;
|
||||
|
||||
|
||||
Reference in new issue
Block a user