Index: trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp
===================================================================
--- trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp	(revision 81106)
+++ trunk/src/VBox/HostDrivers/Support/win/SUPHardenedVerifyProcess-win.cpp	(revision 81118)
@@ -875,5 +875,5 @@
      */
     uint32_t         cSkipAreas = 0;
-    SUPHNTVPSKIPAREA aSkipAreas[6];
+    SUPHNTVPSKIPAREA aSkipAreas[7];
     if (pImage->fNtCreateSectionPatch)
     {
@@ -909,4 +909,13 @@
         aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue;
         aSkipAreas[cSkipAreas++].cb = 14;
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+        /* Ignore our patched KiUserExceptionDispatcher hack. */
+        rc = RTLdrGetSymbolEx(pImage->pCacheEntry->hLdrMod, pbBits, 0, UINT32_MAX, "KiUserExceptionDispatcher", &uValue);
+        if (RT_FAILURE(rc))
+            return supHardNtVpSetInfo2(pThis, rc, "%s: Failed to find 'KiUserExceptionDispatcher': %Rrc", pImage->pszName, rc);
+        aSkipAreas[cSkipAreas].uRva = (uint32_t)uValue + (HC_ARCH_BITS == 64);
+        aSkipAreas[cSkipAreas++].cb = HC_ARCH_BITS == 64 ? 13 : 12;
+#endif
 
         /* LdrSystemDllInitBlock is filled in by the kernel. It mainly contains addresses of 32-bit ntdll method for wow64. */
Index: trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp
===================================================================
--- trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp	(revision 81106)
+++ trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMain-win.cpp	(revision 81118)
@@ -306,12 +306,22 @@
 static uint8_t              g_abLdrLoadDllPatch[16];
 
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+/** Pointer to the bit of assembly code that will perform the original
+ *  KiUserExceptionDispatcher operation. */
+static VOID        (NTAPI *g_pfnKiUserExceptionDispatcherReal)(void);
+/** Pointer to the KiUserExceptionDispatcher function in NtDll (for patching purposes). */
+static uint8_t             *g_pbKiUserExceptionDispatcher;
+/** The patched KiUserExceptionDispatcher bytes (for restoring). */
+static uint8_t              g_abKiUserExceptionDispatcherPatch[16];
+#endif
+
 /** Pointer to the bit of assembly code that will perform the original
  *  KiUserApcDispatcher operation. */
 static VOID        (NTAPI *g_pfnKiUserApcDispatcherReal)(void);
-/** Pointer to the KiUserApcDispatcher function in NtDll (for patching
- *  purposes). */
+/** Pointer to the KiUserApcDispatcher function in NtDll (for patching purposes). */
 static uint8_t             *g_pbKiUserApcDispatcher;
 /** The patched KiUserApcDispatcher bytes (for restoring). */
 static uint8_t              g_abKiUserApcDispatcherPatch[16];
+
 /** Pointer to the LdrInitializeThunk function in NtDll for
  *  supR3HardenedMonitor_KiUserApcDispatcher_C() to use for APC vetting. */
@@ -408,4 +418,7 @@
 DECLASM(void)   supR3HardenedEarlyProcessInitThunk(void);
 DECLASM(void)   supR3HardenedMonitor_KiUserApcDispatcher(void);
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+DECLASM(void)   supR3HardenedMonitor_KiUserExceptionDispatcher(void);
+#endif
 extern "C" void __stdcall suplibHardenedWindowsMain(void);
 
@@ -2535,4 +2548,103 @@
 
 
+/**
+ * SUP_DPRINTF on pCtx, with lead-in text.
+ */
+static void supR3HardNtDprintCtx(PCONTEXT pCtx, const char *pszLeadIn)
+{
+#ifdef RT_ARCH_AMD64
+    SUP_DPRINTF(("%s\n"
+                 "  rax=%016RX64 rbx=%016RX64 rcx=%016RX64 rdx=%016RX64\n"
+                 "  rsi=%016RX64 rdi=%016RX64 r8 =%016RX64 r9 =%016RX64\n"
+                 "  r10=%016RX64 r11=%016RX64 r12=%016RX64 r13=%016RX64\n"
+                 "  r14=%016RX64 r15=%016RX64  P1=%016RX64  P2=%016RX64\n"
+                 "  rip=%016RX64 rsp=%016RX64 rbp=%016RX64    ctxflags=%08x\n"
+                 "  cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x    eflags=%08x   mxcrx=%08x\n"
+                 "   P3=%016RX64  P4=%016RX64  P5=%016RX64  P6=%016RX64\n"
+                 "  dr0=%016RX64 dr1=%016RX64 dr2=%016RX64 dr3=%016RX64\n"
+                 "  dr6=%016RX64 dr7=%016RX64 vcr=%016RX64 dcr=%016RX64\n"
+                 "  lbt=%016RX64 lbf=%016RX64 lxt=%016RX64 lxf=%016RX64\n"
+                 ,
+                 pszLeadIn,
+                 pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
+                 pCtx->Rsi, pCtx->Rdi, pCtx->R8, pCtx->R9,
+                 pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13,
+                 pCtx->R14, pCtx->R15, pCtx->P1Home, pCtx->P2Home,
+                 pCtx->Rip, pCtx->Rsp, pCtx->Rbp, pCtx->ContextFlags,
+                 pCtx->SegCs, pCtx->SegSs, pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs, pCtx->EFlags, pCtx->MxCsr,
+                 pCtx->P3Home, pCtx->P4Home, pCtx->P5Home, pCtx->P6Home,
+                 pCtx->Dr0, pCtx->Dr1, pCtx->Dr2, pCtx->Dr3,
+                 pCtx->Dr6, pCtx->Dr7, pCtx->VectorControl, pCtx->DebugControl,
+                 pCtx->LastBranchToRip, pCtx->LastBranchFromRip, pCtx->LastExceptionToRip, pCtx->LastExceptionFromRip ));
+#elif defined(RT_ARCH_X86)
+    SUP_DPRINTF(("%s\n"
+                 "  eax=%08RX32 ebx=%08RX32 ecx=%08RX32 edx=%08RX32 esi=%08rx64 edi=%08RX32\n"
+                 "  eip=%08RX32 esp=%08RX32 ebp=%08RX32 eflags=%08RX32\n"
+                 "  cs=%04RX16 ds=%04RX16 es=%04RX16 fs=%04RX16 gs=%04RX16\n"
+                 "  dr0=%08RX32 dr1=%08RX32 dr2=%08RX32 dr3=%08RX32 dr6=%08RX32 dr7=%08RX32\n",
+                 pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi,
+                 pCtx->Eip, pCtx->Esp, pCtx->Ebp, pCtx->EFlags,
+                 pCtx->SegCs, pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs,
+                 pCtx->Dr0, pCtx->Dr1, pCtx->Dr2, pCtx->Dr3, pCtx->Dr6, pCtx->Dr7));
+#else
+# error "Unsupported arch."
+#endif
+
+}
+
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+/**
+ * This is called when ntdll!KiUserExceptionDispatcher is invoked (via
+ * supR3HardenedMonitor_KiUserExceptionDispatcher).
+ *
+ * For 64-bit processes there is a return and two parameters on the stack.
+ *
+ * @returns Where to go to run the original code.
+ * @param   pXcptRec    The exception record.
+ * @param   pCtx        The exception context.
+ */
+DECLASM(uintptr_t) supR3HardenedMonitor_KiUserExceptionDispatcher_C(PEXCEPTION_RECORD pXcptRec, PCONTEXT pCtx)
+{
+    /*
+     * Ignore the guard page violation.
+     */
+    if (pXcptRec->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION)
+        return (uintptr_t)g_pfnKiUserExceptionDispatcherReal;
+
+    /*
+     * Log the exception and context.
+     */
+    char szLeadIn[384];
+    if (pXcptRec->NumberParameters == 0)
+        RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x @ %p (flags=%#x)",
+                    pXcptRec->ExceptionCode, pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+    else if (pXcptRec->NumberParameters == 1)
+        RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (%p) @ %p (flags=%#x)",
+                    pXcptRec->ExceptionCode, pXcptRec->ExceptionInformation[0],
+                    pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+    else if (pXcptRec->NumberParameters == 2)
+        RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (%p, %p) @ %p (flags=%#x)",
+                    pXcptRec->ExceptionCode, pXcptRec->ExceptionInformation[0], pXcptRec->ExceptionInformation[1],
+                    pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+    else if (pXcptRec->NumberParameters == 3)
+        RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (%p, %p, %p) @ %p (flags=%#x)",
+                    pXcptRec->ExceptionCode, pXcptRec->ExceptionInformation[0], pXcptRec->ExceptionInformation[1],
+                    pXcptRec->ExceptionInformation[2], pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+    else
+        RTStrPrintf(szLeadIn, sizeof(szLeadIn), "KiUserExceptionDispatcher: %#x (#%u: %p, %p, %p, %p, %p, %p, %p, %p, ...) @ %p (flags=%#x)",
+                    pXcptRec->ExceptionCode, pXcptRec->NumberParameters,
+                    pXcptRec->ExceptionInformation[0], pXcptRec->ExceptionInformation[1],
+                    pXcptRec->ExceptionInformation[2], pXcptRec->ExceptionInformation[3],
+                    pXcptRec->ExceptionInformation[4], pXcptRec->ExceptionInformation[5],
+                    pXcptRec->ExceptionInformation[6], pXcptRec->ExceptionInformation[7],
+                    pXcptRec->ExceptionAddress, pXcptRec->ExceptionFlags);
+    supR3HardNtDprintCtx(pCtx, szLeadIn);
+
+    return (uintptr_t)g_pfnKiUserExceptionDispatcherReal;
+}
+#endif /* !VBOX_WITHOUT_HARDENDED_XCPT_LOGGING */
+
+
 static void supR3HardenedWinHookFailed(const char *pszWhich, uint8_t const *pbPrologue)
 {
@@ -2696,7 +2808,10 @@
     } const s_aPatches[] =
     {
-        { sizeof(g_abNtCreateSectionPatch),     g_abNtCreateSectionPatch,     &g_pbNtCreateSection,     "NtCreateSection"     },
-        { sizeof(g_abLdrLoadDllPatch),          g_abLdrLoadDllPatch,          &g_pbLdrLoadDll,          "LdrLoadDll"          },
-        { sizeof(g_abKiUserApcDispatcherPatch), g_abKiUserApcDispatcherPatch, &g_pbKiUserApcDispatcher, "KiUserApcDispatcher" },
+        { sizeof(g_abNtCreateSectionPatch),           g_abNtCreateSectionPatch,           &g_pbNtCreateSection,           "NtCreateSection"     },
+        { sizeof(g_abLdrLoadDllPatch),                g_abLdrLoadDllPatch,                &g_pbLdrLoadDll,                "LdrLoadDll"          },
+        { sizeof(g_abKiUserApcDispatcherPatch),       g_abKiUserApcDispatcherPatch,       &g_pbKiUserApcDispatcher,       "KiUserApcDispatcher" },
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+        { sizeof(g_abKiUserExceptionDispatcherPatch), g_abKiUserExceptionDispatcherPatch, &g_pbKiUserExceptionDispatcher, "KiUserExceptionDispatcher" },
+#endif
     };
 
@@ -2812,4 +2927,9 @@
     SUPR3HARDENED_ASSERT(g_pfnLdrInitializeThunk != NULL);
 
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+    PFNRT pfnKiUserExceptionDispatcher = supR3HardenedWinGetRealDllSymbol("ntdll.dll", "KiUserExceptionDispatcher");
+    SUPR3HARDENED_ASSERT(pfnKiUserExceptionDispatcher != NULL);
+#endif
+
     /*
      * Exec page setup & management.
@@ -3056,4 +3176,90 @@
     *(uint32_t *)&g_abKiUserApcDispatcherPatch[1] = (uintptr_t)supR3HardenedMonitor_KiUserApcDispatcher - (uintptr_t)&pbKiUserApcDispatcher[1+4];
 #endif
+
+#ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+    /*
+     * Hook #4 - KiUserExceptionDispatcher
+     * Purpose: Logging crashes.
+     *
+     * This differs from the first function in that is no a system call and
+     * we're at the mercy of the handwritten assembly.  This is not mandatory,
+     * so we ignore failures here.
+     */
+    uint8_t * const pbKiUserExceptionDispatcher = (uint8_t *)(uintptr_t)pfnKiUserExceptionDispatcher;
+    g_pbKiUserExceptionDispatcher = pbKiUserExceptionDispatcher;
+    memcpy(g_abKiUserExceptionDispatcherPatch, pbKiUserExceptionDispatcher, sizeof(g_abKiUserExceptionDispatcherPatch));
+
+# ifdef RT_ARCH_AMD64
+    /*
+     * Patch 64-bit hosts.
+     *
+     * Assume the following sequence and replacing the loaded Wow64PrepareForException
+     * function pointer with our callback:
+     *      cld
+     *      mov  rax, Wow64PrepareForException ; Wow64PrepareForException(PCONTEXT, PEXCEPTION_RECORD)
+     *      test rax, rax
+     *      jz   skip_wow64_callout
+     *      <do_callout_thru_rax>
+     * (We're not a WOW64 process, so the callout should normally never happen.)
+     */
+    if (   pbKiUserExceptionDispatcher[ 0] == 0xfc /* CLD */
+        && pbKiUserExceptionDispatcher[ 1] == 0x48 /* MOV RAX, symbol wrt rip */
+        && pbKiUserExceptionDispatcher[ 2] == 0x8b
+        && pbKiUserExceptionDispatcher[ 3] == 0x05
+        && pbKiUserExceptionDispatcher[ 8] == 0x48 /* TEST RAX, RAX */
+        && pbKiUserExceptionDispatcher[ 9] == 0x85
+        && pbKiUserExceptionDispatcher[10] == 0xc0
+        && pbKiUserExceptionDispatcher[11] == 0x74)
+    {
+        /* Assemble the KiUserExceptionDispatcher patch. */
+        g_abKiUserExceptionDispatcherPatch[1]  = 0x48; /* MOV RAX, supR3HardenedMonitor_KiUserExceptionDispatcher */
+        g_abKiUserExceptionDispatcherPatch[2]  = 0xb8;
+        *(uint64_t *)&g_abKiUserExceptionDispatcherPatch[3] = (uint64_t)supR3HardenedMonitor_KiUserExceptionDispatcher;
+        g_abKiUserExceptionDispatcherPatch[11] = 0x90; /* NOP (was JZ) */
+        g_abKiUserExceptionDispatcherPatch[12] = 0x90; /* NOP (was DISP8 of JZ) */
+    }
+    else
+        SUP_DPRINTF(("supR3HardenedWinInstallHooks: failed to patch KiUserExceptionDispatcher (%.20Rhxs)\n",
+                     pbKiUserExceptionDispatcher));
+# else
+    /*
+     * Patch 32-bit hosts.
+     */
+    /* Just use the disassembler to skip 5 bytes or more. */
+    offJmpBack = 0;
+    while (offJmpBack < 5)
+    {
+        cbInstr = 1;
+        int rc = DISInstr(pbKiUserExceptionDispatcher + offJmpBack, DISCPUMODE_32BIT, &Dis, &cbInstr);
+        if (   RT_FAILURE(rc)
+            || (Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)) )
+        {
+            SUP_DPRINTF(("supR3HardenedWinInstallHooks: failed to patch KiUserExceptionDispatcher (off %#x in %.20Rhxs)\n",
+                         offJmpBack, pbKiUserExceptionDispatcher));
+            break;
+        }
+        offJmpBack += cbInstr;
+    }
+    if (offJmpBack >= 5)
+    {
+        /* Assemble the code for resuming the call.*/
+        *(PFNRT *)&g_pfnKiUserExceptionDispatcherReal = (PFNRT)(uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage];
+
+        memcpy(&g_abSupHardReadWriteExecPage[offExecPage], pbKiUserExceptionDispatcher, offJmpBack);
+        offExecPage += offJmpBack;
+
+        g_abSupHardReadWriteExecPage[offExecPage++] = 0xe9; /* jmp rel32 */
+        *(uint32_t *)&g_abSupHardReadWriteExecPage[offExecPage] = (uintptr_t)&pbKiUserExceptionDispatcher[offJmpBack]
+                                                                - (uintptr_t)&g_abSupHardReadWriteExecPage[offExecPage + 4];
+        offExecPage = RT_ALIGN_32(offExecPage + 4, 16);
+
+        /* Assemble the KiUserExceptionDispatcher patch. */
+        memcpy(g_abKiUserExceptionDispatcherPatch, pbKiUserExceptionDispatcher, sizeof(g_abKiUserExceptionDispatcherPatch));
+        Assert(offJmpBack >= 5);
+        g_abKiUserExceptionDispatcherPatch[0] = 0xe9;
+        *(uint32_t *)&g_abKiUserExceptionDispatcherPatch[1] = (uintptr_t)supR3HardenedMonitor_KiUserExceptionDispatcher - (uintptr_t)&pbKiUserExceptionDispatcher[1+4];
+    }
+# endif
+#endif /* !VBOX_WITHOUT_HARDENDED_XCPT_LOGGING */
 
     /*
@@ -4007,5 +4213,4 @@
 
 
-
 /**
  * Sets up the early process init.
@@ -4119,40 +4324,12 @@
     {
 #ifdef RT_ARCH_AMD64
-        SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Initial context:\n"
-                     "  RAX=%016RX64 RBX=%016RX64 RCX=%016RX64 RDX=%016RX64\n"
-                     "  RSI=%016RX64 RDI=%016RX64  R8=%016RX64  R9=%016RX64\n"
-                     "  R10=%016RX64 R11=%016RX64 R12=%016RX64 R13=%016RX64\n"
-                     "  R14=%016RX64 R15=%016RX64\n"
-                     "  RIP=%016RX64 RSP=%016RX64 RBP=%016RX64 RFLAGS=%08RX32\n"
-                     "   P1=%016RX64  P2=%016RX64  P3=%016RX64 P4=%016RX64\n"
-                     "   P5=%016RX64  P6=%016RX64\n"
-                     "  CS=%04RX16 DS=%04RX16 ES=%04RX16 FS=%04RX16 GS=%04RX16 SS=%04RX16\n"
-                     "  DR0=%016RX64 DR1=%016RX64 DR2=%016RX64 DR3=%016RX64\n"
-                     "  DR6=%016RX64 DR7=%016RX64\n",
-                     Ctx.Rax, Ctx.Rbx, Ctx.Rcx, Ctx.Rdx,
-                     Ctx.Rsi, Ctx.Rdi, Ctx.R8, Ctx.R9,
-                     Ctx.R10, Ctx.R11, Ctx.R12, Ctx.R13,
-                     Ctx.R14, Ctx.R15,
-                     Ctx.Rip, Ctx.Rsp, Ctx.Rbp, Ctx.EFlags,
-                     Ctx.P1Home, Ctx.P2Home, Ctx.P3Home,
-                     Ctx.P4Home, Ctx.P5Home, Ctx.P6Home,
-                     Ctx.SegCs, Ctx.SegDs, Ctx.SegEs, Ctx.SegFs, Ctx.SegGs, Ctx.SegSs,
-                     Ctx.Dr0, Ctx.Dr1, Ctx.Dr2, Ctx.Dr3,
-                     Ctx.Dr6, Ctx.Dr7));
         DWORD64 *pPC = &Ctx.Rip;
 #elif defined(RT_ARCH_X86)
-        SUP_DPRINTF(("supR3HardenedWinSetupChildInit: Initial context:\n"
-                     "  EAX=%08RX32 EBX=%08RX32 ECX=%08RX32 EDX=%08RX32 ESI=%08RX64 EDI=%08RX32\n"
-                     "  EIP=%08RX32 ESP=%08RX32 EBP=%08RX32 EFLAGS=%08RX32\n"
-                     "  CS=%04RX16 DS=%04RX16 ES=%04RX16 FS=%04RX16 GS=%04RX16\n"
-                     "  DR0=%08RX32 DR1=%08RX32 DR2=%08RX32 DR3=%08RX32 DR6=%08RX32 DR7=%08RX32\n",
-                     Ctx.Eax, Ctx.Ebx, Ctx.Ecx, Ctx.Edx, Ctx.Esi, Ctx.Edi,
-                     Ctx.Eip, Ctx.Esp, Ctx.Ebp, Ctx.EFlags,
-                     Ctx.SegCs, Ctx.SegDs, Ctx.SegEs, Ctx.SegFs, Ctx.SegGs,
-                     Ctx.Dr0, Ctx.Dr1, Ctx.Dr2, Ctx.Dr3, Ctx.Dr6, Ctx.Dr7));
         DWORD   *pPC = &Ctx.Eip;
 #else
 # error "Unsupported arch."
 #endif
+        supR3HardNtDprintCtx(&Ctx, "supR3HardenedWinSetupChildInit: Initial context:");
+
         /* Entrypoint for the executable: */
         uintptr_t const uChildMain = uChildExeAddr + (  (uintptr_t)&suplibHardenedWindowsMain
Index: trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm
===================================================================
--- trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm	(revision 81106)
+++ trunk/src/VBox/HostDrivers/Support/win/SUPR3HardenedMainA-win.asm	(revision 81118)
@@ -35,4 +35,7 @@
 extern NAME(supR3HardenedEarlyProcessInit)
 extern NAME(supR3HardenedMonitor_KiUserApcDispatcher_C)
+%ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+extern NAME(supR3HardenedMonitor_KiUserExceptionDispatcher_C)
+%endif
 
 
@@ -184,4 +187,93 @@
 ENDPROC   supR3HardenedMonitor_KiUserApcDispatcher
 
+
+%ifndef VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
+;;
+; Hook for KiUserExceptionDispatcher that logs exceptions.
+;
+; For the AMD64 variant, we're not directly intercepting the function itself, but
+; patching into a Wow64 callout that's done at the very start of the routine.  RCX
+; and RDX are set to PEXCEPTION_RECORD and PCONTEXT respectively and there is a
+; return address.  Also, we don't need to do any return-via-copied-out-code stuff.
+;
+; For X86 we hook the function and have PEXCEPTION_RECORD and PCONTEXT pointers on
+; the stack, but no return address.
+
+; We just call C code here, just like supR3HardenedEarlyProcessInitThunk and
+; supR3HardenedMonitor_KiUserApcDispatcher does.
+;
+; @sa supR3HardenedMonitor_KiUserExceptionDispatcher_C
+;
+BEGINPROC supR3HardenedMonitor_KiUserExceptionDispatcher
+        ;
+        ; Prologue.
+        ;
+
+ %ifndef RT_ARCH_AMD64
+        ; Reserve space for the "return" address.
+        push    0
+ %endif
+
+        ; Create a stack frame, saving xBP.
+        push    xBP
+        SEH64_PUSH_xBP
+        mov     xBP, xSP
+        SEH64_SET_FRAME_xBP 0 ; probably wrong...
+
+        ; Save all volatile registers.
+        push    xAX
+        push    xCX
+        push    xDX
+ %ifdef RT_ARCH_AMD64
+        push    r8
+        push    r9
+        push    r10
+        push    r11
+ %endif
+
+        ; Reserve spill space and align the stack.
+        sub     xSP, 20h
+        and     xSP, ~0fh
+        SEH64_END_PROLOGUE
+
+        ;
+        ; Call the C/C++ code that does the actual work.  For x86 this returns
+        ; the resume address in xAX, which we put in the "return" stack position.
+        ;
+        ; On both AMD64 and X86 we have two parameters on the stack that we
+        ; passes along to the C code (see function description for details).
+        ;
+ %ifdef RT_ARCH_X86
+        mov     xCX, [xBP + xCB*2]
+        mov     xDX, [xBP + xCB*3]
+        mov     [xSP], xCX
+        mov     [xSP+4], xDX
+ %endif
+        call    NAME(supR3HardenedMonitor_KiUserExceptionDispatcher_C)
+ %ifdef RT_ARCH_X86
+        mov     [xBP + xCB], xAX
+ %endif
+
+        ;
+        ; Restore volatile registers.
+        ;
+        mov     xAX, [xBP - xCB*1]
+        mov     xCX, [xBP - xCB*2]
+        mov     xDX, [xBP - xCB*3]
+ %ifdef RT_ARCH_AMD64
+        mov     r8,  [xBP - xCB*4]
+        mov     r9,  [xBP - xCB*5]
+        mov     r10, [xBP - xCB*6]
+        mov     r11, [xBP - xCB*7]
+ %endif
+        ;
+        ; Use the leave instruction to restore xBP and set up xSP to point at
+        ; the resume address. Then use the 'ret' instruction to execute the
+        ; original KiUserExceptionDispatcher code as if we've never been here...
+        ;
+        leave
+        ret
+ENDPROC   supR3HardenedMonitor_KiUserExceptionDispatcher
+%endif ; !VBOX_WITHOUT_HARDENDED_XCPT_LOGGING
 
 ;;
