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