系统异常处理:尝试使用参数获取字符串消息

时间:2014-04-03 12:27:25

标签: c windows winapi exception interrupt

在Windows XP上,我尝试在CPU引发异常(中断)时打印官方字符串消息。在这里,我有一段代码试图访问

#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);
    }
}

它产生以下输出:

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

如您所见,邮件被截断。

ntdll.dll上有字符串消息:

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

知道什么应该是获得完整信息的正确方法?感谢。

2 个答案:

答案 0 :(得分:3)

ExceptionInformation[]数组中的参数与NTDLL消息字符串中的格式说明符不匹配。如果您阅读documentationEXCEPTION_ACCESS_VIOLATIONEXCEPTION_IN_PAGE_ERROR在第一个数组元素中提供读/写标志,在第二个数组元素中提供内存地址,但是您尝试的消息字符串使用两个期望2个内存地址。基本上,FormatMessage()不适合用于格式化这两个特定的异常。对于其他例外情况,ExceptionInformation[]的内容未定义,因此您不应将其传递给FormatMessage()。您需要查看ExceptionCode,然后相应地格式化您的消息,例如:

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;
}

话虽如此,为什么在索引+1数组时使用ExceptionInformation[]EXCEPTION_ACCESS_VIOLATION提供了2个数组元素,EXCEPTION_IN_PAGE_ERROR提供3.您正在跳过第一个数组元素并访问不存在的最后一个数组元素。

调用FORMAT_MESSAGE_ARGUMENT_ARRAY时,您也没有指定FormatMessage()标志。如果没有该标志,则最后一个参数必须是正确的va_list,如果不是NULL。您没有使用va_list,因此在传递序数数组时必须指定FORMAT_MESSAGE_ARGUMENT_ARRAY

答案 1 :(得分:1)

我遇到了同样的问题,我无法解决它,但我可以提供一些提示。

“字符串”工具没有显示正确的字符串,因为它正在查找ASCII文本,但真实文本存储在UTF-16(即Windows本机)中。

您应该从FormatMessage获取的文本如下所示:

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

显然,FormatMessage将%0定义为转义字符。它用空字符替换它然后停止。我检查了调试器中的字符串,以确认它在%0之后停止。标志FORMAT_MESSAGE_IGNORE_INSERTS应该禁用此行为,但它也没有帮助。

如果您确实想要获取该错误文本,可以从ntdll.dll中的消息表中检索它。答案显示了如何:https://stackoverflow.com/a/24127220/35951。我尝试了它,它确实返回ID为0xc0000005的完整字符串。

编辑:原来已经解释过问题的人(但他们也没有提供好的解决方案):https://stackoverflow.com/a/33044673/35951