技術交流,DH講解.
前幾天一個朋友在弄遊戲外掛想帶NP調試,就像自己來捕獲遊戲的異常.
好像就要用到SEH這方面的知識.
一起研究了一下,這裡看下研究 和 在網上找的資料吧.
SEH就是Structure Exception Handling.結構化異常處理,具體可以看下MSDN.
MSDN在手,走遍天下無敵手.哈哈.
當時先自己看下Delphi 怎麼實現try..except..end的吧.我們寫段程式然後調試就知道了.
Procedure TForm1.Button1Click( Sender: TObject );Vara: TForm1;Begina := Nil;Trya.Show;ExceptShowMessage( '1111' );End;End;
這樣是會發生異常的吧,我們下斷點,然後調試的時候ctrl + alt + c進入CPU視窗.
Unit1.pas.38: a := Nil;00452702 33C0 xor eax,eaxUnit1.pas.39: Try00452704 33D2 xor edx,edx00452706 55 push ebp00452707 6821274500 push $004527210045270C 64FF32 push dword ptr fs:[edx]0045270F 648922 mov fs:[edx],espUnit1.pas.40: a.Show;00452712 E811B3FFFF call TCustomForm.Show00452717 33C0 xor eax,eax00452719 5A pop edx0045271A 59 pop ecx0045271B 59 pop ecx0045271C 648910 mov fs:[eax],edx0045271F EB14 jmp +$1400452721 E91E12FBFF jmp @HandleAnyExceptionUnit1.pas.42: ShowMessage( '1111' );00452726 B844274500 mov eax,$004527440045272B E85C8DFDFF call ShowMessage00452730 E87715FBFF call @DoneExceptUnit1.pas.44: End;
我們看見了try模組的代碼了吧.好的自己來寫個函數.
Procedure SetExceptionProc( Proc: Pointer );Asm//將回呼函數指標壓入堆棧push eax//保護 原來的處理函數push fs:[0]mov fs:[0],espEnd;
你看和Delphi代碼裡面Try反編譯出來的一樣吧.
至於為什麼要這樣寫?我也不知道,所以我們現在需要去找資料了.
發生異常時系統的處理順序(by Jeremy Gordon):
1.系統首先判斷異常是否應發送給目標程式的異常處理常式,如果決定應該發送,並且目標程式正在被調試,則系統
掛起程式並向調試器發送EXCEPTION_DEBUG_EVENT訊息.呵呵,這不是正好可以用來探測調試器的存在嗎?
2.如果你的程式沒有被調試或者調試器未能處理異常,系統就會繼續尋找你是否安裝了線程相關的異常處理常式,如果
你安裝了線程相關的異常處理常式,系統就把異常發送給你的程式seh處理常式,交由其處理.
3.每個線程相關的異常處理常式可以處理或者不處理這個異常,如果他不處理並且安裝了多個線程相關的異常處理常式,
可交由鏈起來的其他常式處理.
4.如果這些常式均選擇不處理異常,如果程式處於被調試狀態,作業系統仍會再次掛起程式通知debugger.
5.如果程式未處於被調試狀態或者debugger沒有能夠處理,並且你調用SetUnhandledExceptionFilter安裝了最後異
常處理常式的話,系統轉向對它的調用.
6.如果你沒有安裝最後異常處理常式或者他沒有處理這個異常,系統會調用預設的系統處理常式,通常顯示一個對話方塊,
你可以選擇關閉或者最後將其附加到調試器上的調試按鈕.如果沒有調試器能被附加於其上或者調試器也處理不了,系統
就調用ExitProcess終結程式.
7.不過在終結之前,系統仍然對發生異常的線程異常處理控制代碼來一次展開,這是線程異常處理常式最後清理的機會.
事實上,當異常發生時,系統給了我們一個處理異常的機會,他首先會調用我們自訂的seh處理常式,當然也包括
了相關資訊,在調用之前,系統把包含這些資訊結構的指標壓入stack,供我們的異常處理常式調用,
傳遞給常式的參數通常是四個,其中只有三個有明確意義,另一個到現在為止還沒有發現有什麼作用,
這些參數是:pExcept:DWORD,pErr:DWORD,pContext:DWORD,pDispatch意義如下:
pExcept: --- EXCEPTION_RECORD結構的指標
pErr: --- 前面ERR結構的指標
pContext: --- CONTEXT結構的指標 ,裡面都是我們寄存器的值.
pDispatch:---沒有發現有啥意義
Delphi裡面已經定義好了這些結構體指標了,我就不多說了.
我們來把回呼函數寫出來吧.
Function ExceptionProc( pExcept: PExceptionRecord;pError: Pointer;pContxt: PContext;pDispatch: Pointer ): Integer; Stdcall;BeginShowMessage( '1111' );Result:=0;End;
注意是stdcall調用方式,貌似有些資料上面是cdecl.這裡我們先不管這麼多了.
現在我們看看系統是怎麼調用我們回呼函數的.
關鍵的 Win32 資料結構——線程資訊塊(即 TEB 和 TIB)。
該資料結構的某些域在 Windows NT、Windows 95、Win32s 和 OS/2 平台上是一樣的。
TIB 中的第一個 DWORD 是指向線程 EXCEPTION_REGISTRATION 結構的指標。
在 Intel Win32 平台上,FS 寄存器總是指向當前的 TIB。
因此,在 FS:[0]位置,你能找到 EXCEPTION_REGISTRATION 結構的指標。
這裡也就解釋了 我們SetExceptionProc函數了.
好的我們自己寫段代碼來測試一下了:
Procedure TestException( );Begin//trySetExceptionProc( @ExceptionProc );//make a exceptionAsmxor edx,edxmov [edx],0End;//恢複異常Asmpop eaxmov fs:[0],eaxadd esp,8End;End;Procedure TForm1.Button2Click( Sender: TObject );BeginTestException;End;
測試中恢複異常還是有問題.
終於在CSDN的書獃子的部落格上面找到了答案.
Program Project2;{$APPTYPE CONSOLE}UsesSysUtils,Windows;TypePExecption_Handler= ^Exception_Handler;PException_Registration= ^Exception_Registration;_ExceptionHandler= RecordExceptionRecord: PExceptionRecord;SEH: PException_Registration;Context: PContext;DispatcherContext: Pointer;End;Exception_Handler= _ExceptionHandler;_ExceptionRegistration= RecordPrev: PException_Registration;Handler: PExecption_Handler;End;Exception_Registration= _ExceptionRegistration;ConstEXCEPTION_CONTINUE_EXECUTION= 0; ///恢複CONTEXT裡的寄存器環境,繼續執行EXCEPTION_CONTINUE_SEARCH= 1; ///拒絕處理這個異常,請調用下個異常處理函數EXCEPTION_NESTED_EXCEPTION= 2; ///函數中出發了新的異常EXCEPTION_COLLIDED_UNWIND= 3; ///發生了嵌套展開操作EH_NONE= 0;EH_NONCONTINUABLE= 1;EH_UNWINDING= 2;EH_EXIT_UNWIND= 4;EH_STACK_INVALID= 8;EH_NESTED_CALL= 16;STATUS_ACCESS_VIOLATION= $C0000005; ///訪問非法地址STATUS_ARRAY_BOUNDS_EXCEEDED= $C000008C;STATUS_FLOAT_DENORMAL_OPERAND= $C000008D;STATUS_FLOAT_DIVIDE_BY_ZERO= $C000008E;STATUS_FLOAT_INEXACT_RESULT= $C000008F;STATUS_FLOAT_INVALID_OPERATION= $C0000090;STATUS_FLOAT_OVERFLOW= $C0000091;STATUS_FLOAT_STACK_CHECK= $C0000092;STATUS_FLOAT_UNDERFLOW= $C0000093;STATUS_INTEGER_DIVIDE_BY_ZERO= $C0000094; ///除0錯誤STATUS_INTEGER_OVERFLOW= $C0000095;STATUS_PRIVILEGED_INSTRUCTION= $C0000096;STATUS_STACK_OVERFLOW= $C00000FD;STATUS_CONTROL_C_EXIT= $C000013A;VarG_TEST: DWORD;Procedure Log( LogMsg: String );BeginWriteln( LogMsg );End;//看這個回呼函數,和我們那個有點兒區別,第二個參數的作用原來是ExceptionRegistration,原來秘密在它身上Function ExceptionHandler( ExceptionHandler: EXCEPTION_HANDLER ): LongInt; Cdecl;BeginResult := EXCEPTION_CONTINUE_SEARCH;If ExceptionHandler.ExceptionRecord.ExceptionFlags= EH_NONE ThenBeginCase ExceptionHandler.ExceptionRecord.ExceptionCode OfSTATUS_ACCESS_VIOLATION:BeginLog( '發現異常為非法記憶體訪問,嘗試修複EBX,繼續執行' );ExceptionHandler.Context.Ebx := DWORD( @G_TEST );Result := EXCEPTION_CONTINUE_EXECUTION;End;ElseLog( '這個異常我無法處理,請讓別人處理吧' );End;EndElseIf ExceptionHandler.ExceptionRecord.ExceptionFlags= EH_UNWINDING ThenLog( '異常展開操作' );End;BeginAsm///設定SEHXOR EAX, EAXPUSH OFFSET ExceptionHandlerPUSH FS:[EAX]MOV FS:[EAX], ESP///產生記憶體訪問錯誤XOR EBX, EBXMOV [EBX], 0///取消SEHXOR EAX, EAX//這裡用的這個 而不是我們用的那個pop eax呀..哈哈.一切正常了MOV ECX, [ESP]MOV FS:[EAX], ECXADD ESP, 8End;Readln;End.
牛人拜讀了.大家可以去CSDN上面看下.
好了我是DH.大家想瞭解更多可以看看雪的加密解密 以及 那個 什麼軟體漏洞分析 書上面都有SEH的章節.