Unhandled exceptions in VC8 and above… for x86 and x64

Starting with VC8 (VS2005) it is not possible to catch all unhandled exceptions with your installed exception-filter (via SetUnhandledExceptionFilter). In some situations, the CRT forces the call of WER (Windows Error Reporting). One situation (/GS buffer overruns) can never be catched in your filter. If you want to catch all exception inside your filter, you need to intercept calls to SetUnhandledExceptionFilter to prevent the removing of all previous installed filters.

See also my previous post about this problem:
“SetUnhandledExceptionFilter” and VC8″

Here is the code to “hook” the SetUnhandledExceptionFilter which works now for x86 and x64:

#include 
#include 
#include 

#if defined _M_X64 || defined _M_IX86
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI 
  MyDummySetUnhandledExceptionFilter(
  LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
  return NULL;
}
#else
#error "This code works only for x86 and x64!"
#endif

BOOL PreventSetUnhandledExceptionFilter()
{
  HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
  if (hKernel32 == NULL) return FALSE;
  void *pOrgEntry = GetProcAddress(hKernel32, 
    "SetUnhandledExceptionFilter");
  if(pOrgEntry == NULL) return FALSE;

  DWORD dwOldProtect = 0;
  SIZE_T jmpSize = 5;
#ifdef _M_X64
  jmpSize = 13;
#endif
  BOOL bProt = VirtualProtect(pOrgEntry, jmpSize, 
    PAGE_EXECUTE_READWRITE, &dwOldProtect);
  BYTE newJump[20];
  void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
#ifdef _M_IX86
  DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
  dwOrgEntryAddr += jmpSize; // add 5 for 5 op-codes for jmp rel32
  DWORD dwNewEntryAddr = (DWORD) pNewFunc;
  DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
  // JMP rel32: Jump near, relative, displacement relative to next instruction.
  newJump[0] = 0xE9;  // JMP rel32
  memcpy(&newJump[1], &dwRelativeAddr, sizeof(pNewFunc));
#elif _M_X64
  // We must use R10 or R11, because these are "scratch" registers 
  // which need not to be preserved accross function calls
  // For more info see: Register Usage for x64 64-Bit
  // http://msdn.microsoft.com/en-us/library/ms794547.aspx
  // Thanks to Matthew Smith!!!
  newJump[0] = 0x49;  // MOV R11, ...
  newJump[1] = 0xBB;  // ...
  memcpy(&newJump[2], &pNewFunc, sizeof (pNewFunc));
  //pCur += sizeof (ULONG_PTR);
  newJump[10] = 0x41;  // JMP R11, ...
  newJump[11] = 0xFF;  // ...
  newJump[12] = 0xE3;  // ...
#endif
  SIZE_T bytesWritten;
  BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
    pOrgEntry, newJump, jmpSize, &bytesWritten);

  if (bProt != FALSE)
  {
    DWORD dwBuf;
    VirtualProtect(pOrgEntry, jmpSize, dwOldProtect, &dwBuf);
  }
  return bRet;
}

