[NTOS:KE/x64] Handle user faults in KiGeneralProtectionFaultHandler

This commit is contained in:
Timo Kreuzer 2021-06-26 18:49:47 +02:00
parent f659ac5201
commit 45f75d5d32
3 changed files with 198 additions and 15 deletions

View file

@ -425,6 +425,184 @@ KiNpxNotAvailableFaultHandler(
return -1;
}
static
BOOLEAN
KiIsPrivilegedInstruction(PUCHAR Ip, BOOLEAN Wow64)
{
ULONG i;
/* Handle prefixes */
for (i = 0; i < 15; i++)
{
if (!Wow64)
{
/* Check for REX prefix */
if ((Ip[0] >= 0x40) && (Ip[0] <= 0x4F))
{
Ip++;
continue;
}
}
switch (Ip[0])
{
/* Check prefixes */
case 0x26: // ES
case 0x2E: // CS / null
case 0x36: // SS
case 0x3E: // DS
case 0x64: // FS
case 0x65: // GS
case 0x66: // OP
case 0x67: // ADDR
case 0xF0: // LOCK
case 0xF2: // REP
case 0xF3: // REP INS/OUTS
Ip++;
continue;
}
break;
}
if (i == 15)
{
/* Too many prefixes. Should only happen, when the code was concurrently modified. */
return FALSE;
}
switch (Ip[0])
{
case 0xF4: // HLT
case 0xFA: // CLI
case 0xFB: // STI
return TRUE;
case 0x0F:
{
switch (Ip[1])
{
case 0x06: // CLTS
case 0x07: // SYSRET
case 0x08: // INVD
case 0x09: // WBINVD
case 0x20: // MOV CR, XXX
case 0x21: // MOV DR, XXX
case 0x22: // MOV XXX, CR
case 0x23: // MOV YYY, DR
case 0x30: // WRMSR
case 0x32: // RDMSR
case 0x33: // RDPMC
case 0x35: // SYSEXIT
case 0x78: // VMREAD
case 0x79: // VMWRITE
return TRUE;
case 0x00:
{
/* Check MODRM Reg field */
switch ((Ip[2] >> 3) & 0x7)
{
case 2: // LLDT
case 3: // LTR
return TRUE;
}
break;
}
case 0x01:
{
switch (Ip[2])
{
case 0xC1: // VMCALL
case 0xC2: // VMLAUNCH
case 0xC3: // VMRESUME
case 0xC4: // VMXOFF
case 0xC8: // MONITOR
case 0xC9: // MWAIT
case 0xD1: // XSETBV
case 0xF8: // SWAPGS
return TRUE;
}
/* Check MODRM Reg field */
switch ((Ip[2] >> 3) & 0x7)
{
case 2: // LGDT
case 3: // LIDT
case 6: // LMSW
case 7: // INVLPG / SWAPGS / RDTSCP
return TRUE;
}
break;
}
case 0x38:
{
switch (Ip[2])
{
case 0x80: // INVEPT
case 0x81: // INVVPID
return TRUE;
}
break;
}
case 0xC7:
{
/* Check MODRM Reg field */
switch ((Ip[2] >> 3) & 0x7)
{
case 0x06: // VMPTRLD, VMCLEAR, VMXON
case 0x07: // VMPTRST
return TRUE;
}
break;
}
}
break;
}
}
return FALSE;
}
static
NTSTATUS
KiGeneralProtectionFaultUserMode(
_In_ PKTRAP_FRAME TrapFrame)
{
BOOLEAN Wow64 = TrapFrame->SegCs == KGDT64_R3_CMCODE;
PUCHAR InstructionPointer;
NTSTATUS Status;
/* We need to decode the instruction at RIP */
InstructionPointer = (PUCHAR)TrapFrame->Rip;
_SEH2_TRY
{
/* Probe the instruction address */
ProbeForRead(InstructionPointer, 64, 1);
/* Check if it's a privileged instruction */
if (KiIsPrivilegedInstruction(InstructionPointer, Wow64))
{
Status = STATUS_PRIVILEGED_INSTRUCTION;
}
else
{
Status = STATUS_ACCESS_VIOLATION;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
return Status;
}
NTSTATUS
NTAPI
@ -436,8 +614,7 @@ KiGeneralProtectionFaultHandler(
/* Check for user-mode GPF */
if (TrapFrame->SegCs & 3)
{
UNIMPLEMENTED;
ASSERT(FALSE);
return KiGeneralProtectionFaultUserMode(TrapFrame);
}
/* Check for lazy segment load */
@ -454,15 +631,6 @@ KiGeneralProtectionFaultHandler(
return STATUS_SUCCESS;
}
/* Check for nested exception */
if ((TrapFrame->Rip >= (ULONG64)KiGeneralProtectionFaultHandler) &&
(TrapFrame->Rip < (ULONG64)KiGeneralProtectionFaultHandler))
{
/* Not implemented */
UNIMPLEMENTED;
ASSERT(FALSE);
}
/* Get Instruction Pointer */
Instructions = (PUCHAR)TrapFrame->Rip;

View file

@ -285,7 +285,7 @@ KiInvalidOpcodeKernel:
/* Kernel mode fault */
/* Dispatch the exception */
DispatchException STATUS_ILLEGAL_INSTRUCTION, 3, 0, 0, 0
DispatchException STATUS_ILLEGAL_INSTRUCTION, 0, 0, 0, 0
/* Return */
ExitTrap TF_SAVE_ALL
@ -385,8 +385,18 @@ FUNC KiGeneralProtectionFault
test eax, eax
jge KiGpfExit
/* Dispatch the exception */
DispatchException eax, 3, 0, 0, 0
/* Check for access violation */
cmp eax, STATUS_ACCESS_VIOLATION
je DispatchAccessViolation
/* Dispatch privileged instruction fault */
DispatchException eax, 0, 0, 0, 0
jmp KiGpfFatal
DispatchAccessViolation:
/* Dispatch access violation */
DispatchException eax, 2, 0, -1, 0
KiGpfFatal:

View file

@ -701,9 +701,15 @@ RtlpUnwindInternal(
Note: this can happen after the first frame as the result of an exception */
UnwindContext.Rip = *(DWORD64*)UnwindContext.Rsp;
UnwindContext.Rsp += sizeof(DWORD64);
/* Copy the context back for the next iteration */
*ContextRecord = UnwindContext;
continue;
}
/* Save Rip before the virtual unwind */
DispatcherContext.ControlPc = UnwindContext.Rip;
/* Do a virtual unwind to get the next frame */
ExceptionRoutine = RtlVirtualUnwind(HandlerType,
ImageBase,
@ -749,7 +755,6 @@ RtlpUnwindInternal(
sizeof(DispatcherContext));
/* Set up the variable fields of the dispatcher context */
DispatcherContext.ControlPc = ContextRecord->Rip;
DispatcherContext.ImageBase = ImageBase;
DispatcherContext.FunctionEntry = FunctionEntry;
DispatcherContext.LanguageHandler = ExceptionRoutine;