調試器編寫第一講,調試器基本框架


                  調試器編寫第一講,調試器基本框架


作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)

今天開始調試器第一講,調試器的基本框架,我們用過很多調試器,比如 WinDbg,OllyDbg,那為什么我們還要自己編寫調試器哪?

原因是,OllyDbg等等的各種調試器都太容易被針對了,寫調試器,主要是理解別人怎么反調試,並且我們怎么在安全開發的時候,讓我們的軟件針對調試器.今天就開始調試器第一講,調試器的基本框架

很多人認為調試器怎么寫,沒思路,其實調試器就是調用API,熟練運用這些API,則可以進行軟件調試

一丶寫調試器注意的問題

首先,我們思考一個問題,我們要調試我們的程序,要怎么讓我們的程序知道被調試了

是這樣的,微軟已經幫我們提供了API了,比如我們常用WriteProcessMemory這個API,想一下,微軟怎么可能提供在別人進程里面寫內存的API哪?

其實這個就是調試器用的,只不過被我們玩壞了.

那么我們MSDN搜索一下這個API,就可以找到所有和調試器相關的API

可以在下方看到,所以和調試器相關的API了.

API就怎么多,熟練運用即可.

二丶調試器API各個API的意思

 這里介紹下各個API的意思,並不細講,等到用到的時候才會細講怎么用.主要是熟悉一下,算是翻譯一下API吧.

/*
ContinueDebugEvent      :看名字就知道,繼續調試事件,意思就是調試程序的時候有事件來,你處理完了要繼續.

DebugActiveProcess      :調試程序,附加進程,這個API就是,如果我們的程序打開了,那么調用這個API,傳入進程ID是可以附加調試的.

DebugActiveProcessStop    :停止調試器,調試的指定進程,也就是調試器要停止對某一個進程的調試

debugBreak           :如果程序處於調試的狀態,,如果發生斷點異常(下斷點),允許線程,通知我們的調試器來調試,處理這個異常.否則系統接收
DebugBreakProcess       :在指定的進程中,產生一個斷點異常
DebugSetProcessKillOnExit  :設置調試的線程,退出的時候進行的操作. FatalExit            :強制退出調用的進程,並將控制權交給調試器 FlushInstructionCache :刷新指令的高速緩存 GetThreadContext    :獲取寄存器的信息,具體獲取那個,要設置標志位,標志位在注釋中(MSDN查不到) GetThreadSelectorEntry  :獲取指定選擇器和線程的描述的入口表. IsDebuggerPresent   :判斷進程是否在調試器下面運行(和我們前面說的反調試那個差不多,都是判斷調試是否運行) OutPutDebugString :調試輸出字符串. ReadProcessMemory :讀取指定進程的某塊數據 SetThreadContext      :設置寄存器 WaitForDebugEvent      :等待調試事件 WriteProcessMemory    :往指定進程寫某塊數據
*/

三丶查閱MSDN,看下調試器的說明

我們要調試一個程序,第一步就要創建這個程序,那么創建程序是用CreateProcess,那么我們看下MSDN有沒有特別說明

隨便進去一個調試的API,看到下方說了一個基本的調試,點擊去看看.

第一個說,關於基本調試,也就是介紹一下

第二個說,調試的參考,我們先看下第一個怎么說把

可以看到,他告訴了我們,關於基本調試個各種步驟.

第一個: 說的是,調試的函數,也就是上面的我們那些調試API

第二個: 說的是,調試的時候,進程,線程,和異常函數的各種特性,也就是說調試進程,線程,還有異常的時候,該怎么做.

第三個: 告訴了我們使用基本的調試函數可以創建一個基本的調試器.這些函數,可以下斷點,異常等等.

第四個: 這個則是告訴了我們,調試程序的時候來的各種事件.

看下第二個把,調試的時候的各種特性.

說的是,進程函數,線程函數,異常函數,我們現在首要任務是調試進程,所以看第一個

告訴了我們,要調試一個進程,你要用CreateProcess,並且要設置標志為上面畫框框的地方.

下面還說了,我們要還可以通過OpenPeocess獲得一個進程ID,通過DebugActiveProcess附加這個進程調試.

那么我們現在知道了,要調試一個程序,首先要創建進程,並且給出特定的參數.

四丶編寫調試器的基本框架(匯編編寫,C/C++一樣編寫)

 我們知道了,要調試一個程序,要先創建進程,然后我們應該要等待事件的到來,進行處理,如果是否繼續處理,交給

ContinueDebugEvent

開始編寫程序:

RadAsm創建控制台程序

匯編代碼

1.創建調試進程

LOCAL @si:STARTUPINFO 
    LOCAL @di:PROCESS_INFORMATION
    mov @si.cb,sizeof STARTUPINFO
    ;創建調試進程
    invoke CreateProcess,offset g_szAppName,NULL,NULL,NULL,FALSE,DEBUG_PROCESS,NULL,NULL,addr @si,addr @di

CTRL + D 調試,看下計算器是否啟動.

2.調用調試函數,等待事件到來.