LONG WINAPI MyUnhandledExceptionFilter(
struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
{
  // TODO: MiniDumpWriteDump
  FatalAppExit(0, _T("Unhandled Exception occured!"));
  return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain()
{
  SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
  BOOL bRet = PreventSetUnhandledExceptionFilter();
  _tprintf(_T("Prevented: %d"), bRet);

  abort();  // force Dr.Watson in release!
}

21 thoughts on “Unhandled exceptions in VC8 and above… for x86 and x64

  1. edgar

    Fine and well done,
    but do we really want to catch it all ?
    It is often the better, faster and a secure way, if the application is crashing immediately to find the real problem.
    You have to know what you do otherwise, but no one knows at the end 🙂

  2. Fjeronimo

    Hi,

    First of all, great work on the ‘Walking the callstack’ article. It was really helpful. This is not a comment to this article, although it is related. I just couldn’t find your email anywhere.

    I have a tricky question and I wonder if you could help us.

    We’ve built a fairly complex symbol engine using the latest version of dbghelp that is called when an exception occurs (either user defined or fatal). The engine processes locals, globals and parameters and is able to handle UDT, arrays, basic types, etc. It also dereferences all pointers and checks for bad memory. UDT children are parsed until they are basic types or up to the maximum recursion depth. The result is then saved to log.

    A symbol is output as:

    ” = []

    where,

    – Symbol’s scope : Local/Global/Parameter for base elements. Normally empty for UDT fields, except when they are static.
    – Symbol’s type (e.g. int).
    – Symbol’s pointer indirection (e.g. *).
    – Symbol’s name.
    – String representation of the symbol’s value. Can also assume BAD! (bad memory), VISITED! (already processed symbol) and EXTERNAL (for symbols not defined in current module – e.g. static consts).
    – Symbol’s address (dereferenced).
    – Optional list of children. Only valid for UDTs (fields) or arrays (elements).

    The engine also allows for custom processors for specific types and names and prevents duplicated addresses from being repeated in the log. Recursive functions are also not a problem.

    Here’s a simple example of the symbol engine in action:

    class Test
    {
    public:
    Test()
    {
    testInt = 45;
    testFloat = -14.7f;
    emptyIntPointer = 0;
    invalidShortPointer = (short *)0xbaadf00d;
    testDoubleArr[0] = 3.4f;
    testDoubleArr[1] = -56.4f;
    testDoubleArr[2] = 111.12f;
    shortBase = 67;
    shortBasePointer = &shortBase;
    intStat = 9;
    }
    int testInt;
    float testFloat;
    int* emptyIntPointer;
    double testDoubleArr[3];
    short *invalidShortPointer;
    short shortBase;
    short *shortBasePointer;
    static int intStat;
    };
    int Test::intStat = 9;

    void TestFrame::TestFunc()
    {
    Test theTest;
    char smallStr[8];
    smallStr[0] = ‘a’;
    smallStr[1] = ‘b’;
    smallStr[2] = ‘c’;
    smallStr[3] = ”;
    smallStr[4] = ‘d’;
    smallStr[5] = ‘e’;

    float c[2];
    c[0] = 3.4f;
    c[1] = 1.2f;
    float* floatPtrArr[5];
    float ab = 5.0f, bc = 10.0f;
    floatPtrArr[0] = &ab;
    floatPtrArr[1] = kNew float;
    floatPtrArr[2] = &bc;
    floatPtrArr[3] = (float*)0xcccccccc;
    floatPtrArr[4] = c;
    floatPtrArr[1][0] = 17.0f;

    Test *theTestPtr = &theTest;

    std::vector vectInt;
    vectInt.push_back(45);
    vectInt.push_back(65);
    vectInt.push_back(16);
    std::vector::iterator it = vectInt.begin();
    }

    ————–

    and here’s the log output for the locals/parameters (non relevant elements have been cut off)

    […]

    [7] KGTest::TestFrame::TestFunc at testframe.cpp(691) [KGTest]

    Local KGParticleEditor::UI::Test* ‘theTestPtr’ [0x13f448]
    int ‘testInt’ = 45 [0x13f448]
    float ‘testFloat’ = -14.700000 [0x13f44c]
    int* ’emptyIntPointer’ = 0 [0x13f450]
    double testDoubleArr[3] [0x13f458] =
    [0] 3.400000 [0x13f458]
    [1] -56.400002 [0x13f460]
    [2] 111.120003 [0x13f468]
    short* ‘invalidShortPointer’ = 0xbaadf00d [internal reserved allocated memory not yet written] [0x13f470]
    short ‘shortBase’ = 67 [0x13f474]
    short* ‘shortBasePointer’ = 67 [0x13f474]
    global/static int ‘KGParticleEditor::UI::Test::intStat’ = 9 [0x98419c]
    Local float c[2] [0x13f440] =
    [0] 3.400000 [0x13f440]
    [1] 1.200000 [0x13f444]
    Local KGParticleEditor::UI::Test ‘theTest’ = VISITED! [0x13f448]
    Local char smallStr[8] [0x13f484] = abc
    Local std::vector<int,std::allocator > ‘vectInt’ [0x13f478]
    std::_Vector_val<int,std::allocator >
    std::_Container_base_aux_alloc_empty<std::allocator >
    std::_Container_base
    std::allocator ‘_Alval’ [0x13f478]
    std::_Allocator_base
    int* ‘_Myfirst’ = 45 [0x1085718]
    int* ‘_Mylast’ = -1414812757 [0x1085724]
    int* ‘_Myend’ = -1414812757 [0x1085724]
    Local float ‘ab’ = 5.000000 [0x13f48c]
    Local float floatPtrArr[5] [0x13f490] =
    [0] 5.000000 [0x13f490]
    [1] 17.000000 [0x13f494]
    [2] 10.000000 [0x13f498]
    [3] 0xcccccccc [VC++ automatically initialized stack object] [0x13f49c]
    [4] 3.400000 [0x13f4a0]
    Local float ‘bc’ = 10.000000 [0x13f4a4]
    Local std::_Vector_iterator<int,std::allocator > ‘it’ [0x13f4a4]
    std::_Vector_const_iterator<int,std::allocator >
    std::_Ranit
    std::_Iterator_with_base
    std::_Iterator_base_universal
    int* ‘_Myptr’ = 45 [0x1085718]

    […]

    ————–

    As you can see, templated classes (in this case stl containers) don’t really provide all the necessary information. And we can’t create a custom processor for handling the symbol’s address, because we would have to build one for each possible datatype and UDT that the container accepts… Is there any way to obtain the contents of a std::vector that allows us to access its individual elements and process them as we like? Something like:

    Local std::vector<int,std::allocator > ‘vectInt’ [0x13f478]
    [0] 45 [address1]
    [1] 65 [address2]
    [2] 16 [address3]

    Basically, we would have to know the type associated with the container, the total container size and a way to obtain the address and size of each of its elements. If this is not possible, can we at least obtain the value of the iterator? Something like:

    Local std::_Vector_iterator<int,std::allocator > ‘it’ = 45 [0x13f4a4]

    And what about more complex containers like sets and maps (red black trees)?

    Thanks in advance for any suggestions,

    Frederico Jerónimo

  3. Fjeronimo

    Ouch. It seems angle brackets are problematic. And the tree hierarchy is all messed up because the spaces have been eaten.

    It should have been:

    [scope] [type][pointer info] ‘[name]’ = [value] [address]
    [children]

    where,

    [scope] – Symbol’s scope : Local/Global/Parameter for base elements. Normally empty for UDT fields, except when they are static.
    [type] – Symbol’s type (e.g. int).
    [pointer info] – Symbol’s pointer indirection (e.g. *).
    [name] – Symbol’s name.
    [value] – String representation of the symbol’s value. Can also assume BAD! (bad memory), VISITED! (already processed symbol) and EXTERNAL (for symbols not defined in current module – e.g. static consts).
    [address] – Symbol’s address (dereferenced).
    [children] – Optional list of children. Only valid for UDTs (fields) or arrays (elements).

    Thanks.

  4. Tom Keetch

    Just a suggestion/question, why didn’t you use IAT rewriting when hooking? Then your code would have worked on all PE executables and not been tied to x86 as it was.

    Also, running code after a GS Exception is not recommended as an attacker has already corrupted the stack and so it’s (probably) not safe to continue executing code. Obviously code does run after the exception, so it can be done, I guess it must depend on your use of global data which may have been corrupted.

    Cheers,

    Tom

  5. Matthew Smith

    Jochen,
    Thanks for posting this approach. In testing this approach on x64 we found an issue. A client of SetUnhandledExceptionFilter was not expecting register r15 to be overwritten. The client later failed because it was using R15 and its value was overwritten. Based on our research, it seems R15 should be preserved across calls. We adjusted the assembly code to use R11 which is a scratch register, and it fixed our issue. Here is our updated x64 code:

    // R11 is not expected to be preserved across calls
    newJump[0] = 0x49; // MOV R11, …
    newJump[1] = 0xBB;
    memcpy(&newJump[2], &pNewFunc, sizeof (pNewFunc));
    newJump[10] = 0x41; // JMP R11, …
    newJump[11] = 0xFF;
    newJump[12] = 0xE3;

    – Matt

  6. Ed

    Hi,
    Thanks for this discussion, it is very useful.

    This works for my 32 bit program however I have an issue with 64 bit program.
    My program loads MbCustom.dll (32 bit version for 32 bit program & 64 bit version for 64 bit program), for 64 bit version when the dll is loaded
    I see access violation in the debugger output and MbCustom.dll will unload. If I disable the call to PreventSetUnhandledExceptionFilter()
    then I don’t see this issue and MbCustom.dll loads fine.

    Enabling the loader logs, I see following (have copied portion that will be useful)

    when it is working fine…
    1aac:16e0 @ 847138126 – LdrGetProcedureAddressEx – INFO: Locating procedure “EncodePointer” by name
    1aac:16e0 @ 847138126 – LdrGetProcedureAddressEx – INFO: Locating procedure “RtlEncodePointer” by name
    1aac:16e0 @ 847138126 – LdrGetProcedureAddressEx – INFO: Locating procedure “DecodePointer” by name
    1aac:16e0 @ 847138126 – LdrGetProcedureAddressEx – INFO: Locating procedure “RtlDecodePointer” by name
    1aac:16e0 @ 847138141 – LdrGetDllHandleEx – INFO: Searching for DLL “msvcrt.dll” from path “f:\myapp\x64\DebugU;C:\Windows\system32;C:\Windows\system;C:\Windows;.;C:\Program Files (x86)\Microsoft DirectX 9.0 SDK (October 2005)\Utilities\Bin\x86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\Microsoft SQL Server\90\Tools\binn\;C:\Program Files (x86)\Common Files\Adobe\AGL;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Windows\System32\
    1aac:16e0 @ 847138141 – LdrGetProcedureAddressEx – INFO: Locating procedure “_set_error_mode” by name
    1aac:16e0 @ 847138141 – LdrGetProcedureAddressEx – INFO: Locating procedure “?set_terminate@@YAP6AXXZP6AXXZ@Z” by name
    1aac:16e0 @ 847138141 – LdrGetProcedureAddressEx – INFO: Locating procedure “_get_terminate” by name
    1aac:16e0 @ 847138141 – LdrGetDllHandleEx – INFO: Searching for DLL “kernel32.dll” from path “f:\myapp\x64\DebugU;C:\Windows\system32;C:\Windows\system;C:\Windows;.;C:\Program Files (x86)\Microsoft DirectX 9.0 SDK (October 2005)\Utilities\Bin\x86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\Microsoft SQL Server\90\Tools\binn\;C:\Program Files (x86)\Common Files\Adobe\AGL;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Windows\System3
    1aac:16e0 @ 847138141 – LdrGetProcedureAddressEx – INFO: Locating procedure “FindActCtxSectionStringW” by name
    1aac:16e0 @ 847138141 – LdrGetDllHandleEx – INFO: Searching for DLL “MSCoree.dll” from path “f:\myapp\x64\DebugU;C:\Windows\system32;C:\Windows\system;C:\Windows;.;C:\Program Files (x86)\Microsoft DirectX 9.0 SDK (October 2005)\Utilities\Bin\x86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\Microsoft SQL Server\90\Tools\binn\;C:\Program Files (x86)\Common Files\Adobe\AGL;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Windows\System32

    When it throws the access violation…
    0340:1778 @ 847589889 – LdrGetProcedureAddressEx – INFO: Locating procedure “EncodePointer” by name
    0340:1778 @ 847589889 – LdrGetProcedureAddressEx – INFO: Locating procedure “RtlEncodePointer” by name
    0340:1778 @ 847589889 – LdrGetProcedureAddressEx – INFO: Locating procedure “DecodePointer” by name
    0340:1778 @ 847589889 – LdrGetProcedureAddressEx – INFO: Locating procedure “RtlDecodePointer” by name
    First-chance exception at 0x77ccfd7d in TestApp.exe: 0xC0000005: Access violation writing location 0x00000000109ff858.
    0340:1778 @ 847589889 – LdrpGenericExceptionFilter – ERROR: Function LdrpLoadDll raised exception 0xc0000005
    Exception record: .exr 000000000011A580
    Context record: .cxr 000000000011A0B0
    0340:1778 @ 847589889 – LdrpLoadDll – ERROR: Running the init routines of DLL “MbCustom.dll” and its static imports failed with status 0xc0000005

    Thanks.
    ed

  7. huasheng

    First of all, thanks for posting the solution. One question is if this function is invoked in a dll which is loaded and free dynamically, the application may crash because the dummy funciton address becomes invalid.

    I tried write the original address back when the dll is detached from the main app process. It seems it works but not fully tested cross platform.

    Could you please shed light on how to solve it?

    Thanks
    hs

  8. jkalmbach Post author

    Good point… I need to make an “undo” function 😉

  9. Pingback: ??VS2005?VC8??????Unhandled exceptions

  10. Vadim

    Sometimes PreventSetUnhandledExceptionFilter() throws exception calling WriteProcessMemory method. Error message: invalid access to a memory location.
    Windows XP Service Pack 3.

  11. Vadim

    No i have not a repro-case. I wrote application which dynamically creates a lot of threads – in application proccess. In each thread i do PreventSetUnhandledExceptionFilter initialization and sometimes i get error:invalid access to a memory location. It seems that error happens there:

    BOOL bProt = VirtualProtect(pOrgEntry, jmpSize,
    PAGE_EXECUTE_READWRITE, &dwOldProtect);

    I made tests on Windows XP and Windows 7 OS – result is the same.

    I’ll try to do a repro-case for you.

  12. jkalmbach Post author

    You must not call “PreventSetUnhandledExceptionFilter” more than once! This has process-wide effect, so please only call it *once*!

  13. Rich

    Interesting article, thanks.

    I also found the following from a article on Codeproject which seems to be doing the same in just 1 line of code. Is there a difference between your method and this one?

    LONG WINAPI RedirectedSetUnhandledExceptionFilter(EXCEPTION_POINTERS * /*ExceptionInfo*/)
    {
    // When the CRT calls SetUnhandledExceptionFilter with NULL parameter
    // our handler will not get removed.
    return 0;
    }

    int main()
    {
    ::SetUnhandledExceptionFilter(OurCrashHandler);

    CAPIHook apiHook(“kernel32.dll”,
    “SetUnhandledExceptionFilter”,
    (PROC)RedirectedSetUnhandledExceptionFilter);

    // Code that crashes
    }

  14. jkalmbach Post author

    Great solution… but I think you missed to llok into the “”CAPIHook”-Framework… I guess it has much more lines than my code 😉

  15. Rich

    Ah yes. I should have noticed that.

    Is it correct that either method will only work when the CRT is dynamically linked?

    I have a program which needs to be statically linked to the CRT and your solution does not seem to work. Is there any way to get it to work for static linking?

  16. jkalmbach Post author

    It should work in in either case; dynamic and static…

  17. Pingback: Improved “PreventSetUnhandledExceptionFilter” | Jochen Kalmbach's Blog

Comments are closed.