2017-01-07 58 views
4

我讀這article有關Windows的SEH。 這裏是源代碼myseh.cppSEH,調用堆棧回溯走了

我調試了myseh.cpp。我在printf("Hello from an exception handler\n");處設置了2個斷點,分別爲24行和DWORD handler = (DWORD)_except_handler;行36行。

然後我跑了它,它在打破行:36。我看到堆棧跟蹤如下。

enter image description here 至於去,AccessViolationException發生,因爲mov [eax], 1 然後,它打破了在行:24。我看到堆棧跟蹤如下。 enter image description here

相同的線程,但框架main不見了!而不是_except_handle。而ESP從0018f6c8跳到0018ef34;這是一個很大的差距0018f6c80018ef34 後例外處理。

我知道_except_handle必須在用戶模式而不是內核模式下運行。 返回_except_handle後,線程轉向ring0,然後windows內核修改上下文EAX&scratch &,然後返回到ring3。因此線程不斷運行。

我很好奇處理例外窗口的機制: 爲什麼框架調用main不見了?

爲什麼ESP從0018f6c8跳到0018ef34?(我的意思是一個很大的音高),那些ESP地址屬於同一個線程的堆棧嗎?內核是否在ring3上使用了ESP技巧?如果是這樣,爲什麼它選擇地址0018ef34作爲處理程序回調的框架?非常感謝!

+0

是同一線程的堆棧。內核將'CONTEXT'和'EXCEPTION_RECORD'複製到用戶線程堆棧。關閉當然Esp的例外 - 因爲這已經是Esp嚴重遞減。然後從內核調用'KiUserExceptionDispatcher'回調函數,該指針指向複製的'CONTEXT'和'EXCEPTION_RECORD'記錄。最後調用你的'_except_handler'。但是如果你尋找'ContextRecord-> Esp',你可以注意到它和'main()'中的'Esp'完全相同。對於真正的處理程序實現看'VC \ crt \ src \ i386 \ chandler4.c'和'\ VC \ crt \ src \ amd64 \ chandler.c' – RbMm

+0

是的,我知道當出現異常時,CONTEXT必須是硬件的快照。所以,main()'中的'ContextRecord-> Esp == Esp「。事實上,我們應該責怪調試器的默認設置,它會丟失堆棧跟蹤。順便說一句,我知道'* ContextRecord'的可修改性,這就是內核在回到ring3之前恢復硬件上下文的意思。但WHY是類型'*'而不是'const *'的第二個參數'ExceptionRecord'? – TanakaYasen

+0

ExceptionRecord確實不是const。 ExceptionFlags'在異常處理期間被修改。我們兩次走過棧!首先我們尋找'__try/__ except'塊,直到有些不返回'EXCEPTION_EXECUTE_HANDLER',然後'_except_handlerX'調用'RtlUnwindEx',它在'ExceptionFlags'中設置'EXCEPTION_UNWIND'標誌,並再次爲'__try/__ finally'處理程序走棧。在'winnt.h'和'wdm.h'中尋找'IS_DISPATCHING(Flag)'和'IS_UNWINDING(Flag)'宏 - 還要研究'chandler.c'注意if'(IS_DISPATCHING(ExceptionRecord-> ExceptionFlags))'switch在x64中還使用了另外'IS_TARGET_UNWIND'的 – RbMm

回答

7

您使用的是默認調試程序設置,不夠好,看到所有的細節。他們被選中來幫助您專注於自己的代碼,並儘快開始調試會話。

的[外部代碼]塊告訴你,有一些不屬於你已經寫好的代碼堆棧幀的部分。它們不屬於操作系統。使用工具>選項>調試>常規,並取消選中「啓用我的代碼」選項。

[下面的幀可能不正確...]警告告訴您,調試器沒有準確的PDB來正確地走棧。使用工具>選項>調試>符號並勾選「Microsoft Symbol Servers」選項並選擇一個緩存位置。調試器現在將通過操作系統DLL下載您需要調試的PDB。可能需要一段時間,它只會做一次。

可以推理出大ESP的變化,上下文結構是相當大,佔用空間堆棧。

這些變化後,你應該現在看到類似的東西:

ConsoleApplication1942.exe!_except_handler(_EXCEPTION_RECORD * ExceptionRecord, void * EstablisherFrame, _CONTEXT * ContextRecord, void * DispatcherContext) Line 22 C++ 
[email protected]() Unknown 
[email protected]() Unknown 
[email protected]() Unknown 
ConsoleApplication1942.exe!main() Line 46 C++ 
ConsoleApplication1942.exe!invoke_main() Line 64 C++ 
ConsoleApplication1942.exe!__scrt_common_main_seh() Line 255 C++ 
ConsoleApplication1942.exe!__scrt_common_main() Line 300 C++ 
ConsoleApplication1942.exe!mainCRTStartup() Line 17 C++ 
[email protected]@12() Unknown 
ntdll.dll!__RtlUserThreadStart() Unknown 
[email protected]() Unknown 

錄製在Win10版本1607和VS2015更新2。這不是編寫SEH處理程序的正確方法,請在this post中找到更好的示例。

+0

謝謝。是的,我知道這個例子只是爲了告訴它最基本的概念,不實用。 – TanakaYasen