讀書筆記|Windows 除錯原理學習|持續更新
關於除錯方面的學習筆記,主要來源於《軟體除錯》的讀書筆記和夢織未來論壇的視訊教程
1.偵錯程式使用一個死迴圈監聽除錯資訊。
DebugActiveProcess(PID);
while(TRUE) { DEBUG_EVENT MyDebugInfo; WaitForDebugEvent(MyDebugInfo,INFINITE);//阻塞 switch (MyDebugInfo.dwDebugEventCode) { case CREATE_THREAD_DEBUG_EVENT: break; } }
2.什麼是除錯資訊,程式建立、終止,載入模組都是除錯資訊。dwDebugEventCode說明了除錯資訊的種類。
- dwDebugEventCode
-
Type: DWORD
-
The code that identifies the type of debugging event. This member can be one of the following values.
Value Meaning - CREATE_PROCESS_DEBUG_EVENT
- 3
Reports a create-process debugging event. The value of u.CreateProcessInfo specifies a CREATE_PROCESS_DEBUG_INFO structure.
- CREATE_THREAD_DEBUG_EVENT
- 2
Reports a create-thread debugging event. The value of u.CreateThread specifies a CREATE_THREAD_DEBUG_INFO structure.
- EXCEPTION_DEBUG_EVENT
- 1
Reports an exception debugging event. The value of u.Exception specifies an EXCEPTION_DEBUG_INFO structure.
- EXIT_PROCESS_DEBUG_EVENT
- 5
Reports an exit-process debugging event. The value of u.ExitProcess specifies an EXIT_PROCESS_DEBUG_INFO structure.
- EXIT_THREAD_DEBUG_EVENT
- 4
Reports an exit-thread debugging event. The value of u.ExitThread specifies an EXIT_THREAD_DEBUG_INFO structure.
- LOAD_DLL_DEBUG_EVENT
- 6
Reports a load-dynamic-link-library (DLL) debugging event. The value of u.LoadDll specifies a LOAD_DLL_DEBUG_INFO structure.
- OUTPUT_DEBUG_STRING_EVENT
- 8
Reports an output-debugging-string debugging event. The value of u.DebugString specifies an OUTPUT_DEBUG_STRING_INFO structure.
- RIP_EVENT
- 9
Reports a RIP-debugging event (system debugging error). The value of u.RipInfo specifies a RIP_INFO structure.
- UNLOAD_DLL_DEBUG_EVENT
- 7
Reports an unload-DLL debugging event. The value of u.UnloadDll specifies an UNLOAD_DLL_DEBUG_INFO structure.
3.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;
4.一但附加程式,Windows系統會傳送除錯資訊。包括已經建立過的程式和載入過的模組資訊也會傳送
5.使用者層除錯函式的實現
6.除錯原理概述
Windows除錯系統使用事件驅動,這一點與窗體是很相似的。
WaitForDebugEvent是用來等待除錯事件的,偵錯程式處理除錯事件時,被除錯程式會掛起,所以偵錯程式處理完畢後要呼叫ContinueDebugEvent來使掛起的被除錯程式繼續執行。
在系統核心中除錯事件的資料結構是DBGKM_APIMSG,而NTDLL也就是原生應用層使用的是DBGUI_WAIT_STATE_CHANGE,而應用層的偵錯程式是使用DEBUG_EVENT。所以要進行一些結構的轉換。
在核心建立執行緒的函式執行過程中會呼叫一個DbgkCreateThread函式,這個DbgkCreateThread函式會檢查新建執行緒的程式是否正在被除錯(通過檢查DebugPort的值是否為空)然後會傳送除錯訊息。
注意windows核心函式會根據DebugPort是否為空來判斷程式是否處於被除錯情況。
核心態下的除錯結構DBGKM_APIMSG
1 typedef struct _DBGKM_APIMSG 2 { 3 PORT_MESSAGE h; // LPC埠訊息結構,XP之前使用 4 DBGKM_APINUMBER ApiNumber; // 訊息型別 5 ULONG ReturnedStatus; // 偵錯程式的回覆狀態 6 union // 具體描述訊息的共用體,真正的資訊在這裡面 7 { 8 DBGKM_EXCEPTION Exception; // 異常 9 DBGKM_CREATE_THREAD CreateThread; // 建立執行緒 10 DBGKM_CREATE_PROCESS CreateProcess; // 建立程式 11 DBGKM_EXIT_THREAD ExitThread; // 執行緒退出 12 DBGKM_EXIT_PROCESS ExitProcess; // 程式退出 13 DBGKM_LOAD_DLL LoadDll; // 對映DLl 14 DBGKM_UNLOAD_DLL UnloadDll; // 反對映Dll 15 }; 16 } DBGKM_MSG, *PDBGKM_MSG; 17 18 複製程式碼
NTSTATUS
DbgkpSendApiMessage(
IN OUT PDBGKM_APIMSG ApiMsg,
IN BOOLEAN SuspendProcess
);
除錯系統使用DbgkpSuspendProcess和DbgkpResumeProcess這兩個函式來控制被除錯程式。
DbgkpSuspendProcess會凍結被除錯程式中除了呼叫執行緒之外的所有執行緒,執行這個函式後被除錯程式中就只有這個發生除錯資訊的執行緒還活動著。接著會執行實際的傳送訊息的函式,
即DbgkpQueueMessage。
windows除錯子系統處於CSRSS會話管理器中,除錯子系統是以核心物件DebugObject為核心的。
除錯物件
來自wrk1.2 typedef struct _DEBUG_OBJECT { KEVENT EventsPresent; FAST_MUTEX Mutex; LIST_ENTRY EventList; ULONG Flags; } DEBUG_OBJECT, *PDEBUG_OBJECT
EventPresent事件物件是用來同步偵錯程式程式和被除錯程式的。函式WaitForDebugEvent等待的其實就這個事件。
快速互斥體Mutex用來處理併發訪問,相當於一個鎖的作用。
偵錯程式與除錯子系統連線時,除錯子系統會建立一個除錯物件(NtCreateDebugObject),並且將其儲存在偵錯程式當前執行緒的TEB的DbgSsReserved[1]中,而這個執行緒就是偵錯程式執行緒。
要建立偵錯程式與被除錯程式之間的聯絡,需要把這個除錯物件設定到被除錯程式的EPROCESS的DebugPort中。
DbgkpQueueMessage函式用於向一個除錯物件的訊息佇列中增加除錯事件
這裡EventList連結串列中每一項都是如下結構
1 typedef struct _DEBUG_EVENT { 2 LIST_ENTRY EventList; // Queued to event object through this 3 KEVENT ContinueEvent; //用於等待偵錯程式回覆的事件物件 4 CLIENT_ID ClientId; //除錯事件所在的執行緒ID和程式ID 5 PEPROCESS Process; // 被除錯程式的EPROCESS 6 PETHREAD Thread; // 被除錯程式中觸發除錯事件的執行緒的ETHREAD 7 NTSTATUS Status; //除錯事件處理結果 8 ULONG Flags; 9 PETHREAD BackoutThread; // 產生假訊息(Faked)的執行緒ETHREAD 10 DBGKM_APIMSG ApiMsg; // 除錯事件的真正內容 11 } DEBUG_EVENT, *PDEBUG_EVENT;
以上來自WRK1.2,注意這個是與使用者層同名都是DEBUG_EVENT但是內容完全不同,這是個核心結構。是核心中的除錯事件結構。
DbgkpQueueMessage把這個結構插入到除錯物件的除錯事件連結串列中。
DbgkpQueueMessage有等待和不等待兩種方式,如果指定不等待(非同步處理)則函式直接返回。
如果沒有指定不等待則設定除錯物件的EventPresent,然後再等待(KeWaitForSingleObject)DEBUG_EVENT結構中的ContinueEvent物件用來等待偵錯程式回覆。
偵錯程式呼叫ContinueDebugEvent實際上就是設定這個ContinueEvent物件。
在偵錯程式程式執行的NtDebugActiveProcess中會呼叫一個函式DbgkpSetProcessDebugObject將一個除錯物件設定到要除錯的程式中(即EPROCESS的DebugPort)。
這樣被除錯程式就與偵錯程式程式產生了聯絡(通過除錯物件)
而由於
- 這個函式是代表附加程式方式除錯(只有這種方式才會呼叫這個函式)
- 這個函式代表剛剛啟動對程式的除錯
- 附加程式方式除錯代表目標程式已經執行了一段時間
所以就需要進行虛假除錯資訊傳送。
會通過遍歷被除錯程式的所有執行緒,然後在除錯核心物件中放置這些執行緒的虛假除錯事件訊息。再防止虛假模組載入除錯事件訊息。
當取消對程式的除錯時,會將DebugPort埠清零。
偵錯程式的除錯執行緒的TEB中有特殊結構,這個是區別於普通執行緒的地方。DbgSsReserved[0]指向一個被除錯程式的所有執行緒的連結串列,用來描述被除錯程式中的每一個執行緒。
DbgSsReserved[1]指向除錯物件。
WaitForDebugEvent和ContinueDebugEvent這兩個函式會維護那個執行緒連結串列。
一個程式被除錯會造成
- 程式的EPROCESS的DebugPort值不為0
- 程式的PEB的BeingDebugged值不為0
- 可能會有偵錯程式建立在被除錯程式中的遠端執行緒——RemoteBreakin執行緒
大名鼎鼎的IsDebuggerPresent就是通過判斷BeingDebugged來實現的。
偵錯程式與被除錯程式之間的互動被稱做“除錯會話”
兩種除錯方式
- 啟動被除錯程式
- 附加到已經執行的被除錯程式
1.啟動被除錯程式
首先偵錯程式執行緒會呼叫一個DbgUiConnectToDbg來是偵錯程式執行緒與除錯子系統建立連線(初始化偵錯程式執行緒),具體做法是新建一個核心除錯物件,然後把這個核心除錯物件放入偵錯程式執行緒的DbgSsReserved[1]中,這樣偵錯程式執行緒就初始化好了。
當呼叫CreateProcess建立程式時指定DEBUG_PROCESS標誌即可。系統會把呼叫這個函式的程式當作偵錯程式程式,把新建立的程式當作被除錯的程式。
建立起除錯關係
當程式的初始執行緒創立時會檢視自己是否是被除錯中(BeingDebugged標誌),如果是被除錯中會呼叫DbgBreakPoint來觸發一個斷點
2.附加到已經執行的被除錯程式
通過DebugActiveProcess就可以附加到一個已經執行的程式中。
首先是DbgUiConnectToDbg,這一步與上面是一樣的。
開啟被除錯的程式,因為不開啟被除錯的程式也就沒辦法對其進行操作。
呼叫核心函式(向下分發呼叫)NtDebugActiveProcess
這個核心函式主要是
1.傳送偽造的執行緒建立、程式建立和模組載入除錯訊息。
2.設定被除錯程式的除錯埠,除錯物件在DbgUiConnectToDbg呼叫後就已經建立好了直接拿來用就可以,同時設定被除錯程式BeingDebugged欄位。
為什麼會先傳送除錯訊息,後設定除錯埠呢?
這個不是很奇怪嗎?傳送之後才去設定除錯埠?
其實是因為,所謂的傳送除錯訊息實質上指的是建立並設定好一些除錯事件,然後把這些除錯事件放入除錯物件的除錯事件連結串列中,這樣一來是否設定好了除錯埠也就無關緊要了。
因為這些資料只是儲存在除錯物件中,還沒有人去等待。
WaitForDebugEvent
偵錯程式在使用者層的操作接下來就會呼叫WaitForDebugEvent函式來實現,用來取出一個DEBUG_EVENT結構,這個函式是阻塞的,因而可以設定一個等待時間來防止無限等待。
這個函式會呼叫底層的等待除錯事件函式,然後把等待到的結構轉化為使用者態的DEBUG_EVENT結構。因為我們前面說過,一個除錯事件在不同的層次下的表示的資料結構是不同的。
偵錯程式是如何實現讓執行中的被除錯程式立刻中斷到偵錯程式中的呢?這個功能叫做非同步阻停,一種實現方法是使用CreateRemoteThread函式來新建一個觸發int 3斷點的執行緒。
系統中已經提供一個用來觸發斷點的函式,NTDLL的DbgUiRemoteBreakin函式。
windows xp之後系統提供了一個現成的API DebugBreakProcess
1 BOOL WINAPI DebugBreakProcess( 2 _In_ HANDLE Process 3 );
這個函式會自動建立遠端執行緒
相關文章
- 【持續更新...】ligerGrid 學習筆記2019-05-11筆記
- 【持續更新...】ECharts學習筆記2019-05-11Echarts筆記
- 【持續更新...】Nginx 學習筆記2019-05-11Nginx筆記
- Java 學習筆記(持續更新)2019-06-09Java筆記
- 《Python 簡明教程》讀書筆記(持續更新)2020-04-14Python筆記
- 【持續更新...】Microsoft SSIS 學習筆記2019-05-11ROS筆記
- Node.js 學習筆記_20170924(持續更新…)2019-02-16Node.js筆記
- drupal7學習筆記—–(持續更新中…)2019-05-10筆記
- 《CSS世界》閱讀筆記,持續更新2022-04-18CSS筆記
- [Android學習筆記]雜碎知識(持續更新)2020-10-18Android筆記
- 愛玩手機的貓Linux學習筆記(持續更新)2020-11-13Linux筆記
- 【Mysql原理和原始碼學習記錄】學而思網校早讀會--持續更新中2020-08-07MySql原始碼
- 一個前端工程師的Docker學習筆記【持續更新】2020-03-31前端工程師Docker筆記
- 遊戲開發流程與思想學習筆記(持續更新)2020-04-07遊戲開發筆記
- 軟考筆記 --- 持續更新2024-04-05筆記
- AnimalController 學習 持續更新2024-10-27Controller
- 彙編筆記(持續更新中)2024-11-23筆記
- 日常工作筆記(持續更新中。。)2018-06-04筆記
- Web命令執行筆記(持續更新)2024-05-02Web筆記
- 學習 Laravel —— 前端篇(持續更新)2019-10-05Laravel前端
- MySql報錯(持續更新)2019-06-23MySql
- C語言初學習——易錯點合集(持續更新中)2024-03-14C語言
- 【筆記】從0開始的sql注入漏洞學習(持續更新直到完結)2024-07-19筆記SQL
- Spring Boot初學改錯合集(持續更新)2020-10-07Spring Boot
- 編譯原理讀書筆記2020-11-21編譯原理筆記
- Mysql索引讀書筆記(待續)2018-03-05MySql索引筆記
- iOS開發備忘筆記 (持續更新中)2018-06-14iOS筆記
- 【少用會忘】PHP 函式筆記(持續更新)2020-01-08PHP函式筆記
- [oracle零碎筆記]oracle零碎筆記(持續更新…)2018-10-15Oracle筆記
- 前端學習資源彙總(持續更新)2018-12-09前端
- Kotlin學習資料彙總(持續更新...)2018-03-12Kotlin
- spark學習筆記--Spark調優與除錯2018-07-12Spark筆記除錯
- Laravel 軟刪除所有坑 - 持續更新2021-03-19Laravel
- 讀書筆記-增量學習-Large Scale Incremental Learning2020-10-09筆記REM
- 如果你也打算學習 Spring Cloud [持續更新]2020-01-07SpringCloud
- 【Elasticsearch學習】DSL搜尋大全(持續更新中)2020-05-25Elasticsearch
- (持續更新)Qt3D 學習資源2019-07-05QT3D
- Python 學習除錯記錄2020-10-27Python除錯
- GitHub學習除錯記錄2020-11-09Github除錯