技術交流,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,eax
Unit1.pas.39: Try
00452704 33D2 xor edx,edx
00452706 55 push ebp
00452707 6821274500 push $00452721
0045270C 64FF32 push dword ptr fs:[edx]
0045270F 648922 mov fs:[edx],esp
Unit1.pas.40: a.Show;
00452712 E811B3FFFF call TCustomForm.Show
00452717 33C0 xor eax,eax
00452719 5A pop edx
0045271A 59 pop ecx
0045271B 59 pop ecx
0045271C 648910 mov fs:[eax],edx
0045271F EB14 jmp +$14
00452721 E91E12FBFF jmp @HandleAnyException
Unit1.pas.42: ShowMessage( '1111' );
00452726 B844274500 mov eax,$00452744
0045272B E85C8DFDFF call ShowMessage
00452730 E87715FBFF call @DoneExcept
Unit1.pas.44: End;
我們看見了try模塊的代碼了吧.好的自己來寫個函數.
Procedure SetExceptionProc( Proc: Pointer );
Asm//將回調函數指針壓入堆棧push eax//保護 原來的處理函數push fs:[0]mov fs:[0],esp
End;
你看和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的章節.
總結
以上是生活随笔為你收集整理的Delphi - SEH研究的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。