1. 程式人生 > >Windows驅動開發(中間層)

Windows驅動開發(中間層)

依據《Windows核心安全與驅動開發》及MSDN等網路質料進行學習開發。

二、初步環境

1、下載安裝WDK7.1.0(WinDDK\7600.16385.1)

2、下載InstDrv軟體(用於安裝、啟動、停止、解除安裝驅動)

介面如下:

 

注:srvinstw.exe 也可以安裝、解除安裝sys檔案,但需要手動開啟、關閉,即在cmd命令視窗下執行net start 驅動名、net stop 驅動名來啟動、停止服務。

3、下載DbgWiew.exe 軟體(檢視核心模組的輸出資訊)

地址:https://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

注:詳見DebugView的使用文件。

4、下載64Signer-V1.2(Win7 64位 私有測試數字簽名軟體)

地址:http://www.yiiyee.cn/Blog/64signer-download/

5、其他軟體:虛擬光碟機DAEMON Tools Lite、EasyBCD(系統引導檔案修復工具)、PartitionManager(將C盤設定為活動分割槽)等

三、初步示例

1、編寫一個簡單的驅動樣例(載入、解除安裝及資訊輸出等)

注:詳見TestDriver.c

核心模組的入口是DriverEnter函式,在載入時被系統程序System呼叫。若想能解除安裝驅動,必須設定驅動物件的DriverUnload函式指標。

Windows核心程式設計不是應用程式程式設計,它有自己的一套東西,所有的Win32函式都不能使用,部分C Runtime函式也不能使用,需要呼叫核心API函式,因此在使用函式前,現在幫助裡查詢該函式。

幫助文件在“開始”-->“所有程式”-->“Windows Driver Kits”下。如圖:

 

2、使用WDK的build工具,編譯時需要makefile.def和SOURCES檔案。

1)makfile.def檔案固定,拷貝一份即可,不需要任何改動。

2)SOURCES檔案內容關聯編譯那些檔案及編譯出的.sys檔案的名字等。

WDK的build工具在“開始”-->“所有程式”-->“Windows Driver Kits”-->“WDK 7600.16385.1”下,選擇要根據本地主機的版本,如:本機是64位的Win7旗艦版,則build工具要選x64 Checked build Environment(對應debug)或x64 Free build Environment(對應Release)。

3、InstDrv軟體直接安裝生成的.sys檔案,安裝、解除安裝成功,啟動失敗,並提示“Windows需要已數字簽名的驅動程式”,DebugView軟體中無自定義資訊輸出。圖如下:

 

解決方法:

1、從http://www.yiiyee.cn/Blog/64signer-download/下載Win7 64位私有測試數字簽名軟體(64Signer-V1.2),以管理員身份執行,瀏覽驅動檔案,進行測試數字簽名。圖如下:

 

2、重新安裝簽名後的.sys驅動檔案,並啟動,依然報並提示“Windows需要已數字簽名的驅動程式”。 圖如下:

 

3、以管理員身份執行cmd.exe,,並執行bcdedit /set testsigning true 命令,若提示“操作成功完成”,則表示命令執行成功;否則,若失敗,請將bcdedit命令所在boot資料夾的所在盤(一般C盤)設定為活動狀態;若依然失敗,請修復boot資料夾的所在盤下的Boot(系統引導檔案所在),或乾脆恢復|重灌系統。

4、將Windows 7 設定為測試版本成功後,須重啟計算機,重啟後,以管理員身份執行InstDrv.exe軟體,對已數字簽名的.sys驅動檔案進行安裝、啟動、關閉、解除安裝均成功。圖如下:

 

注:若驅動中包含中斷或錯誤,則計算機藍屏,須重啟恢復。恢復後的計算機系統內新安裝的軟體會遭到解除安裝或損壞,須重新安裝。

以上是一個簡單的驅動程式開發步驟。若要進行較複雜的開發,一般需要配置VS開發環境、安裝虛擬機器、WinDgb.exe等軟體進行除錯等。

四、完善環境

1、下載並安裝VS2010(64位)

地址:https://www.visualstudio.com/downloads/download-visual-studio-vs

2、下載WinDbg.exe軟體

地址:http://www.microsoft.com/whdc/devtools/debugging/default.mspx

3、虛擬機器(WMware10.0供雙機除錯)

地址:http://www.microsoft.com/zh-CN/download/confirmation.aspx?id=8002

五、配置VS2010

1、依據本機版本配置管理器。win7旗艦64位系統配置如下:

