1. 程式人生 > 其它 >竹林蹊徑-深入淺出Windows驅動開發第一章HelloWorld疑問解決

竹林蹊徑-深入淺出Windows驅動開發第一章HelloWorld疑問解決

  最近學習驅動開發,參照深入淺出Windows驅動開發第一章節HelloWorld驅動敲了程式碼,在停止驅動時虛擬機器藍屏,本次解決這個問題,並進行記錄。

開發環境為:vs2013 WDK8.1

除錯環境:虛擬機器VMware 12、 系統Windows7 sp1 x86、VirtualKD-Redux-2020.4、Windbg 10

安裝驅動軟體:InstDrv.exe 這款軟體可以安裝驅動服務、啟動、停止、解除安裝驅動服務,非常方便。

  問題表現為:在驅動啟動後,使用InstDrv軟體停止驅動,會導致win7虛擬機器藍屏。

  擷取一部分程式碼如下:

  #define DRIVER_SYMBOLLINK_NAME L"\\??\\MySymbolLinkName"

  #pragma alloc_text(INIT, DriverEntry)
  #pragma alloc_text(PAGE, DefaultDispatch)
  #pragma alloc_text(PAGE, DriverUnload)

  DriverEntry函式部分程式碼:

  deviceExtension = (PDEVICE_EXTENSION)deviceObject->DeviceExtension;//額外資料指標
  deviceExtension->DeviceObject = deviceObject;
  deviceExtension->DeviceName = deviceName;

  RtlInitUnicodeString(&symbolicLink, DRIVER_SYMBOLLINK_NAME);
  deviceExtension->SymbolicLink = symbolicLink;//

  DriverUnload函式部分程式碼如下:

  

  while (NULL != deviceObject)
  {
    PDEVICE_EXTENSION deviceExtesion = \
    (PDEVICE_EXTENSION)deviceObject->DeviceExtension;

    // 刪除符號連結與裝置
    linkName = deviceExtesion->SymbolicLink;
    IoDeleteSymbolicLink(&linkName);//此處導致崩潰

    deviceObject = deviceObject->NextDevice;
    IoDeleteDevice(deviceExtesion->DeviceObject);
  }

  使用VirtualKD-Redux-2020.4 搭建虛擬機器雙機除錯核心環境,成功啟動後,設定好windbg符號檔案路徑,自己寫的驅動檔名為 HelloWorldWDM.sys,debug模式生成的檔案,生成sys時並且本機生成pdb檔案

  動態除錯:

  1. 使用CTRL+BREAK中斷虛擬機器系統,輸入命令 sxe ld:HelloWorldWDM,輸入命令 g 回車 執行。

  2. 已管理員許可權執行InstDrv.exe 安裝驅動服務,啟動服務,會中斷到偵錯程式中。

  3. 使用命令 !reload /fHelloWorldWDM.sys可以載入本地磁碟中驅動模組的PDB檔案。

  4. 使用lm命令檢視 pdb檔案是否載入成功

  5. 輸入命令bp HelloWorldWDM!DriverEntry打下int 3斷點,輸入g執行,自動開啟原始碼除錯

  6.動態除錯分別在DriverEntry和DriverUnload函式下斷點,IoDeleteSymbolicLink(&linkName)函式導致崩潰,這個linkName是符號連結名,初始化及使用流程如下:

    (1) 在DriverEntry函式中呼叫RtlInitUnicodeString(&symbolicLink, DRIVER_SYMBOLLINK_NAME)對symbolicLink初始化,symbolicLink.Buffer指向字串L"\\??\\MySymbolLinkName"

    (2)在DriverEntry函式中使用語句deviceExtension->SymbolicLink = symbolicLink; 進行復制,這個可以理解為淺拷貝,所以當前deviceExtension->SymbolicLink中的Buffer變數與symbolicLink.Buffer指向同一塊記憶體

    (3)在DriverUnload函式中檢視linkName = deviceExtesion->SymbolicLink, 動態除錯可知linkName指向的記憶體區域此時不可訪問。

  7. 所以現在的問題在於為什麼在DriverEntry函式字串區域可以訪問,在DriverUnload中字串區域不可訪問,是記憶體頁面被換出了嗎?

  靜態分析:

  使用IDA分析驅動檔案,檢視DriverEntry函式:

 

  從上面兩幅圖中可以看到呼叫RtlInitUnicodeString函式並使用符號連結名來初始化變數,而且看最左側還有INIT標記。

  這個之前有#pragma alloc_text(INIT, DriverEntry)一句話,這個表明DriverEntry函式執行完畢後是可以從記憶體頁面中換出的,變數字串\??\MySymbolLinkName也是INIT標記,也會被換出,之前是\\,此處是\,是因為C語言字串中\\代表一個\。

  

  通過動態除錯檢視RtlInitUnicodeString函式是如何實現的

  kd> u nt!RtlInitUnicodeString l30
  nt!RtlInitUnicodeString:
  83e73ed8 57       push edi
  83e73ed9 8b7c240c    mov edi,dword ptr [esp+0Ch] //字串指標
  83e73edd 8b542408   mov edx,dword ptr [esp+8]
  83e73ee1 c70200000000mov dword ptr [edx],0
  83e73ee7 897a04    mov dword ptr [edx+4],edi //複製Buffer為字串指標
  83e73eea 0bff      or edi,edi
  83e73eec 7422     je nt!RtlInitUnicodeString+0x38 (83e73f10)
  83e73eee 83c9ff     or ecx,0FFFFFFFFh
  83e73ef1 33c0      xor eax,eax
  83e73ef3 f266af     repne scas word ptr es:[edi]
  83e73ef6 f7d1      not ecx
  83e73ef8 d1e1      shl ecx,1 //計算 字串長度
  83e73efa 81f9feff0000  cmp ecx,0FFFEh
  83e73f00 7605     jbe nt!RtlInitUnicodeString+0x2f (83e73f07)
  83e73f02 b9feff0000   mov ecx,0FFFEh
  83e73f07 66894a02    mov word ptr [edx+2],cx
  83e73f0b 49      dec ecx
  83e73f0c 49      dec ecx
  83e73f0d 66890a    mov word ptr [edx],cx
  83e73f10 5f       pop edi
  83e73f11 c20800    ret 8

  由上面可知RtlInitUnicodeString函式是通過複製Buffer變數指向字串區域,並沒有再次拷貝一份字串,所以當DriverEntry函式被換出記憶體後,呼叫DriverUnload函式時訪問Buffer指向的記憶體區域無法訪問導致藍屏。

  修改:

  筆者修改DriverUnload部分原始碼如下:

  UNICODE_STRING SymbolLinkName;
  RtlInitUnicodeString(&SymbolLinkName, DRIVER_SYMBOLLINK_NAME);
  IoDeleteSymbolicLink(&SymbolLinkName);

  執行時不會崩潰,檢視這部分程式碼的靜態反彙編:

  

  由上面兩幅圖可以看到 呼叫RtlInitUnicodeString函式時使用的字串是位於PAGE頁面,這個頁面代表不可換出到記憶體。

  總結:

  1. 本次學習了核心雙機除錯、原始碼除錯、符號配置。

  2. 使用者態程式設計時上面定義#define 字串的形式,字串位於PE端的.const段,可讀不可寫,程式內部多次用到此字串時,始終用的是.const段的那一份,這與核心態程式設計不一樣,目前來看,在INIT頁面與PAGE頁面使用的不同記憶體地址的字串。

  3. 學習了RtlInitUnicodeString函式初始化UNICODE_STRING的方式。