Windows驅動開發(中間層)


Windows驅動開發

一、前言

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

 

二、初步環境

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

地址:https://msdn.microsoft.com/en-us/windows/hardware/hh852365

 

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

 

 


免責聲明!

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



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