////////////////////////////////////////////////////////////////////////////// // // Detours Test Program (firstexc.cpp of firstexc.lib) // // Microsoft Research Detours Package // // Copyright (c) Microsoft Corporation. All rights reserved. // // For more information on exception handling, see "A Crash Course on the // Depths of Win32 Structured Exception Handling," by Matt Pietrek in the // January 1997 issue of Microsoft Systems Journal. // #include #include #include "detours.h" #include "firstexc.h" #if _MSC_VER > 1000 #pragma warning(disable: 4740) #endif ////////////////////////////////////////////////////////////////////////////// // static BOOL s_bExceptionDetourInstalled = FALSE; static LPTOP_LEVEL_EXCEPTION_FILTER s_pFirstChanceFilter = NULL; ULONG (NTAPI *Real_NtContinue)(IN PCONTEXT ContextRecord, IN BOOLEAN TestAlerts) = NULL; VOID (NTAPI *Real_KiUserExceptionDispatcher)(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextFrame) = NULL; ////////////////////////////////////////////////////////////////////////////// // // This function effectively removes all try..catch frames for the current // stack. It forces all exceptions to be treated as unhandled exceptions. // #pragma warning(push) #pragma warning(disable: 4733) static VOID WINAPI RemoveAllExceptionHandlers(VOID) { // The basic, OS defined exception frame struct EXCEPTION_REGISTRATION { EXCEPTION_REGISTRATION* prev; FARPROC handler; }; EXCEPTION_REGISTRATION * pVCExcRec = NULL; EXCEPTION_REGISTRATION * pLastGood = NULL; __asm mov eax, FS:[0]; __asm mov [pVCExcRec], eax; for (pLastGood = pVCExcRec; (ULONG)pVCExcRec != ~0ul; ) { if ((ULONG)pVCExcRec >= 0x30000000) break; pLastGood = pVCExcRec; pVCExcRec = (EXCEPTION_REGISTRATION *)(pVCExcRec->prev); } __asm mov eax, [pLastGood]; __asm mov FS:[0], eax; } #pragma warning(pop) ////////////////////////////////////////////////////////////////////////////// // Routine Description: // // This routine is entered on return from kernel mode to dispatch a user // mode exception. If a frame based handler handles the exception, then // the execution is continued. Else last chance processing is performed. // // NOTE: This procedure is not called, but rather dispatched to. // It depends on there not being a return address on the stack // (assumption w.r.t. argument offsets.) // // Arguments: // ExceptionRecord (esp+0) - Supplies a pointer to an exception record. // ContextRecord (esp+4) - Supplies a pointer to a context frame. // // Return Value: // None. // static VOID __declspec(naked) NTAPI Detour_KiUserExceptionDispatcher(PEXCEPTION_RECORD pExceptRec, CONTEXT *pContext) { __asm { xor eax, eax ; // Create fake return address on stack. push eax ; // (Generally, we are called by the kernel.) push ebp ; // Prolog mov ebp, esp ; sub esp, __LOCAL_SIZE ; } LPTOP_LEVEL_EXCEPTION_FILTER pFirstChanceFilter; EXCEPTION_POINTERS ep; DWORD dwReturn; DWORD dwError; ep.ExceptionRecord = pExceptRec; ep.ContextRecord = pContext; pFirstChanceFilter = s_pFirstChanceFilter; dwReturn = EXCEPTION_CONTINUE_SEARCH; dwError = 0; if (s_pFirstChanceFilter) { dwReturn = pFirstChanceFilter(&ep); } if (dwReturn == EXCEPTION_CONTINUE_EXECUTION) { dwError = Real_NtContinue(pContext, 0); // This call should *NEVER* return. If it does, we want to fail to the debugger. RemoveAllExceptionHandlers(); } if (dwReturn == EXCEPTION_EXECUTE_HANDLER) { // Special: Call debugger. RemoveAllExceptionHandlers(); } __asm { mov ebx, pExceptRec ; mov ecx, pContext ; push ecx ; push ebx ; mov eax, [Real_KiUserExceptionDispatcher]; jmp eax ; ; ; The above code should never return. ; int 3 ; // Break! ; mov esp, ebp ; // Epilog pop ebp ; ret ; } } ////////////////////////////////////////////////////////////////////////////// // // Set the first-chance exception filter. // // Returns the pointer to the last first-chance exception filter if there // was one. If this is the first first-chance exception filter, installs // the necessary detour and acquires the appropriate function pointers. // If the parameter is NULL, first-chance exception filtering is disabled. // // A first-chance exception filter should always return one of three // possible codes: // // EXCEPTION_CONTINUE_SEARCH: // The exception was not handled by this filter; continue the // search for the appropriate exception handler. // // EXCEPTION_CONTINUE_EXECUTION: // The exception was handled by this filter; continue execution // at the point were the exception was thrown. // // EXCEPTION_EXECUTE_HANDLER: // Drastic failure in the exception filter. Process the // exception as if no exception handlers were installed. // (i.e. Give the user a chance to invoke the debugger.) // LPTOP_LEVEL_EXCEPTION_FILTER WINAPI DetourFirstChanceExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER pNewFirstChanceFilter) { if (!s_bExceptionDetourInstalled) { s_bExceptionDetourInstalled = TRUE; Real_NtContinue = (ULONG (NTAPI *)(IN PCONTEXT, IN BOOLEAN)) DetourFindFunction("ntdll.dll", "NtContinue"); Real_KiUserExceptionDispatcher = (VOID (NTAPI *)(IN PEXCEPTION_RECORD, IN PCONTEXT)) DetourFindFunction("ntdll.dll", "KiUserExceptionDispatcher"); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)Real_KiUserExceptionDispatcher, Detour_KiUserExceptionDispatcher); DetourTransactionCommit(); } LPTOP_LEVEL_EXCEPTION_FILTER pOldFirstChanceFilter = s_pFirstChanceFilter; s_pFirstChanceFilter = pNewFirstChanceFilter; return pOldFirstChanceFilter; } // ///////////////////////////////////////////////////////////////// End of File.