Windows驅動開發學習筆記(二)—— 驅動調試&核心程式設計基礎
-
- 基礎知識
- 驅動調試
-
- PDB(Program Debug Database)
- WinDbg 加載 PDB
- 實驗:調試 .sys 檔案
-
- 第一步:編譯代碼
- 第二步:将 .sys 檔案拷貝到虛拟機中
- 第三步:添加 pdb 檔案路徑
- 第四步:部署 .sys 檔案并運作
- 核心程式設計基礎
-
- 核心API的使用
- 未導出函數的使用
- 基本資料類型
- 傳回值
- 核心中的異常處理
- 常用核心函數(記憶體操作)
- 核心字元串種類
- 核心字元串常用函數
基礎知識
- 驅動程式無法在目前系統中進行調試,否則會導緻系統卡死
- 可以采用雙機調試的方式調試驅動程式
驅動調試
當我們使用 windbg 檢視某一個位址的反彙編時,例如:
windbg 能夠自動幫我們識别出該位址屬于哪個函數(紅框部分)
思考:windbg 是如何識别出該位址對應的函數的?
PDB(Program Debug Database)
- PDB檔案是在我們編譯工程的時候産生的,它是和對應的子產品(exe或dll)一起生成出來的
- 每個子產品編譯的時候都可以生成自己的PDB檔案,比如
/.exe
/.dll
等等.sys
- 0環調試器(例如 WinDbg)正是通過解析pdb檔案來找到函數與位址之間的對應關系
WinDbg 加載 PDB
實驗:調試 .sys 檔案
第一步:編譯代碼
#include "ntddk.h"
//解除安裝函數
VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("驅動程式已停止.\r\n");
}
//驅動程式入口函數,相當于控制台的main函數
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
__asm
{
int 3
mov eax, eax
mov eax, eax
mov eax, eax
}
DbgPrint("驅動程式已運作.\r\n");
//設定一個解除安裝函數 便于退出
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
第二步:将 .sys 檔案拷貝到虛拟機中
第三步:添加 pdb 檔案路徑
第四步:部署 .sys 檔案并運作
WinDbg 成功截獲中斷即可
核心程式設計基礎
核心API的使用
- 在應用層程式設計時,可以使用WINDOWS提供的各種API函數,隻要導入頭檔案<windows.h>就可以了。但是在核心程式設計的時候,不能像在Ring3那樣直接使用
- 微軟為核心程式提供了專用的API,隻要在程式中包含相應的頭檔案就可以使用了,如:#include <ntddk.h> (前提是已經正确安裝了WDK)
- 在應用層程式設計的時候,我們通過MSDN來了解函數的詳細資訊,在核心程式設計的時候,要使用WDK自己的幫助文檔
未導出函數的使用
描述:
- WDK說明文檔中隻包含了核心子產品導出的函數,對于未導出的函數,則不能直接使用。
- 如果要使用未導出的函數,隻要自己定義一個函數指針,為函數指針提供正确的函數位址就可以使用了
擷取未導出的函數位址:
- 特征碼搜尋
- 解析核心PDB檔案
基本資料類型
在核心程式設計的時候,強烈建議大家遵守WDK的編碼習慣,不要這樣寫: unsigned long length;
WDK類型:
ULONG(unsigned long) PULONG(unsigned long *)
UCHAR(unsigned char) PUCHAR(unsigned char *)
UINT(unsigned int) PUNIT(unsigned int *)
VOID(void) PVOID(void *)
傳回值
大部分核心函數的傳回值都是NTSTATUS類型,如:
NTSTATUS PsCreateSystemThread();
NTSTATUS ZwOpenProcess();
NTSTATUS ZwOpenEvent();
這個值能說明函數執行的結果,如:
STATUS_SUCCESS 0x00000000 成功
STATUS_INVALID_PARAMETER 0xC000000D 參數無效
STATUS_BUFFER_OVERFLOW 0x80000005 緩沖區長度不夠
當調用的核心函數時,如果傳回的結果不是STATUS_SUCCESS,就說明函數執行中遇到了問題,具體是什麼問題,可以在ntstatus.h檔案中檢視
核心中的異常處理
描述:
- 在核心中,一個小小的錯誤就可能導緻藍屏,比如:讀寫一個無效的記憶體位址
- 為了讓自己的核心程式更加健壯,強烈建議大家在編寫核心程式時,使用異常處理
Windows提供了結構化異常處理機制,一般的編譯器都是支援的,如下:
__try{
//可能出錯的代碼
}
__except(filter_value) {
//出錯時要執行的代碼
}
filter_value:
EXCEPTION_EXECUTE_HANDLER(1) //代碼進入except塊
EXCEPTION_CONTINUE_SEARCH(0) //不處理異常,由上一層調用函數處理
EXCEPTION_CONTINUE_EXECUTION(-1) //回去繼續執行錯誤處的代碼
常用核心函數(記憶體操作)
核心字元串種類
ANSI_STRING字元串:
typedef struct _STRING
{
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
}STRING;
UNICODE_STRING字元串:
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaxmumLength;
PWSTR Buffer;
} UNICODE_STRING;