點選VS內工具欄中的解決方案配置的下按鈕(即debug所處的下拉框按鈕),點選配置管理器,點選活動方案配置,點選新建,輸入DriverDebug64、預設空,在專案上下文中的平臺項新建X64並在活動解決方案平臺:選X64。

注:32位系統選Win32

2、在VS中VisualC++下新建“空專案”,將TestDrive.c檔案新增到工程中(或直接新建.c檔案),.c檔案新增後,在屬性中就顯示出C/C++配置項。

3、點選VS中“檢視”選單->“其他視窗”->“屬性管理器”,右擊屬性管理器中的DriverDebug64,選擇屬性,在彈出的窗體中進行必要的設定。(或直接點選專案的屬性)

注: 1、WDK7.1.0預設安裝在C:\WinDDK\7600.16385.1資料夾下

2、此處的設定都是必須的, 對複雜些的驅動開發也許還要額外另加設定

具體如下:

1)常規

目標副檔名:.sys

2)VC++目錄

可執行檔案目錄(編譯器路徑):

C:\WinDDK\7600.16385.1\bin\amd64

包含目錄:

C:\WinDDK\7600.16385.1\inc\api

C:\WinDDK\7600.16385.1\inc\crt

C:\WinDDK\7600.16385.1\inc\ddk

庫目錄:

C:\WinDDK\7600.16385.1\lib\win7\amd64

3)C/C++

前處理器->前處理器定義:

_AMD64_=1,AMD64=1,STD_CALL=1,WINVER=0x0501,_DEBUG =1

高階->呼叫約定:

__stdcall (/Gz)

程式碼生成-->執行庫:

選擇多執行緒除錯/MTd 或者 多執行緒除錯DLL/MDd

(主要解決_DEBUG未預定義的問題)

常規-->除錯資訊格式:用於“編輯並繼續”的程式資料庫 (/ZI)

優化:已禁用 (/Od)

4)聯結器

輸入->附加依賴項:                                                                                        ntoskrnl.lib;Hal.lib;wdm.lib;wdmsec.lib;wmilib.lib;ndis.lib;MSVCRT.LIB;LIBCMT.LIB;%(AdditionalDependencies)

輸入->忽略所有預設庫:是 (/NODEFAULTLIB)

清單檔案->啟用使用者賬戶控制(UAC):否 (/MANIFESTUAC:NO)

系統->子系統:控制檯 (/SUBSYSTEM:CONSOLE)

系統->驅動程式:驅動程式 (/Driver)

系統->堆疊保留大小:4194304(可修改)

系統->堆疊提交大小:4096  (可修改)

高階->入口點:DriverEntry

高階->基址:0x10000

高階->隨機基址:(清空)

高階->資料執行保護(dep):(清空)

        除錯->生成除錯資訊:是 (/DEBUG)

注:參考C:\WinDDK\7600.16385.1資料夾的ia64、X86等路徑,進行修改可以配置為ia64、32位系統。編譯若通過,則配置成功,並生產.sys等檔案。

六、配置WinDbg

1、設定系統字元表路徑:WinDbg->File->Symbol File Path介面中輸入

SRV*C:\WINDOWS\Symbols*http://msdl.microsoft.com/download/symbols; 並選擇Reload,WinDbg會自動下載字元表,關鍵是勾選reload。

2)設定自己生成的.sys對應的字元(Symbol)路徑:

C:\Users\shenc\Desktop\TestDriver\objchk_win7_amd64\amd64

3)設定自己生成.sys的原始碼路徑:

C:\Users\shenc\Desktop\TestDriver

注:

1)在除錯原始碼時,若發生竄碼(除錯驅動與對應的原始碼不一致),須將WinDbg軟體File選單下儲存的舊原始碼路徑刪除。

2)除錯時WinDbg預設不輸出Dbgprintf資訊,須執行ednt!Kd_DEFAULT_Mask 0x8 命令。

3)驅動執行時,按下Ctrol+Break組合鍵,進入中斷狀態,點選工具按鈕(快捷鍵:F8、F10等)可進行詳細除錯。

4)中斷除錯時,可檢視區域性變數等資訊

七、虛擬機器

1、安裝虛擬機器

若主機板預設沒有開啟虛擬化技術,則一般方法是開機或重啟時按F12鍵進入BIOS選單,將虛擬化(Virtualization)設定為Enable。

2、配置虛擬機器

1)開始-->WMware Work Stations-->雙擊“我的電腦”下的一個虛擬機器(Windows 7 x64)-->編輯虛擬機器設定-->移除印表機-->新增竄行埠

2)選中剛新增的竄行埠,在右側的對話方塊中,設定如下:

勾選 啟動時連線

選擇 使用命名的管道(N)

其下的兩個下拉框分別選擇:該端是伺服器、另一端是應用程式

八、安裝並配置虛擬機器上的Win7 64位系統

1、將Win7設定為可除錯狀態

1)以管理員身份開啟cmd命令視窗:Win + R 開啟執行輸入框,輸入cmd;或滑鼠 點選系統開始圖示,在輸入框中輸入cmd,右擊上方搜尋顯示出的cmd.exe,以管理員身份運 行。

2)依次輸入:

bcdedit /?

Bcdedit /enum OSLOADER

Bcdedit /copy {current} /d “Windows 7 Copy”

Bcdedit /debug on

Bcdedit /bootdebug on

Bcdedit /dbgsettings

Bcdedit /timeout 7

2、將Win7設定為測試版

管理員身份執行cmd命令,bcdedit /set testsigning true

3、測試證書數字簽名

以管理員身份執行64Signer V1.2.exe,點選瀏覽找到並雙機.sys檔案,點選簽名。

 4、安裝.sys檔案

以管理員身份執行執行InstDrv.exe,選擇.sys檔案,進行安裝、啟動等操作

5、檢視核心輸出資訊

以管理員身份執行Dbgview.exe,點選Capture選單,勾選上Captrue kernel項。

注:測試含有斷點的核心,否則卡機,無法進行任何操作,因此不在本地主機測試,而採用雙機除錯(即新建個虛擬機器,本地機與虛擬機器通過管道進行通訊)。

九、雙機除錯

1、原始碼中新增中斷語句

#if _DEBUG   //DBG

                 __debugbreak(); //64位

#endif

注: 1)彙編_asm int 3 中斷,在64位Win7上報錯。

2)如果未能進入斷點進行除錯,請檢查sys檔案是否在客戶機(虛擬Win7系統)安裝成功,系統字符集(Symbols)、本人字元Symbols是否下載或設定正確,管道埠是否正確等。

2、虛擬機器共享主機資料夾

方法:

1)啟動VMware,選擇虛擬機器,點選編輯虛擬機器設定;

2) 點選選項,點選共享資料夾,右側點選總是啟用,然後點選新增按鈕;

3) 輸入win7主機下需要共享的資料夾路徑,制定共享名,點選下一步;

4) 點選確定,點選虛擬機器內的計算機,在網路下就可以訪問共享的資料夾嘍。

/*

3、Windows 7系統中的驅動簽名強制要求,關閉強制驅動簽名的命令為:

bcdedit /set loadoptions DISABLE_INTEGRITY_CHECKS

使用管理員的身份開啟CMD命令列,然後輸入上面的命令,完成之後重新啟動計算機, 就可以在64位win7系統上使用未有數字簽名的驅動程式。(測試時不簽名啟動失敗!)

 */

十、NDIS中間層驅動開發

1、前言

TDI(傳輸層驅動介面)是傳輸層的過濾技術,在Windows Vista之前,常用來開發網路資料過濾,Windows Vista之後的作業系統中不再支援,取而代之的是WFP(Widows過濾平臺)技術,一套系統API和服務,其簡單穩定,但微軟沒有介紹XP等Windows Vista之前的系統如何支援WFP。NDIS Filter(網路驅動介面規範)過濾框架,即支援XP等Windows Vista之前的系統,又支援Win7等Windows Vista之前的系統;並且WDK7.1.0安裝環境下的passthru工程,就是NDIS Filter開發的示例,在其基礎上可以方便開發。passthru工程路徑如下圖:

 

2、Windows 網路架構

1)最上層是網路應用層。Socket、WinInet編寫的程式。

2)第二層是網路API層,也是系統最頂層,為應用程式提供程式設計介面,且介面協議無關性。介面的定義必須在使用者層,內部邏輯實現常在核心層。如:Windows套接字、WinInet庫 API等

3)第三層是網路API的核心實現(即第二層的核心層)。核心實現層總是以核心模式裝置驅動程式的形式體現,並且他有一個統一的責任:將上層網路請求格式化為TDI格式,並將這個格式化後的IRP傳送到下層NDIS協議驅動。

4)再下一層是NDIS協議驅動,又叫TDI傳輸器。TCP/IP等都是NDIS協議驅動。

5)最下層是NDIS小埠驅動程式,直接驅動物理網絡卡。

圖如下:

 

網路驅動模型的每一層都定義了對外的公開的公共介面,與其相連的上下層驅動模組,不需要關心其內部實現就可以很好的支援擴充套件性、完成工作。

