revolutionary crash dump, msvcrt.dll redirect, text editor, some win32 functions

This commit is contained in:
maksw committed 2026-05-24 09:49:01 +02:00
1 parent 923e8792b4
commit 603fbdfab1
9 files changed
+1432 -160

No files matched your search

+297 -41
View File
@@ -3,10 +3,12 @@
#include <stdint.h>
#include <string.h>
#include <hal_api.h>
#include <ke_debug.h>
#include <ke_spinlock.h>
#include <ke_process.h>
#include <ke_thread.h>
#include <ke_sched.h>
#include <mm_virt.h>
#include <printf.h>
int _fltused; // fuckass
@@ -84,40 +86,259 @@ static const char* KiFaultVectorName(int vector) {
}
}
// Walk the process module list to find which image owns rip.
// Returns the module name string, or "unknown" if rip is outside all regions.
static const char* KiResolveModule(PROCESS* proc, uintptr_t rip) {
// Check the main executable image first.
if (proc->ImageBase && proc->ImageSize &&
rip >= proc->ImageBase && rip < proc->ImageBase + proc->ImageSize)
return proc->Name;
// Walk the process module list to find which image owns an address.
// Returns the module name string, or "unknown" if the address is outside all
// known image ranges. Base/size are optional and are useful for offset dumps.
static const char* KiResolveModuleEx(PROCESS* proc, uintptr_t addr,
uintptr_t* baseOut, size_t* sizeOut) {
if (baseOut) *baseOut = 0;
if (sizeOut) *sizeOut = 0;
if (!proc) return "unknown";
if (proc->ImageBase && proc->ImageSize &&
addr >= proc->ImageBase && addr < proc->ImageBase + proc->ImageSize) {
if (baseOut) *baseOut = proc->ImageBase;
if (sizeOut) *sizeOut = proc->ImageSize;
return proc->Name;
}
// Walk the per-process module table (DLLs / shared libraries).
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
uintptr_t base = proc->Modules[i].Base;
size_t size = proc->Modules[i].Size;
if (base && size && rip >= base && rip < base + size)
if (base && size && addr >= base && addr < base + size) {
if (baseOut) *baseOut = base;
if (sizeOut) *sizeOut = size;
return proc->Modules[i].Name;
}
}
return "unknown";
}
static const char* KiResolveModule(PROCESS* proc, uintptr_t addr) {
return KiResolveModuleEx(proc, addr, (uintptr_t*)0, (size_t*)0);
}
static void KiPrintAddress(PROCESS* proc, const char* label, uintptr_t addr) {
uintptr_t base = 0;
size_t size = 0;
const char* mod = KiResolveModuleEx(proc, addr, &base, &size);
if (base) {
printf("%s%016llx <%s+0x%llx>\n",
label,
(unsigned long long)addr,
mod,
(unsigned long long)(addr - base));
} else {
printf("%s%016llx <unknown>\n",
label,
(unsigned long long)addr);
}
}
static int KiUserReadU64(uintptr_t addr, uint64_t* out) {
if (!out) return 0;
if (!addr) return 0;
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint64_t))))
return 0;
*out = *(const uint64_t*)addr;
return 1;
}
static int KiUserReadU8(uintptr_t addr, uint8_t* out) {
if (!out) return 0;
if (!addr) return 0;
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(uint8_t))))
return 0;
*out = *(const uint8_t*)addr;
return 1;
}
static void KiDumpBytes(PROCESS* proc, const char* label, uintptr_t center) {
if (!center) {
printf("%s: <null>\n", label);
return;
}
printf("%s around ", label);
KiPrintAddress(proc, "", center);
uintptr_t start = center >= 16 ? center - 16 : 0;
for (int row = 0; row < 2; row++) {
uintptr_t addr = start + (uintptr_t)row * 16u;
printf(" %016llx:", (unsigned long long)addr);
for (int i = 0; i < 16; i++) {
uint8_t b = 0;
if (KiUserReadU8(addr + (uintptr_t)i, &b))
printf(" %02x", b);
else
printf(" ??");
}
printf("\n");
}
}
static int KiUserReadI32(uintptr_t addr, int32_t* out) {
if (!out) return 0;
if (!addr) return 0;
if (!KSUCCESS(MmProbeUserRead((const void*)addr, sizeof(int32_t))))
return 0;
*out = *(const int32_t*)addr;
return 1;
}
static void KiDumpIndirectCallSlot(PROCESS* proc, uintptr_t ret) {
if (!ret) return;
// Most x64 import/function-pointer calls are one of:
// FF 15 disp32 call qword ptr [rip+disp32]
// 48 FF 15 disp32 rex.w call qword ptr [rip+disp32]
// The saved return address points immediately after the instruction.
uintptr_t insn = 0;
int32_t disp = 0;
uint8_t b0 = 0, b1 = 0, b2 = 0;
if (KiUserReadU8(ret - 6, &b0) &&
KiUserReadU8(ret - 5, &b1) &&
b0 == 0xff && b1 == 0x15 &&
KiUserReadI32(ret - 4, &disp)) {
insn = ret - 6;
} else if (KiUserReadU8(ret - 7, &b0) &&
KiUserReadU8(ret - 6, &b1) &&
KiUserReadU8(ret - 5, &b2) &&
b0 == 0x48 && b1 == 0xff && b2 == 0x15 &&
KiUserReadI32(ret - 4, &disp)) {
insn = ret - 7;
} else {
printf("No RIP-relative indirect call pattern found before return address.\n");
return;
}
uintptr_t slot = ret + (intptr_t)disp;
uint64_t target = 0;
printf("Decoded null indirect call:\n");
KiPrintAddress(proc, " insn=", insn);
KiPrintAddress(proc, " slot=", slot);
if (KiUserReadU64(slot, &target)) {
KiPrintAddress(proc, " *slot=", (uintptr_t)target);
} else {
printf(" *slot=<unreadable>\n");
}
KiDumpBytes(proc, "Bytes near indirect slot", slot);
}
static void KiDumpModules(PROCESS* proc) {
if (!proc) return;
printf("Images:\n");
if (proc->ImageBase && proc->ImageSize) {
printf(" %-31s %016llx-%016llx size=%llx\n",
proc->Name,
(unsigned long long)proc->ImageBase,
(unsigned long long)(proc->ImageBase + proc->ImageSize),
(unsigned long long)proc->ImageSize);
}
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
uintptr_t base = proc->Modules[i].Base;
size_t size = proc->Modules[i].Size;
printf(" %-31s %016llx-%016llx size=%llx\n",
proc->Modules[i].Name,
(unsigned long long)base,
(unsigned long long)(base + size),
(unsigned long long)size);
}
}
static void KiDumpUserStack(PROCESS* proc, uintptr_t rsp) {
printf("User stack @ %016llx:\n", (unsigned long long)rsp);
if (!rsp) {
printf(" <no user RSP captured>\n");
return;
}
for (int i = 0; i < 12; i++) {
uintptr_t slot = rsp + (uintptr_t)i * 8u;
uint64_t value = 0;
if (KiUserReadU64(slot, &value)) {
uintptr_t base = 0;
size_t size = 0;
const char* mod = KiResolveModuleEx(proc, (uintptr_t)value, &base, &size);
if (base) {
printf(" [%02d] %016llx : %016llx <%s+0x%llx>\n",
i,
(unsigned long long)slot,
(unsigned long long)value,
mod,
(unsigned long long)((uintptr_t)value - base));
} else {
printf(" [%02d] %016llx : %016llx\n",
i,
(unsigned long long)slot,
(unsigned long long)value);
}
} else {
printf(" [%02d] %016llx : <unreadable>\n",
i,
(unsigned long long)slot);
}
}
}
static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) {
if (!f) return;
printf("Regs:\n");
printf(" RAX=%016llx RBX=%016llx RCX=%016llx RDX=%016llx\n",
(unsigned long long)f->Rax, (unsigned long long)f->Rbx,
(unsigned long long)f->Rcx, (unsigned long long)f->Rdx);
printf(" RSI=%016llx RDI=%016llx RBP=%016llx RSP=%016llx\n",
(unsigned long long)f->Rsi, (unsigned long long)f->Rdi,
(unsigned long long)f->Rbp, (unsigned long long)f->Rsp);
printf(" R8 =%016llx R9 =%016llx R10=%016llx R11=%016llx\n",
(unsigned long long)f->R8, (unsigned long long)f->R9,
(unsigned long long)f->R10, (unsigned long long)f->R11);
printf(" R12=%016llx R13=%016llx R14=%016llx R15=%016llx\n",
(unsigned long long)f->R12, (unsigned long long)f->R13,
(unsigned long long)f->R14, (unsigned long long)f->R15);
printf(" CS=%04llx SS=%04llx RFLAGS=%016llx\n",
(unsigned long long)f->Cs,
(unsigned long long)f->Ss,
(unsigned long long)f->Rflags);
if (f->Rip == 0) {
uint64_t ret = 0;
if (KiUserReadU64((uintptr_t)f->Rsp, &ret)) {
printf("Likely null indirect call. Return address from [RSP]:\n");
KiPrintAddress(proc, " ret=", (uintptr_t)ret);
KiDumpBytes(proc, "Bytes near return address", (uintptr_t)ret);
KiDumpIndirectCallSlot(proc, (uintptr_t)ret);
} else {
printf("Likely null indirect call, but [RSP] is unreadable.\n");
}
} else {
KiDumpBytes(proc, "Bytes near RIP", (uintptr_t)f->Rip);
}
KiDumpUserStack(proc, (uintptr_t)f->Rsp);
}
static void KiPrintCrashChar(char c) {
PrintChar(c, 0xFFAA00, 0x000000); // amber - distinct from green debug / red panic
}
_Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2) {
THREAD* thread = KeGetCurrentThread();
PROCESS* proc = thread ? thread->Owner : (PROCESS*)0;
// Snapshot everything we need before touching process state.
static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip,
uintptr_t errCode, uintptr_t cr2) {
const char* faultName = KiFaultVectorName(vector);
const char* procName = proc ? proc->Name : "unknown";
uint32_t pid = proc ? proc->ProcessId : 0;
const char* modName = proc ? KiResolveModule(proc, rip) : "unknown";
// Amber crash banner.
static const char banner[] = "\n*** APPLICATION CRASH ***\n";
for (const char* p = banner; *p; p++)
KiPrintCrashChar(*p);
@@ -139,20 +360,67 @@ _Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, u
modName,
(unsigned long long)rip,
(unsigned long long)errCode);
}
// Restore normal (green) output for subsequent kernel messages.
static _Noreturn void KiFinishFaultedProcess(PROCESS* proc, THREAD* thread) {
printf_set_putchar(KiKernelPutChar);
// Kill the process and exit this thread.
// KeExitThread sets ThreadDead then calls KeYield; the scheduler will
// never return here, so the process crash cannot affect anything else.
if (proc) KeExitProcess(proc, -1);
if (thread) KeExitThread(thread, -1);
// Unreachable - KeExitThread never returns for a dead thread.
while (1) __halt();
}
_Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, uintptr_t cr2) {
THREAD* thread = KeGetCurrentThread();
PROCESS* proc = thread ? thread->Owner : (PROCESS*)0;
KiPrintCrashHeader(proc, vector, rip, errCode, cr2);
KiDumpModules(proc);
KiFinishFaultedProcess(proc, thread);
}
__declspec(dllexport) _Noreturn void KeFaultDispatchFrame(const KFAULT_FRAME* frame) {
THREAD* thread = KeGetCurrentThread();
PROCESS* proc = thread ? thread->Owner : (PROCESS*)0;
if (!frame || frame->Magic != 0x3146544C5541464BULL) {
KePanic("PANIC: bad KFAULT_FRAME %016llx\n",
(unsigned long long)(uintptr_t)frame);
}
if ((frame->Cs & 3) == 0) {
switch ((int)frame->Vector) {
case 0:
KePanic("PANIC: #DE Divide Error | RIP: %016llx\n",
(unsigned long long)frame->Rip);
case 13:
KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n",
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode);
case 14:
KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n",
(unsigned long long)frame->Rip,
(unsigned long long)frame->ErrorCode,
(unsigned long long)frame->Cr2);
default:
KePanic("PANIC: Exception #%llu | RIP: %016llx\n",
(unsigned long long)frame->Vector,
(unsigned long long)frame->Rip);
}
}
KiPrintCrashHeader(proc, (int)frame->Vector,
(uintptr_t)frame->Rip,
(uintptr_t)frame->ErrorCode,
(uintptr_t)frame->Cr2);
KiDumpFaultFrame(proc, frame);
KiDumpModules(proc);
KiFinishFaultedProcess(proc, thread);
}
// ---------------------------------------------------------------------------
// KeFaultDispatch - single C entry point for all three IDT fault stubs.
//
@@ -163,25 +431,13 @@ _Noreturn void KeBugCheckProcess(int vector, uintptr_t rip, uintptr_t errCode, u
__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
uintptr_t errCode, uintptr_t cr2,
uintptr_t cs) {
if ((cs & 3) == 0) {
// Kernel-mode fault: unrecoverable.
switch (vector) {
case 0:
KePanic("PANIC: #DE Divide Error | RIP: %016llx\n",
(unsigned long long)rip);
case 13:
KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n",
(unsigned long long)rip, (unsigned long long)errCode);
case 14:
KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n",
(unsigned long long)rip, (unsigned long long)errCode,
(unsigned long long)cr2);
default:
KePanic("PANIC: Exception #%d | RIP: %016llx\n",
vector, (unsigned long long)rip);
}
}
// User-mode fault: recoverable - kill the process, resume the scheduler.
KeBugCheckProcess(vector, rip, errCode, cr2);
KFAULT_FRAME frame;
memset(&frame, 0, sizeof(frame));
frame.Magic = 0x3146544C5541464BULL;
frame.Vector = (uint64_t)vector;
frame.ErrorCode = errCode;
frame.Cr2 = cr2;
frame.Rip = rip;
frame.Cs = cs;
KeFaultDispatchFrame(&frame);
}
+56 -27
View File
@@ -1,27 +1,56 @@
#ifndef KE_DEBUG_H
#define KE_DEBUG_H
#include <stdint.h>
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
__declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...);
// ---------------------------------------------------------------------------
// Fault dispatch - called from the three IDT fault stubs (ke_syscall.asm).
//
// vector : CPU exception number (0=#DE, 13=#GP, 14=#PF)
// rip : faulting instruction pointer (from the CPU exception frame)
// errCode : CPU error code (0 for #DE)
// cr2 : value of CR2 at fault time (meaningful only for #PF)
// cs : CS from the exception frame; RPL bits determine ring
//
// Kernel-mode faults (cs & 3 == 0) call KePanic and halt.
// User-mode faults (cs & 3 == 3) kill the offending process and yield.
// ---------------------------------------------------------------------------
__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
uintptr_t errCode, uintptr_t cr2,
uintptr_t cs);
#endif // KE_DEBUG_H
#ifndef KE_DEBUG_H
#define KE_DEBUG_H
#include <stdint.h>
#include <stddef.h>
void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v);
__declspec(dllexport) void DbgPrint(const char* fmt, ...);
__declspec(dllexport) void KePrintConsole(const char* buf, size_t len);
__declspec(dllexport) _Noreturn void KePanic(const char* fmt, ...);
// ---------------------------------------------------------------------------
// Fault dispatch - called from the IDT fault stubs (ke_syscall.asm).
//
// The old scalar entry only reported RIP/error/CR2, which is not enough for
// debugging null indirect calls. The frame entry snapshots user GPRs plus the
// CPU exception frame, including user RSP. For a CALL through a null function
// pointer, RIP is 0 and [RSP] is usually the return address after the bad call;
// that is the useful caller site.
// ---------------------------------------------------------------------------
typedef struct KFAULT_FRAME {
uint64_t Magic;
uint64_t Vector;
uint64_t ErrorCode;
uint64_t Cr2;
uint64_t Rip;
uint64_t Cs;
uint64_t Rflags;
uint64_t Rsp;
uint64_t Ss;
uint64_t Rax;
uint64_t Rcx;
uint64_t Rdx;
uint64_t Rbx;
uint64_t Rbp;
uint64_t Rsi;
uint64_t Rdi;
uint64_t R8;
uint64_t R9;
uint64_t R10;
uint64_t R11;
uint64_t R12;
uint64_t R13;
uint64_t R14;
uint64_t R15;
} KFAULT_FRAME;
__declspec(dllexport) _Noreturn void KeFaultDispatchFrame(const KFAULT_FRAME* frame);
// Compatibility wrapper for any older assembly/object still calling this.
__declspec(dllexport) _Noreturn void KeFaultDispatch(int vector, uintptr_t rip,
uintptr_t errCode, uintptr_t cr2,
uintptr_t cs);
#endif // KE_DEBUG_H
+206 -44
View File
@@ -7,6 +7,7 @@
EXTERN KeSyscallDispatch:PROC
EXTERN KiEnterTrapFrame:PROC
EXTERN KiExitTrapFrame:PROC
EXTERN KeFaultDispatchFrame:PROC
KTRAP_FRAME_SIZE EQU 240
KTRAP_FRAME_MAGIC EQU 4650545241544B31h
@@ -143,76 +144,237 @@ KeSyscallEntry PROC
KeSyscallEntry ENDP
; ---------------------------------------------------------------------------
; Fault stubs - thin wrappers that collect the CPU exception frame and
; forward to KeFaultDispatch (C), which handles both kernel panics and
; user-process bugchecks.
; Fault stubs - snapshot a full user fault frame before entering C.
;
; KeFaultDispatch signature (MS x64):
; void KeFaultDispatch(int vector, // rcx
; UINT64 rip, // rdx
; UINT64 errCode, // r8
; UINT64 cr2, // r9
; UINT64 cs) // [rsp+20h] (5th arg, on stack)
; For null indirect calls/jumps the CPU reports RIP=0, which is almost useless
; by itself. The captured user RSP lets the C crash dumper read [RSP], which is
; usually the return address immediately after the bad CALL. The GPR snapshot
; also shows which register held the null target.
;
; KeFaultDispatchFrame signature (MS x64):
; void KeFaultDispatchFrame(const KFAULT_FRAME* frame) ; rcx
; ---------------------------------------------------------------------------
EXTERN KeFaultDispatch:PROC
; Allocation for a 5-argument call (MS x64):
; 32 bytes shadow space (0x00..0x1F)
; 8 bytes for 5th arg (0x20..0x27)
; Total = 0x28 bytes.
; After "and rsp,-16" rsp%16==0; sub 0x28 (40) → rsp%16==8,
; so the subsequent "call" push makes rsp 16-byte aligned at callee entry. ✓
FAULT_FRAME_SIZE EQU 28h
KFAULT_MAGIC EQU 3146544C5541464Bh ; "KFAULTF1" little-ish
KFAULT_SIZE EQU 0C0h
KF_Magic EQU 000h
KF_Vector EQU 008h
KF_ErrorCode EQU 010h
KF_Cr2 EQU 018h
KF_Rip EQU 020h
KF_Cs EQU 028h
KF_Rflags EQU 030h
KF_Rsp EQU 038h
KF_Ss EQU 040h
KF_Rax EQU 048h
KF_Rcx EQU 050h
KF_Rdx EQU 058h
KF_Rbx EQU 060h
KF_Rbp EQU 068h
KF_Rsi EQU 070h
KF_Rdi EQU 078h
KF_R8 EQU 080h
KF_R9 EQU 088h
KF_R10 EQU 090h
KF_R11 EQU 098h
KF_R12 EQU 0A0h
KF_R13 EQU 0A8h
KF_R14 EQU 0B0h
KF_R15 EQU 0B8h
FAULT_CALL_SHADOW EQU 28h
; ---------------------------------------------------------
; Divide Error (#0) - NO error code pushed by CPU
; Exception frame at entry: [rsp]=RIP [rsp+8]=CS [rsp+16]=RFLAGS
; User exception frame: [E]=RIP [E+8]=CS [E+16]=RFLAGS [E+24]=RSP [E+32]=SS
; ---------------------------------------------------------
KeFaultDE PROC
mov rdx, [rsp] ; Arg2: rip
xor r8, r8 ; Arg3: errCode = 0 (no error code for #DE)
xor r9, r9 ; Arg4: cr2 = 0 (not a page fault)
mov r10, [rsp+8] ; cs (save before we clobber rsp)
mov ecx, 0 ; Arg1: vector = 0
push rax ; [rsp+8] after next push = original RAX
push r11 ; [rsp] = original R11
mov r11, rsp ; saved pair; CPU frame starts at [r11+16]
sub rsp, KFAULT_SIZE
mov rax, rsp ; rax = KFAULT_FRAME*
mov [rax + KF_R10], r10
mov r10, KFAULT_MAGIC
mov [rax + KF_Magic], r10
mov qword ptr [rax + KF_Vector], 0
mov qword ptr [rax + KF_ErrorCode], 0
mov qword ptr [rax + KF_Cr2], 0
mov [rax + KF_Rcx], rcx
mov [rax + KF_Rdx], rdx
mov [rax + KF_Rbx], rbx
mov [rax + KF_Rbp], rbp
mov [rax + KF_Rsi], rsi
mov [rax + KF_Rdi], rdi
mov [rax + KF_R8], r8
mov [rax + KF_R9], r9
mov r10, [r11]
mov [rax + KF_R11], r10
mov r10, [r11+8]
mov [rax + KF_Rax], r10
mov [rax + KF_R12], r12
mov [rax + KF_R13], r13
mov [rax + KF_R14], r14
mov [rax + KF_R15], r15
lea rdx, [r11+16]
mov r10, [rdx]
mov [rax + KF_Rip], r10
mov r10, [rdx+8]
mov [rax + KF_Cs], r10
mov r10, [rdx+16]
mov [rax + KF_Rflags], r10
mov r10, [rax + KF_Cs]
test r10, 3
jz de_kernel_frame
mov r10, [rdx+24]
mov [rax + KF_Rsp], r10
mov r10, [rdx+32]
mov [rax + KF_Ss], r10
jmp de_call
de_kernel_frame:
mov qword ptr [rax + KF_Rsp], 0
mov qword ptr [rax + KF_Ss], 0
de_call:
and rsp, -16
sub rsp, FAULT_FRAME_SIZE
mov [rsp+20h], r10 ; Arg5: cs
call KeFaultDispatch ; _Noreturn - never returns
sub rsp, FAULT_CALL_SHADOW
mov rcx, rax
call KeFaultDispatchFrame
KeFaultDE ENDP
; ---------------------------------------------------------
; General Protection Fault (#13) - error code pushed by CPU
; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS
; User exception frame: [E]=ERR [E+8]=RIP [E+16]=CS [E+24]=RFLAGS [E+32]=RSP [E+40]=SS
; ---------------------------------------------------------
KeFaultGP PROC
mov r8, [rsp] ; Arg3: errCode
mov rdx, [rsp+8] ; Arg2: rip
xor r9, r9 ; Arg4: cr2 = 0 (not a page fault)
mov r10, [rsp+16] ; cs
mov ecx, 13 ; Arg1: vector = 13
push rax
push r11
mov r11, rsp
sub rsp, KFAULT_SIZE
mov rax, rsp
mov [rax + KF_R10], r10
mov r10, KFAULT_MAGIC
mov [rax + KF_Magic], r10
mov qword ptr [rax + KF_Vector], 13
mov qword ptr [rax + KF_Cr2], 0
mov [rax + KF_Rcx], rcx
mov [rax + KF_Rdx], rdx
mov [rax + KF_Rbx], rbx
mov [rax + KF_Rbp], rbp
mov [rax + KF_Rsi], rsi
mov [rax + KF_Rdi], rdi
mov [rax + KF_R8], r8
mov [rax + KF_R9], r9
mov r10, [r11]
mov [rax + KF_R11], r10
mov r10, [r11+8]
mov [rax + KF_Rax], r10
mov [rax + KF_R12], r12
mov [rax + KF_R13], r13
mov [rax + KF_R14], r14
mov [rax + KF_R15], r15
lea rdx, [r11+16]
mov r10, [rdx]
mov [rax + KF_ErrorCode], r10
mov r10, [rdx+8]
mov [rax + KF_Rip], r10
mov r10, [rdx+16]
mov [rax + KF_Cs], r10
mov r10, [rdx+24]
mov [rax + KF_Rflags], r10
mov r10, [rax + KF_Cs]
test r10, 3
jz gp_kernel_frame
mov r10, [rdx+32]
mov [rax + KF_Rsp], r10
mov r10, [rdx+40]
mov [rax + KF_Ss], r10
jmp gp_call
gp_kernel_frame:
mov qword ptr [rax + KF_Rsp], 0
mov qword ptr [rax + KF_Ss], 0
gp_call:
and rsp, -16
sub rsp, FAULT_FRAME_SIZE
mov [rsp+20h], r10 ; Arg5: cs
call KeFaultDispatch
sub rsp, FAULT_CALL_SHADOW
mov rcx, rax
call KeFaultDispatchFrame
KeFaultGP ENDP
; ---------------------------------------------------------
; Page Fault (#14) - error code pushed by CPU
; Exception frame at entry: [rsp]=ErrCode [rsp+8]=RIP [rsp+16]=CS
; User exception frame: [E]=ERR [E+8]=RIP [E+16]=CS [E+24]=RFLAGS [E+32]=RSP [E+40]=SS
; ---------------------------------------------------------
KeFaultPF PROC
mov r8, [rsp] ; Arg3: errCode
mov rdx, [rsp+8] ; Arg2: rip
mov r9, cr2 ; Arg4: cr2 (faulting virtual address)
mov r10, [rsp+16] ; cs
mov ecx, 14 ; Arg1: vector = 14
push rax
push r11
mov r11, rsp
sub rsp, KFAULT_SIZE
mov rax, rsp
mov [rax + KF_R10], r10
mov r10, KFAULT_MAGIC
mov [rax + KF_Magic], r10
mov qword ptr [rax + KF_Vector], 14
mov [rax + KF_Rcx], rcx
mov [rax + KF_Rdx], rdx
mov [rax + KF_Rbx], rbx
mov [rax + KF_Rbp], rbp
mov [rax + KF_Rsi], rsi
mov [rax + KF_Rdi], rdi
mov [rax + KF_R8], r8
mov [rax + KF_R9], r9
mov r10, cr2
mov [rax + KF_Cr2], r10
mov r10, [r11]
mov [rax + KF_R11], r10
mov r10, [r11+8]
mov [rax + KF_Rax], r10
mov [rax + KF_R12], r12
mov [rax + KF_R13], r13
mov [rax + KF_R14], r14
mov [rax + KF_R15], r15
lea rdx, [r11+16]
mov r10, [rdx]
mov [rax + KF_ErrorCode], r10
mov r10, [rdx+8]
mov [rax + KF_Rip], r10
mov r10, [rdx+16]
mov [rax + KF_Cs], r10
mov r10, [rdx+24]
mov [rax + KF_Rflags], r10
mov r10, [rax + KF_Cs]
test r10, 3
jz pf_kernel_frame
mov r10, [rdx+32]
mov [rax + KF_Rsp], r10
mov r10, [rdx+40]
mov [rax + KF_Ss], r10
jmp pf_call
pf_kernel_frame:
mov qword ptr [rax + KF_Rsp], 0
mov qword ptr [rax + KF_Ss], 0
pf_call:
and rsp, -16
sub rsp, FAULT_FRAME_SIZE
mov [rsp+20h], r10 ; Arg5: cs
call KeFaultDispatch
sub rsp, FAULT_CALL_SHADOW
mov rcx, rax
call KeFaultDispatchFrame
KeFaultPF ENDP
EXTERN KeIrqDispatch:PROC
+86 -14
View File
@@ -8,11 +8,16 @@
#include <mm_virt.h>
#include <ke_debug.h>
int _stricmp(const char* a, const char* b);
// ---------------------------------------------------------------------------
// PE32+ structures (just what we need)
// ---------------------------------------------------------------------------
#define PE_SIGNATURE 0x00004550UL // 'PE\0\0'
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#define IMAGE_FILE_MACHINE_I386 0x014c
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
#pragma pack(push, 1)
typedef struct {
@@ -117,6 +122,25 @@ typedef struct {
static MODULE_ENTRY gModules[MAX_MODULES];
static uint32_t gModuleCount = 0;
static const char* KiCanonicalModuleName(const char* name) {
if (!name) return name;
if (!_stricmp(name, "kernel32.dll") ||
!_stricmp(name, "user32.dll") ||
!_stricmp(name, "gdi32.dll") ||
!_stricmp(name, "ole32.dll") ||
!_stricmp(name, "winmm.dll")) {
return "win32.dll";
}
if (!_stricmp(name, "msvcrt.dll")) {
return "posix.dll";
}
return name;
}
static uintptr_t KiFindModuleBaseInTable(const MODULE_ENTRY* modules, uint32_t count, const char* name) {
for (uint32_t i = 0; i < count; i++) {
if (_stricmp(modules[i].Name, name) == 0) return modules[i].Base;
@@ -152,7 +176,7 @@ void LdrRegisterModule(const char* name, uintptr_t base) {
}
static uintptr_t KiFindGlobalModuleBase(const char* name) {
return KiFindModuleBaseInTable(gModules, gModuleCount, name);
return KiFindModuleBaseInTable(gModules, gModuleCount, KiCanonicalModuleName(name));
}
static uintptr_t KiFindProcessModuleBaseInTable(
@@ -161,7 +185,7 @@ static uintptr_t KiFindProcessModuleBaseInTable(
const char* name
) {
for (uint32_t i = 0; i < count; i++) {
if (_stricmp(modules[i].Name, name) == 0) {
if (_stricmp(modules[i].Name, KiCanonicalModuleName(name)) == 0) {
return modules[i].Base;
}
}
@@ -208,12 +232,12 @@ static void* KiGetExportAtBase(uintptr_t base, const char* functionName) {
// Export lookup
// ---------------------------------------------------------------------------
void* LdrGetExport(const char* moduleName, const char* functionName) {
uintptr_t base = KiFindGlobalModuleBase(moduleName);
uintptr_t base = KiFindGlobalModuleBase(KiCanonicalModuleName(moduleName));
return KiGetExportAtBase(base, functionName);
}
void* LdrGetExportForProcess(PROCESS* process, const char* moduleName, const char* functionName) {
uintptr_t base = KiFindProcessOrGlobalModuleBase(process, moduleName);
uintptr_t base = KiFindProcessOrGlobalModuleBase(process, KiCanonicalModuleName(moduleName));
return KiGetExportAtBase(base, functionName);
}
@@ -304,13 +328,8 @@ static KSTATUS LdrResolveImportsCommon(
while (imp->Name) {
const char* dllName = (const char *)(imageBase + imp->Name);
// Win32 compatibility remap
if (!_stricmp(dllName, "kernel32.dll") ||
!_stricmp(dllName, "user32.dll") ||
!_stricmp(dllName, "ole32.dll") ||
!_stricmp(dllName, "winmm.dll")) {
dllName = "win32.dll";
}
//Compatibility remap.
dllName = KiCanonicalModuleName(dllName);
//
// Ensure dependency is loaded into the process
@@ -397,6 +416,28 @@ KSTATUS LdrResolveImportsForProcess(PROCESS* process, uintptr_t imageBase) {
);
}
static void KiDebugLibtccAllocatorSlot(const char* phase, const char* filename, uintptr_t imageBase) {
if (!filename || !imageBase) return;
if (_stricmp(filename, "libtcc.dll") != 0) return;
uint64_t slot = *(uint64_t*)(imageBase + 0x4A010);
uint64_t expectedDefault = (uint64_t)(imageBase + 0x25F0);
DbgPrint("LDR libtcc %s: base=%p slot[+4A010]=%p expected-default=%p\n",
phase,
(void*)imageBase,
(void*)(uintptr_t)slot,
(void*)(uintptr_t)expectedDefault);
DbgPrint("LDR libtcc data[+4A000..+4A028]: %016llx %016llx %016llx %016llx %016llx %016llx\n",
(unsigned long long)*(uint64_t*)(imageBase + 0x4A000),
(unsigned long long)*(uint64_t*)(imageBase + 0x4A008),
(unsigned long long)*(uint64_t*)(imageBase + 0x4A010),
(unsigned long long)*(uint64_t*)(imageBase + 0x4A018),
(unsigned long long)*(uint64_t*)(imageBase + 0x4A020),
(unsigned long long)*(uint64_t*)(imageBase + 0x4A028));
}
static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uintptr_t base) {
if (!process || !name || !base) return STATUS_INVALID_PARAMETER;
@@ -419,6 +460,8 @@ static KSTATUS LdrRegisterProcessModule(PROCESS* process, const char* name, uint
KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_t* baseOut) {
if (!process || !filename) return STATUS_INVALID_PARAMETER;
filename = KiCanonicalModuleName(filename);
for (uint32_t i = 0; i < process->ModuleCount; i++) {
if (_stricmp(process->Modules[i].Name, filename) == 0) {
if (baseOut) *baseOut = process->Modules[i].Base;
@@ -462,12 +505,16 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
return s;
}
KiDebugLibtccAllocatorSlot("after load/reloc before imports", filename, imageBase);
s = LdrResolveImportsForProcess(process, imageBase);
if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase);
return s;
}
KiDebugLibtccAllocatorSlot("after imports", filename, imageBase);
s = LdrRegisterProcessModule(process, filename, imageBase);
if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase);
@@ -481,11 +528,36 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
// LdrLoadImage - load a PE from a raw buffer into virtual space.
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user) {
if (!buffer || !size || !baseOut) return STATUS_INVALID_PARAMETER;
if (size < 0x40) return STATUS_FAILURE;
uint32_t peOffset = *(uint32_t *)((uint8_t *)buffer + 0x3C);
PE_HEADER* pe = (PE_HEADER *)((uint8_t *)buffer + peOffset);
if (pe->Signature != PE_SIGNATURE) return STATUS_FAILURE;
const uint8_t* bytes = (const uint8_t*)buffer;
uint32_t peOffset = *(const uint32_t *)(bytes + 0x3C);
if ((size_t)peOffset > size || size - (size_t)peOffset < 24) return STATUS_FAILURE;
uint32_t signature = *(const uint32_t *)(bytes + peOffset);
if (signature != PE_SIGNATURE) return STATUS_FAILURE;
uint16_t machine = *(const uint16_t *)(bytes + peOffset + 4);
uint16_t optionalSize = *(const uint16_t *)(bytes + peOffset + 20);
if (optionalSize < sizeof(OPT_HEADER64) ||
(size_t)peOffset + 24 + optionalSize > size) {
return STATUS_FAILURE;
}
uint16_t optionalMagic = *(const uint16_t *)(bytes + peOffset + 24);
if (machine == IMAGE_FILE_MACHINE_I386 || optionalMagic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
DbgPrint("LdrLoadImage: hard error: 32-bit PE images are not supported (machine=0x%X magic=0x%X)\n",
machine, optionalMagic);
return STATUS_NOT_SUPPORTED;
}
if (machine != IMAGE_FILE_MACHINE_AMD64 || optionalMagic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
DbgPrint("LdrLoadImage: unsupported PE image (machine=0x%X magic=0x%X)\n",
machine, optionalMagic);
return STATUS_NOT_SUPPORTED;
}
PE_HEADER* pe = (PE_HEADER *)(bytes + peOffset);
OPT_HEADER64* opt = &pe->OptionalHeader;
size_t imageSize = opt->SizeOfImage;