異常處理
異常和中斷是由CPU觸發(fā)的.
操作系統(tǒng)怎么接收到異常的?
IDT表, 操作系統(tǒng)在啟動(dòng)時(shí),就會(huì)將中斷處理的地址存入到IDT中.
當(dāng)產(chǎn)生中斷的時(shí)候,CPU(硬件)就會(huì)調(diào)用IDT中的函數(shù)(軟件).
Windows的異常處理機(jī)制, 都是由Windows操作系統(tǒng)提供.
- SEH
- VEH
SEH的原理
SEH的異常處理函數(shù)是怎么被調(diào)用?
產(chǎn)生異常后 , 操作系統(tǒng)使用fs段寄存器找到TEB, 通過(guò)TEB.ExceptionList 找到SEH鏈表的頭節(jié)點(diǎn), 通過(guò)節(jié)點(diǎn)中記錄的異常處理函數(shù)的地址調(diào)用該函數(shù).異常過(guò)濾函數(shù)是怎么被調(diào)用的?
由編譯器提供的異常處理函數(shù)(except_handler4)內(nèi)部所調(diào)用的,
except_handler4這個(gè)函數(shù)被充當(dāng)為注冊(cè)SEH節(jié)點(diǎn)時(shí)的異常處理函數(shù).
系統(tǒng)調(diào)用的是except_handler4函數(shù),except_handler4 調(diào)用我們?cè)趀xcept塊的異常過(guò)濾表達(dá)式中給出的異常過(guò)濾函數(shù).except語(yǔ)句塊怎么被執(zhí)行的?
也是由except_handler4函數(shù)調(diào)用的.seh是怎么找到上一層的異常處理塊的.
通過(guò)節(jié)點(diǎn)的Next找到下一個(gè)節(jié)點(diǎn), 然后找到節(jié)點(diǎn)的異常出來(lái)函數(shù).
=====================
- 異常是在哪里產(chǎn)生的?
1.1 CPU外部硬件息拜。
1.2 CPU內(nèi)部中斷(中斷指令)
SEH異常處理函數(shù)是被誰(shuí)調(diào)用的?異常過(guò)濾函數(shù)呢丢氢?
異常處理函數(shù) :保存在SEH節(jié)點(diǎn)中的蘸炸,被操作系統(tǒng)調(diào)用玫镐。是系統(tǒng)的原生SEH異常處理桨踪。
異常過(guò)濾函數(shù) : 保存在編譯器所提供異常機(jī)制的某個(gè)結(jié)構(gòu)中(《軟件調(diào)試》),最終被exceptHandler4所調(diào)用栖博。是對(duì)編譯器在原生SEH異常處理機(jī)制的封裝阁苞。SEH節(jié)點(diǎn)保存在內(nèi)存中的什么位置?
保存在棧中, 一般和函數(shù)是相關(guān)腰埂, 一般在進(jìn)入到函數(shù)的時(shí)候飒焦,SEH的節(jié)點(diǎn)被安裝在棧中,在離開(kāi)函數(shù)之后就會(huì)刪除節(jié)點(diǎn)。
- 從哪可以找到SEH的頭節(jié)點(diǎn)?
TEB.TIB.ExceptionList,也就是FS:[0]
- 異常是怎么從CPU轉(zhuǎn)發(fā)到操作系統(tǒng)中
1.1 CPU的處理:
1.1.1 當(dāng)發(fā)生異常的時(shí)候牺荠,CPU根據(jù)異常號(hào)調(diào)用IDT表中對(duì)應(yīng)的異常處理程序翁巍。
1.2 操作系統(tǒng)的處理:
1.2.1 將各種異常處理函數(shù)填充到IDT
1.2.2 等待IDT的異常處理函數(shù)被調(diào)用
1.2.2.1 KiTrap03 (IDT中3號(hào)處理函數(shù)),相當(dāng)于異常的源頭休雌。
base\ntos\ke\i386\trap.asm
1.2.2.2 函數(shù)的功能:開(kāi)辟椩詈空間保存了產(chǎn)生異常時(shí)的線程上下文。通過(guò)寄存器傳參杈曲,調(diào)用CommonDispatchException函數(shù)驰凛。
主要傳遞了:通過(guò)寄存器傳遞的有:異常地址,異常代碼担扑,異常附加參數(shù)恰响。通過(guò)棧傳遞有:線程的上下文。
1.2.3 CommonDispatchException
1.2.3.1 函數(shù)的功能:
1.2.3.1.1 開(kāi)辟一個(gè)椨肯祝空間將異常記錄信息保存到棧中胚宦。
1.2.3.1.2 獲取異常發(fā)生的模式(用戶層/內(nèi)核層)
1.2.3.1.3 KiDispatchException
參數(shù)1: 異常記錄結(jié)構(gòu)體的地址
參數(shù)2: NULL
參數(shù)3:異常記錄幀
參數(shù)4: 異常發(fā)生的模式
參數(shù)5:是否是第一次處理異常(通過(guò)KiTrapXX系列函數(shù)處理的異常都是第一次)
1.2.4 KiDispatchException
base\ntos\ke\i386\exceptn.c
1.2.4.1 功能:
1.2.4.1.1 內(nèi)核模式的處理:
1.2.4.1.1.1 將異常交給內(nèi)核調(diào)試器處理
1.2.4.1.1.2 內(nèi)核調(diào)試器處理不了才交給異常處理機(jī)制處理
1.2.4.1.1.3 異常處理機(jī)制也處理不了,則進(jìn)入到第二次異常分發(fā):
再將異常交給調(diào)試器處理
1.2.4.1.1.4 還處理不了燕垃,就KeBugCheckEx
1.2.4.2 用戶模式異常的處理
1.2.4.2.1 通過(guò)發(fā)送消息枢劝,將調(diào)試記錄發(fā)送到用戶層的調(diào)試器,并等待調(diào)試器的處理結(jié)果利术。
1.2.4.2.2 如果調(diào)試器處理不了呈野, 則輪到異常處理機(jī)制處理。
1.2.4.2.2.1 將內(nèi)核棧的數(shù)據(jù)拷貝到用戶棧印叁,并將esp指向拷貝后數(shù)據(jù)的首地址
1.2.4.2.2.2 將eip設(shè)置到ntdll!KiUserExceptionDispatcher函數(shù)中被冒。這樣當(dāng)執(zhí)行流從內(nèi)核回到用戶層的時(shí)候, esp指向了EXCEPTION_POINTERS的變量的地址轮蜕, eip指向了用戶層的異常分發(fā)函數(shù)的地址昨悼。
1.2.5 用戶層的異常分發(fā)(用戶層的SHE,VEH的調(diào)用過(guò)程):
1.2.5.1 遍歷VEH跃洛,并調(diào)用異常處理函數(shù)
1.2.5.2 遍歷SEH率触,并調(diào)用異常處理函數(shù)
- 用戶異常處理程序怎么接收到的異常
- 分析操作系統(tǒng)處理異常的源碼
3.1 系統(tǒng)異常處理的步驟
3.2 系統(tǒng)對(duì)調(diào)試機(jī)制的支持
3.2.1 反調(diào)試 - 反調(diào)試和反反調(diào)試
=====================
// 01_try_finally.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//
#include "stdafx.h"
#include <windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
__try{
printf("try塊\n");
//return 0;
*(int*)0 = 0;
__leave; // 以正常方式離開(kāi)try的關(guān)鍵字
}
__finally{
printf("finally塊\n");
}
printf("main\n");
return 0;
}
====================
// 02_try_except.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)汇竭。
//
#include "stdafx.h"
#include <windows.h>
int* g_pNum = NULL;
void fun()
{
__try{
*(int*)0 = 0;
}
__except (EXCEPTION_CONTINUE_SEARCH){
}
}
// 異常過(guò)濾函數(shù), 用于處理程序中出現(xiàn)的異常.
int seh(EXCEPTION_POINTERS* pExce)
{
printf("在%08X處產(chǎn)生了%08X異常\n",
pExce->ExceptionRecord->ExceptionAddress,
pExce->ExceptionRecord->ExceptionCode);
printf("EAX:%08X ECX:%08X\n",
pExce->ContextRecord->Eax,
pExce->ContextRecord->Ecx);
pExce->ContextRecord->Eax = (DWORD)new int;
return EXCEPTION_CONTINUE_EXECUTION;
}
int _tmain(int argc, _TCHAR* argv[])
{
//執(zhí)行處理程序(except塊)
EXCEPTION_EXECUTE_HANDLER;
//繼續(xù)搜索
// 將異常傳遞到上一層的try和except,將異常交給它執(zhí)行
EXCEPTION_CONTINUE_SEARCH;
//繼續(xù)執(zhí)行
// 繼續(xù)執(zhí)行產(chǎn)生異常的那條指令
EXCEPTION_CONTINUE_EXECUTION;
__try{
fun();
//*(int*)0 = 0;
*g_pNum = 10;
printf("try塊\n");
}
__except ( seh(GetExceptionInformation())){
printf("finally塊\n");
}
printf("main()\n");
__try{
*(int*)0 = 0;
printf("try塊\n");
}
__except (EXCEPTION_CONTINUE_EXECUTION){
printf("finally塊\n");
}
return 0;
}
=============================
// 03_veh.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)葱蝗。
//
#include "stdafx.h"
#include <windows.h>
LONG WINAPI veh(EXCEPTION_POINTERS* pExce)
{
printf("veh\n");
// 繼續(xù)執(zhí)行, 說(shuō)明異常已被處理,產(chǎn)生異常的指令將會(huì)
// 被繼續(xù)執(zhí)行
EXCEPTION_CONTINUE_EXECUTION;
// 讓下一個(gè)veh節(jié)點(diǎn)處理異常.
return EXCEPTION_CONTINUE_SEARCH;
}
LONG WINAPI seh(EXCEPTION_POINTERS* pExce){
printf("seh\n");
// 讓下一個(gè)veh節(jié)點(diǎn)處理異常.
return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
//1. 將異常處理函數(shù)注冊(cè)到系統(tǒng)
AddVectoredExceptionHandler(TRUE, veh);
__try{
*(int*)0 = 0;
}
__except (seh(GetExceptionInformation())){
}
return 0;
}
============================
// 04_異常 處理的優(yōu)先級(jí).cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//
#include "stdafx.h"
#include <windows.h>
LONG WINAPI vch(EXCEPTION_POINTERS* pExcept){
printf("vch\n");
return EXCEPTION_CONTINUE_SEARCH;
}
LONG WINAPI veh(EXCEPTION_POINTERS* pExcept){
printf("veh\n");
return EXCEPTION_CONTINUE_SEARCH;
}
LONG WINAPI seh(EXCEPTION_POINTERS* pExcept){
printf("seh\n");
return EXCEPTION_CONTINUE_SEARCH;
}
LONG WINAPI ueh(EXCEPTION_POINTERS* pExcept){
printf("ueh\n");
return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
AddVectoredContinueHandler(TRUE, vch);//vch
AddVectoredExceptionHandler(TRUE, veh);//veh
// 在64位系統(tǒng)下, 當(dāng)程序被調(diào)試時(shí),UEH不會(huì)被調(diào)用
// 不被調(diào)試才會(huì)被調(diào)用.
// 在32位系統(tǒng)下,被調(diào)試時(shí)也會(huì)被調(diào)用.
SetUnhandledExceptionFilter(ueh);
__try{
*(int*)0 = 0;
}
__except (seh(GetExceptionInformation())){
}
return 0;
}
======================
// 05_手工安裝SEH節(jié)點(diǎn).cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)细燎。
//
#include "stdafx.h"
#include <windows.h>
EXCEPTION_DISPOSITION NTAPI seh(struct _EXCEPTION_RECORD *ExceptionRecord,PVOID EstablisherFrame,struct _CONTEXT *ContextRecord,PVOID DispatcherContext)
{
printf("seh\n");
// 繼續(xù)執(zhí)行
return ExceptionContinueExecution;
}
int _tmain(int argc, _TCHAR* argv[])
{
// EXCEPTION_REGISTRATION_RECORD node;
/*
* 產(chǎn)生異常后 , 操作系統(tǒng)使用fs段寄存器找到TEB,
* 通過(guò)TEB.ExceptionList 找到SEH鏈表的頭節(jié)點(diǎn),
* 通過(guò)節(jié)點(diǎn)中記錄的異常處理函數(shù)的地址調(diào)用該函數(shù).
*/
// node.Handler = seh;
// node.Next = NULL;
_asm
{
push seh; // 將SEH異常處理函數(shù)的地址入棧
push fs:[0];//將SEH頭節(jié)點(diǎn)的地址入棧
;// esp + 0 -- > [fs:0]; node.Next;
;// esp + 4 -- > [seh]; node.handler;
mov fs:[0], esp;// fs:[0] = &node;
}
*(int*)0 = 0;
// 平衡椓铰空間
// 還原FS:[0]原始的頭節(jié)點(diǎn)
_asm{
pop fs : [0]; // 將棧頂?shù)臄?shù)據(jù)(原異常頭節(jié)點(diǎn)的地址)恢復(fù)到FS:[0],然后再平衡4個(gè)字節(jié)的棧
add esp, 4; // 平衡剩下的4字節(jié)的棧.
}
return 0;
}
=========================