aboutsummaryrefslogtreecommitdiffhomepage
path: root/samples/excep/firstexc.cpp
blob: 4222ba891844238871e419f1cea1ed2c179c704b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//////////////////////////////////////////////////////////////////////////////
//
//  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 <stdio.h>
#include <windows.h>
#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.