3、NDIS中間層

為了方便對網路操作進行過濾,依據網路驅動模型內NDIS協議驅動、NDIS小埠驅動的公共介面進行擴展出來的一層。對上面的NDIS協議驅動,扮演NDIS小埠特徵的角色;對下面的NDIS小埠驅動,扮演NDIS協議特徵的角色。

圖如下:

 

注:NDIS中間層的數量理論上不限數量

協議驅動綁定了所有小埠驅動,於是能截獲所有接受到的包;而中間層驅動不僅綁定了所有小埠驅動,而且還被“所有驅動協議”繫結,因此理論上能截獲所有傳送和接受到的包。

4、NDIS中間層驅動開發示例Passthru工程

1)NDIS驅動的入口函式DriverEntry:做了初始化包裝控制代碼、註冊NDIS小埠特徵集、註冊NDIS協議特徵集、關聯NDIS兩個介面等必做工作,也可以在其中建立裝置物件、初始化分發函式表。

初始化包裝控制代碼:NdisMInitializeWrapper函式,獲得NDIS包裝控制代碼;

註冊小埠特徵:先填寫小埠特徵,再使用NdisIMRegisterLayeredMiniport函式進行註冊,輸入引數是小埠特徵、NDIS包裝控制代碼等,獲得關聯小埠的NDIS_HANDLE型別控制代碼(DriverHandle)。

註冊小埠解除安裝關聯程式:NdisMRegisterUnloadHandler函式,引數是NDIS包裝控制代碼、卸載出來程式函式控制代碼。

登出小埠:NdisIMDeregisterLayeredMiniport函式,引數是註冊小埠特徵時獲得的DriverHandle控制代碼。

註冊協議特徵:NdisRegisterProtocol函式,輸入引數是協議特徵、NDIS包裝控制代碼等,獲得關聯協議的NDIS_HANDLE型別控制代碼(ProtHandle)。

關聯兩個介面:NdisIMAssociateMiniport函式,輸入引數是DriverHandle、ProtHandle兩控制代碼,這樣小埠層和協議層,在一個不透明體中進行了關聯。

注:中間層驅動本身就是集小埠驅動、協議驅動於一體的一個混合體驅動。

2)動態繫結NIC裝置

繫結過程是由PNP管理器發起,當PNP管理器發現系統中有可用的NIC裝置時,它最終會找到所有註冊過的中間層驅動。依次呼叫它們的AddDevice函式。

注:驅動的AddDevice函式都是被NDIS庫中函式託管的。

繫結的過程中會呼叫PtBindAdapter函式,在其函式內實現了協議驅動對小埠的繫結。PtBindAdapter函式呼叫NdisAllocatepacketPoolEx函式分配用於傳送和接受資料包的緩衝池;呼叫NDISOpenAdapter函式繫結下層的NIC,本質是在NDIS的核心物件中,建立起中間層驅動和下層被繫結驅動之間的註冊函式的呼叫關係;呼叫NdisIMInitializeDeviceInstanceEx函式,在這個函式內部,呼叫中間層驅動程式的MpInitialize函式來初始化驅動的虛擬NIC。

3)小埠的初始化(MpInitialize)

通過傳入的DriverHandle控制代碼引數,驅動可以很方便地找到兩個特徵結構中的函式介面。

呼叫NdisMSetAttributesEx函式設定介面卡上下文,其第三個引數必須設定屬性值:

NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT :不對未決包進行超時處理。

            NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT:不對驅動程式維持的隊         列中的查詢和設定命令進行超時處理

NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER:告訴NDIS這是一箇中間層驅動

呼叫PtRegisterDevice函式,生成一個控制裝置物件,並設定其派遣函式。

4)中間層傳送資料包

中間層驅動要傳送網路資料包,最終都必須呼叫NDISSend/NDISSendPacket/NDISCoSendPackets這個系列的函式。以NDISSend為例,NdisSend在內部通過協議驅動的繫結控制代碼,找到所繫結的中間層驅動,並找到中間層驅動的MpSend/MpSendPackets函式呼叫。

因此可以在MpSend/MpSendPackets中對傳送的資料包直接進行處理。

(包描述符進行重利用或重申請)

MpSend返回值是NDIS_STATUS_PENDING,則表示傳送資料包的非同步完成。那麼久不能再對包描述符做任何操作了,因為已經對它失去了控制權,響應的操作應該保留到完成函式PtSendComplete中進行。

5)中間層驅動接受資料包

底層面向無連線的小埠驅動可通過下面兩種方式指示資料包接收。

