1

On windows XP, I try to print the official string message when the CPU is raising an exception (interrupt). Here I have a piece of code which try to access

#include <stdio.h>
#include <windows.h>

LONG WINAPI e(LPEXCEPTION_POINTERS ExceptionInfo) {
    printf("Exception Handled ...\n");
    char buf[8192];
    memset(buf, 0, 8192);

    void * pArgs[ExceptionInfo->ExceptionRecord->NumberParameters];
    for (int i = 0; i < ExceptionInfo->ExceptionRecord->NumberParameters; i++) {
        printf("arg[%d] = %d\n", i, ExceptionInfo->ExceptionRecord->ExceptionInformation[i+1]);
        pArgs[i] = (void *) ExceptionInfo->ExceptionRecord->ExceptionInformation[i+1];
    }
    HMODULE Hand = LoadLibrary("NTDLL.DLL");
    int res = FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_FROM_HMODULE,
        Hand,
        ExceptionInfo->ExceptionRecord->ExceptionCode,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        buf,
        8192,
        (va_list *) pArgs);
    printf("res=%d\n", res);
    FreeLibrary(Hand);

    printf("ExceptionCode=0x%08x (%s)\n", ExceptionInfo->ExceptionRecord->ExceptionCode, buf);
    printf("ExceptionFlags=%d\n", ExceptionInfo->ExceptionRecord->ExceptionFlags);
    printf("ExceptionAddress=0x%08x\n", ExceptionInfo->ExceptionRecord->ExceptionAddress);
    printf("NumberParameters=%d\n", ExceptionInfo->ExceptionRecord->NumberParameters);
    printf("ExceptionInformation=%s\n", ExceptionInfo->ExceptionRecord->ExceptionInformation);


    return EXCEPTION_EXECUTE_HANDLER;
}



int main() {
    LPTOP_LEVEL_EXCEPTION_FILTER p = SetUnhandledExceptionFilter(e);
    for (int i = 10; i < 256; i++) {
        int *p = (int *) i;
        printf("address pointed by p = 0x%08x\n", *p);
    }
}

It produces the following output:

Exception Handled ...
arg[0] = 10
arg[1] = 65599
res=22
ExceptionCode=0xc0000005 (The instruction at "0x)
ExceptionFlags=0
ExceptionAddress=0x004018da
NumberParameters=2
ExceptionInformation=

As you can see the message is truncated.

On the ntdll.dll there is the string message:

jlouis@didi /c/WINDOWS/system32
$ strings ntdll.dll | grep instruction
The instruction at %p referenced memory at %p.
The instruction at %p tried to %s

Any idea of what should be the right way to get the full message ? Thanks.

Alex K.
  • 171,639
  • 30
  • 264
  • 288
jlguenego
  • 1,192
  • 15
  • 23

2 Answers2

3

The arguments in the ExceptionInformation[] array do not match the format specifiers in the NTDLL message string. If you read the documentation, EXCEPTION_ACCESS_VIOLATION and EXCEPTION_IN_PAGE_ERROR provide a read/write flag in the first array element and a memory address in the second array element, but the message strings you are trying to use both expect 2 memory addresses instead. Basically, FormatMessage() is not appropriate to use for formatting those two specific exceptions. For other exceptions, the contents of ExceptionInformation[] are undefined, so you should not pass them to FormatMessage(). You need to look at the ExceptionCode and then format your message accordingly, for example:

http://flylinkdc.googlecode.com/svn-history/r10225/trunk/windows/ExceptionDlg.h

std::wstring FormatExceptionMessage()
{
    std::wstring str;

    LPCWSTR pFmt = NULL;

    if (m_pException->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
    {
        DWORD_PTR dwAddress = 0;
        if (m_pException->ExceptionRecord->NumberParameters == 2)
        {
            if (m_pException->ExceptionRecord->ExceptionInformation[0] == 0)
                pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation reading 0x%08Ix.";
            else if (m_pException->ExceptionRecord->ExceptionInformation[0] == 8)
                pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation DEP 0x%08Ix.";
            else
                pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation writing 0x%08Ix.";

            dwAddress = m_pException->ExceptionRecord->ExceptionInformation[1];
        }
        else
            pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000005: Access violation";

        str.resize(95); //TODO
        wsprintf(&str.at(0), pFmt, m_pException->ExceptionRecord->ExceptionAddress, dwAddress);
        dcassert(str.size() > (size_t)lstrlen(str.c_str()));
    }
    else if (m_pException->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR)
    {
        DWORD_PTR dwAddress = 0;
        DWORD_PTR dwCode = 0;
        if (m_pException->ExceptionRecord->NumberParameters == 3)
        {
            if (m_pException->ExceptionRecord->ExceptionInformation[0] == 0)
                pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault reading 0x%08Ix with code 0x%08Ix.";
            else if (m_pException->ExceptionRecord->ExceptionInformation[0] == 8)
                pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault DEP 0x%08Ix with code 0x%08Ix.";
            else
                pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault writing 0x%08Ix with code 0x%08Ix.";

            dwAddress = m_pException->ExceptionRecord->ExceptionInformation[1];
            dwCode = m_pException->ExceptionRecord->ExceptionInformation[3];
        }
        else
            pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0xC0000006: Page fault";

        str.resize(115); //TODO
        wsprintf(&str.at(0), pFmt, m_pException->ExceptionRecord->ExceptionAddress, dwAddress, dwCode);
        dcassert(str.size() > (size_t)lstrlen(str.c_str()));
    }
    else
    {
        LPWSTR pMessage = NULL;
        int iMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, GetModuleHandle(L"ntdll.dll"), m_pException->ExceptionRecord->ExceptionCode, 0, (LPWSTR) & pMessage, 0, NULL);

        if (pMessage)
            pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0x%08x: %s";
        else
            pFmt = L"Unhandled exception at 0x%08Ix:\r\nCode 0x%08x";

        str.resize(115 + iMsgLen); // 55 -> 115 -  http://code.google.com/p/flylinkdc/issues/detail?id=571
        wsprintf(&str.at(0), pFmt, m_pException->ExceptionRecord->ExceptionAddress, m_pException->ExceptionRecord->ExceptionCode, pMessage);
        dcassert(str.size() > (size_t)lstrlen(str.c_str()));

        if (pMessage)
            LocalFree(pMessage);
    }

    return str;
}

With that said, why are you using +1 when indexing into the ExceptionInformation[] array? EXCEPTION_ACCESS_VIOLATION provides 2 array elements, EXCEPTION_IN_PAGE_ERROR provides 3. You are skipping the first array element and accessing a last array element that does not exist.

You are also not specifying the FORMAT_MESSAGE_ARGUMENT_ARRAY flag when calling FormatMessage(). Without that flag, the last parameter must be a proper va_list if not NULL. You are not using va_list, so you must specify FORMAT_MESSAGE_ARGUMENT_ARRAY when passing an ordinal array.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • the +1 was for testing purpose. In fact, for this exception (access violation), the first arg is NULL. For the FORMAT_MESSAGE_ARGUMENT_ARRAY, yes, I agree, it is in the doc but it does not solve the problem. Microsoft proposes to use the FormatMessage API with the ntdll.dll messages (http://support.microsoft.com/kb/259693). The advantage of their method is to use the predefined strings. The code that you propose unfortunately does not. – jlguenego Apr 05 '14 at 08:36
  • 1
    [Read the documentation](http://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx). The first argument for an access violation is a **read/write flag**, not a pointer. 0 means the AV occurred while reading, 1 means the AV occured while writing, and 8 means DEP was violated. And if you read the Support page you linked to, it is not using formatting arguments. As we can clearly see, the arguments for these two exceptions CANNOT be formatted using `FormatMessage()`, they do not match. – Remy Lebeau Apr 05 '14 at 16:39
2

I'm having the same issue, and I can't quite solve it, but I can offer some hints.

The "strings" tool isn't showing you the correct strings, because it's looking for ASCII text, but the real text is stored in UTF-16 (i.e. Windows native).

The text that you should get from FormatMessage looks like this:

"The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s."

Apparently FormatMessage defines %0 as an escape character. It replaces it with a null character and then stops. I checked the string in the debugger to confirm that it stops after the %0. The flag FORMAT_MESSAGE_IGNORE_INSERTS is supposed to disable this behaviour, but it didn't help either.

If you really want to get that error text, you can retrieve it from the message table in ntdll.dll. There's an answer that shows how: https://stackoverflow.com/a/24127220/35951. I tried it and it does return the full string with the ID 0xc0000005.

EDIT: Turns out someone already explained the issue (but they don't offer a good solution either): https://stackoverflow.com/a/33044673/35951

Community
  • 1
  • 1
mooware
  • 1,722
  • 2
  • 16
  • 25