2014-04-03 17 views
0

在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有字符串消息:

[email protected] /c/WINDOWS/system32 
$ strings ntdll.dll | grep instruction 
The instruction at %p referenced memory at %p. 
The instruction at %p tried to %s 

應該是什麼得到充分的信息以正確的方式你知道嗎?謝謝。

回答

2

ExceptionInformation[]數組中的參數與NTDLL消息字符串中的格式說明符不匹配。如果您閱讀documentation,則EXCEPTION_ACCESS_VIOLATIONEXCEPTION_IN_PAGE_ERROR將在第一個數組元素中提供一個讀/寫標誌,並在第二個數組元素中提供一個存儲器地址,但是您嘗試使用的消息字符串都需要2個內存地址。基本上,​​不適合用於格式化這兩個特定的例外。對於其他例外情況,ExceptionInformation[]的內容未定義,因此您不應將它們傳遞給​​。索引到ExceptionInformation[]數組時

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:您需要看ExceptionCode,然後相應地格式化你的信息,例如? EXCEPTION_ACCESS_VIOLATION提供了2個數組元素,EXCEPTION_IN_PAGE_ERROR規定了3.您正在跳過第一個數組元素並訪問不存在的最後一個數組元素。

您在致電​​時也未指定FORMAT_MESSAGE_ARGUMENT_ARRAY標誌。如果沒有該標誌,則最後一個參數必須是合適的va_list,如果不是NULL。您沒有使用va_list,所以在傳遞序數組時必須指定FORMAT_MESSAGE_ARGUMENT_ARRAY

+0

+1是爲測試目的。事實上,對於這個異常(訪問衝突),第一個參數是NULL。 對於FORMAT_MESSAGE_ARGUMENT_ARRAY,是的,我同意,它在文檔中,但它不能解決問題。 Microsoft建議將FormatMessage API與ntdll.dll消息一起使用(http://support.microsoft.com/kb/259693)。他們的方法的優點是使用預定義的字符串。你提出的代碼不幸的不是。 – jlguenego

+1

[閱讀文檔](http://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx)。訪問衝突的第一個參數是**讀/寫標誌**,而不是指針。 0表示在讀取時發生AV,1表示寫入時發生AV,8表示DEP被違反。如果您閱讀了鏈接到的支持頁面,則不會使用格式參數。正如我們可以清楚地看到的那樣,這兩個異常的參數不能用FormatMessage()格式化,它們不匹配。 –

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