注重第一個參數,說了一個DEBUG_EVENT,看下這個結構體

typedef struct _DEBUG_EVENT { 
  DWORD dwDebugEventCode; 
  DWORD dwProcessId; 
  DWORD dwThreadId; 
  union { 
      EXCEPTION_DEBUG_INFO Exception; 
      CREATE_THREAD_DEBUG_INFO CreateThread; 
      CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; 
      EXIT_THREAD_DEBUG_INFO ExitThread; 
      EXIT_PROCESS_DEBUG_INFO ExitProcess; 
      LOAD_DLL_DEBUG_INFO LoadDll; 
      UNLOAD_DLL_DEBUG_INFO UnloadDll; 
      OUTPUT_DEBUG_STRING_INFO DebugString; 
      RIP_INFO RipInfo; 
  } u; 
} DEBUG_EVENT, *LPDEBUG_EVENT;

說了異常來的時候,這個結構體會有異常的代碼,進程的ID,線程的ID,以及根據不同異常,產生不同的結構體

(因為是共用體,所以什么異常來了,就會有不同的結構體,保存了不同的異常信息)

舉個例子,第一個:

異常信息是EXCEPTION_DEBUG_EVENT 表示調試的時候異常信息事件來了,我們看下它的結構體是什么

typedef struct _EXCEPTION_DEBUG_INFO { 
  EXCEPTION_RECORD ExceptionRecord; 
  DWORD dwFirstChance; 
} EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO; 

它的結構體則保存了和異常信息,什么的,(和篩選器異常的結構體差不多.)

上面一個框是一個參數,下面說了,只有線程,被創建調試進程的時候才能用,也就是創建調試進程使用.

下面有例子,抄例子

我就不截圖看了.

 直接編寫匯編代碼:

   assume ecx:ptr DEBUG_EVENT
    
    
    ;EXCEPTION_DEBUG_EVENT  代表我不處理,DBG_CONTINUE代表我處理,這就是OD的F9運行起來的功能
    .while TRUE
          invoke WaitForDebugEvent,addr @DebugEv,INFINITE         ;等待事件到來
          lea ecx,@DebugEv                                        ;結果放到ecx當中,因為ecx已經假設為DebugEvent結構體了
          mov ebx,[ecx].dwDebugEventCode                          ;將結構體的異常代碼給ebx,然后下方通過ebx判斷是哪個事件來了
        .if     ebx == EXCEPTION_DEBUG_EVENT    ;檢測事件是什么
         invoke  crt_puts,offset g_szEcpt
         mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
         
        .elseif ebx == CREATE_THREAD_DEBUG_EVENT
        
        invoke  crt_puts,offset g_szEcpt
        mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
        
        .elseif ebx == CREATE_PROCESS_DEBUG_EVENT
        
        invoke  crt_puts,offset g_szEcpt
        mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
        
        .elseif ebx == EXIT_THREAD_DEBUG_EVENT
        
        invoke  crt_puts,offset g_szEcpt
        mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
        
        .elseif ebx == EXIT_PROCESS_DEBUG_EVENT
        mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
        
        .elseif ebx == LOAD_DLL_DEBUG_EVENT
        
        mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
        
        .elseif ebx == UNLOAD_DLL_DEBUG_EVENT
        mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
        
        .elseif ebx == OUTPUT_DEBUG_STRING_EVENT
        mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
        .endif
        invoke ContinueDebugEvent,[ecx].dwProcessId,
                                  [ecx].dwThreadId,       ;繼續處理事件
                                   @dwContinueStatus
    .endw

代碼很簡單,利用waitforDebugEvent獲取異常代碼,然后異常代碼給ebx,通過ebx模擬switch,看看那個異常事件回來.當然,匯編代碼會放到課堂資料中,帶着C代碼一起發布,這里只是簡單解釋一下.

五丶異常事件是什么

上面說了,異常事件和ebx(異常代碼比較)那么分別代表什么意思?

EXCEPTION_DEBUG_EVENT          :被調試的調試程序的時候來,會在調試的程序中下一個int3斷點.如果被調試的時候,則回來,屬於系統斷點

CREATE_THREAD_DEBUG_EVENT      :被調試的程序創建線程的時候會來

CREATE_PROCESS_DEBUG_EVENT     :被調試的程序創建進程的時候會來

EXIT_THREAD_DEBUG_EVENT       :被調試的程序退出線程的時候會來

EXIT_PROCESS_DEBUG_EVENT      :被調試的程序退出進程的時候會來

LOAD_DLL_DEBUG_EVENT        :被調試的程序加載DLL的時候會來

UNLOAD_DLL_DEBUG_EVENT       :被調試的程序卸載DLL會來

OUTPUT_DEBUG_STRING_EVENT     :被調試的程序調試輸出的時候會來

RIP_EVENT                :64位系統的事件,如果編寫32位調試器,這個則不重要.

今天主要是講了一個框架,具體可以回去自己寫一下就明白,很簡單.

課堂資料:

鏈接:http://pan.baidu.com/s/1jHDJOUU 密碼:mbn4

作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM