我讀這article有關Windows的SEH。 這裏是源代碼myseh.cppSEH,調用堆棧回溯走了
我調試了myseh.cpp。我在printf("Hello from an exception handler\n");
處設置了2個斷點,分別爲24行和DWORD handler = (DWORD)_except_handler;
行36行。
然後我跑了它,它在打破行:36。我看到堆棧跟蹤如下。
至於去,AccessViolationException發生,因爲
mov [eax], 1
然後,它打破了在行:24。我看到堆棧跟蹤如下。
相同的線程,但框架main
不見了!而不是_except_handle
。而ESP從0018f6c8
跳到0018ef34
;這是一個很大的差距0018f6c8
和0018ef34
後例外處理。
我知道_except_handle
必須在用戶模式而不是內核模式下運行。 返回_except_handle
後,線程轉向ring0,然後windows內核修改上下文EAX
到&scratch
&,然後返回到ring3。因此線程不斷運行。
我很好奇處理例外窗口的機制: 爲什麼框架調用main
不見了?
爲什麼ESP從0018f6c8
跳到0018ef34
?(我的意思是一個很大的音高),那些ESP地址屬於同一個線程的堆棧嗎?內核是否在ring3上使用了ESP技巧?如果是這樣,爲什麼它選擇地址0018ef34
作爲處理程序回調的框架?非常感謝!
是同一線程的堆棧。內核將'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
是的,我知道當出現異常時,CONTEXT必須是硬件的快照。所以,main()'中的'ContextRecord-> Esp == Esp「。事實上,我們應該責怪調試器的默認設置,它會丟失堆棧跟蹤。順便說一句,我知道'* ContextRecord'的可修改性,這就是內核在回到ring3之前恢復硬件上下文的意思。但WHY是類型'*'而不是'const *'的第二個參數'ExceptionRecord'? – TanakaYasen
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