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.defView Code
sources
TARGETNAME=first TARGETTYPE=DRIVER SOURCES=first.c TARGETPATH=objView Code