方式一:小埠驅動呼叫過濾無關的NdisMindicateReceivePacket函式,向上層驅動傳遞資料包描述符指標。當上層驅動處理完畢後,將向NIC驅動程式返回那些包描述符及其所指向的資源。此方式下,如果中間層驅動提供了PtReceivePacket處理函式,則PtReceivePacket函式被呼叫;否則PtReceive函式被呼叫。

方式二:小埠驅動呼叫過濾相關的NdisMXxxindicateReceivePacket函式,傳遞包頭及資料緩衝區指標和緩衝區大小。此方式下,PtReceive函式被呼叫。

因此可以在PtReceive/PtReceivePacket中對接受的資料包直接進行處理。

上層驅動收到網路包接收通知後,會在合適的時候呼叫NdisTransferData函式來要求底層驅動將完整的包資料傳送給它。我們會在MpTransferData函式中得到上層的這個請求;但因為我們沒有完整的報資料,所以應該在這個函式中繼續把請求往底層傳遞。底層驅動如果立刻返回包資料。那麼我們在MpTransferData中即能立刻截獲到;否則在MpTransferData的非同步完成函式PtTransferDataComplete中才能截獲到完整的包內容。

因此可以在MpTransferData、PtTransferDataComplete中對資料包進行處理。

6)中間層驅動程式查詢和設定

查詢和設定,是小埠特徵回撥中兩個重要的介面:一個用來處理OID查詢請求,一個用來處理OID設定請求。Passthru中分別對應的是MPQueryInformation和MPSetInformation.

綜上,可以在MpSend/MpSendPackets,PtReceive/PtReceivePacket,MpTransferData、PtTransferDataComplete中對資料包進行處理。

5、NDIS包描述符

呼叫NdisQueryPacket函式,可以找到第一個Ndis_Buffer,然後通過NdisGetNextBuffer函式,來獲得後續的NDIS_BUFFER。

呼叫NdisQueryBufferSafe函式,可以取得Ndis_Buffer中儲存緩衝區的虛擬地址。

呼叫NdisMoveMemory函式,可以將各NDIS_BUFFER內容拷貝到指定的儲存區。

十一、安裝驅動

1、將netsf.inf、netsf_m.inf、Passthru.sys放在同一目錄下。

2、安裝

(1) 開啟“網路和共享中心”。

(2) 右擊“本地連線”或“無線網路”,選擇“屬性”。

(3) 在彈出的“本地連線 屬性”對話方塊中選中“常規”屬性頁,點選“安裝”按鈕。

(4) 在彈出的“選擇網路元件型別”對話方塊中選中“服務”,然後點選“新增”按鈕。

(5) 在彈出的“選擇網路服務”對話方塊中點選“從磁碟安裝”按鈕。

(6) 在彈出的“從磁碟安裝”對話方塊中點選“瀏覽...”按鈕。“netsf.inf”檔案,點選“開啟”按鈕,確定。

(7) 在彈出的“選擇網路服務”對話方塊中選中“Passthru”,點選“確定”按鈕。

(8) 在安裝過程中對彈出的數字簽名對話方塊都要點選“確認”按鈕。

(9) 安裝完成後,“Passthru”就出現在了元件列表中。

十二、 其他

 first.c

///
/// @file first.c
/// @author crazy_chu
/// @date2008-11-1
/// 

#include <ntddk.h>
    

// 提供一個Unload函式只是為了
VOID DriverUnload(PDRIVER_OBJECT driver)
{
    // 但是實際上我們什麼都不做,只打印一句話:
    DbgPrint("first: Our driver is unloading…\r\n");
}

// DriverEntry,入口函式。相當於main。
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
#if DBG
      // _asm int 3
#endif
    // 這是我們的核心模組的入口,可以在這裡寫入我們想寫的東西。
    // 我在這裡列印一句話。因為”Hello,world” 常常被高手恥笑,所以
    // 我們列印一點別的。
    DbgPrint("first: Hello, my salary!");

    // 設定一個解除安裝函式便於這個函式能退出。
    driver->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}
View Code

makefile

!IF 0

Copyright (C) Microsoft Corporation, 1999 - 2002

Module Name:

    makefile.

Notes:

    DO NOT EDIT THIS FILE!!!  Edit .\sources. if you want to add a new source
    file to this component.  This file merely indirects to the real make file
    that is shared by all the components of Windows NT (DDK)

!ENDIF

!INCLUDE $(NTMAKEENV)\makefile.def
View Code

sources

TARGETNAME=first
TARGETTYPE=DRIVER
SOURCES=first.c
TARGETPATH=obj
View Code