摘 要:WDF是微軟推出的下一代驅(qū)動(dòng)程序開(kāi)發(fā)模型,它所提供的KMDF框架為內(nèi)核模式驅(qū)動(dòng)開(kāi)發(fā)提供了一個(gè)面向?qū)ο?、事件?qū)動(dòng)的開(kāi)發(fā)框架,它隔離了設(shè)備驅(qū)動(dòng)程序與操作系統(tǒng)內(nèi)核,降低了驅(qū)動(dòng)程序?qū)?nèi)核的影響。濾器驅(qū)動(dòng)程序是一類(lèi)中間驅(qū)動(dòng)程序。根據(jù)其在驅(qū)動(dòng)程序堆棧中所處位置的不同,它可以分為上層過(guò)濾器驅(qū)動(dòng)程序和下層過(guò)濾器驅(qū)動(dòng)程序兩種。過(guò)濾器驅(qū)動(dòng)程序可以監(jiān)視、攔截和修改IRP流,在不影響已有驅(qū)動(dòng)程序功能的前提下增加一些附加功能。本文深入研究了WDF驅(qū)動(dòng)模型和過(guò)濾器驅(qū)動(dòng)技術(shù),設(shè)計(jì)和實(shí)現(xiàn)了一個(gè)針對(duì)我司自主研發(fā)的USBCAN設(shè)備“BULKDevice”的數(shù)據(jù)監(jiān)控系統(tǒng)。并通過(guò)一個(gè)簡(jiǎn)單的實(shí)例介紹了基本編程技巧。
關(guān)鍵詞:WDF;過(guò)濾驅(qū)動(dòng)程序;監(jiān)控
中圖分類(lèi)號(hào):TP311.1
隨著CAN總線網(wǎng)絡(luò)在汽車(chē)電子行業(yè)中的普及,成為國(guó)際上應(yīng)用最廣泛、最有前途的現(xiàn)場(chǎng)總線之一。USBCAN越來(lái)越大量的應(yīng)用到各個(gè)項(xiàng)目研發(fā)中去,但每一套USBCAN都要付費(fèi)購(gòu)買(mǎi)相應(yīng)的licenses及其配套軟件,這造成了項(xiàng)目巨量的開(kāi)銷(xiāo)。在自主研發(fā)了USBCAN設(shè)備之后,相應(yīng)的配套監(jiān)控調(diào)試軟件也亟待解決。
過(guò)濾驅(qū)動(dòng)程序可以修改已有驅(qū)動(dòng)的功能,也可以對(duì)數(shù)據(jù)進(jìn)行過(guò)濾加密。在不影響USB設(shè)備的正常數(shù)據(jù)通訊情況下,采用過(guò)濾驅(qū)動(dòng)技術(shù),將之附于功能驅(qū)動(dòng)的下層,獲取USB設(shè)備輸入輸出的數(shù)據(jù)流,并將其分類(lèi)顯示出來(lái),達(dá)到監(jiān)控的目的。
1 WDF模型
自Windows 2000開(kāi)始,開(kāi)發(fā)驅(qū)動(dòng)程序均以WDM(Windows Driver Model)為基礎(chǔ),是一個(gè)標(biāo)準(zhǔn)的驅(qū)動(dòng)模型,用戶可以在這個(gè)模型上有所改動(dòng)。
WDF是微軟推出的新一代驅(qū)動(dòng)開(kāi)發(fā)模型,全稱為Windows Driver Foundation,以WDM為基礎(chǔ)進(jìn)行了建模和封裝,顯著特點(diǎn)是降低了開(kāi)發(fā)難度[1]。此模型比WDM更先進(jìn)、合理,將WDF中關(guān)于電源、PnP等一些復(fù)雜的細(xì)節(jié)由微軟實(shí)現(xiàn),所以在此模型上開(kāi)發(fā)驅(qū)動(dòng)比以前要簡(jiǎn)單。
WDF改變了操作系統(tǒng)內(nèi)核與驅(qū)動(dòng)程序之間的關(guān)系,WDM驅(qū)動(dòng)程序中,一方面要處理硬件,另一方面要處理驅(qū)動(dòng)程序與操作系統(tǒng)內(nèi)核的交互?,F(xiàn)在WDF則將驅(qū)動(dòng)程序與操作系統(tǒng)內(nèi)核之間進(jìn)行了分離,驅(qū)動(dòng)程序與操作系統(tǒng)交互工作交給框架內(nèi)封裝的方法(函數(shù))完成,這樣驅(qū)動(dòng)開(kāi)發(fā)者只需專注處理硬件的行為即可。這不僅避免了顧此失彼兩面不周的弊端,也由于雙方的分離,對(duì)操作系統(tǒng)內(nèi)的某些改動(dòng),硬件制造商配套驅(qū)動(dòng)程序的開(kāi)發(fā)都有莫大的好處。
WDF一方面能兼容WDM,或者說(shuō)是能輕松切換到WDM,不至于使程序員代碼編寫(xiě)了一大半時(shí),突然發(fā)現(xiàn)某個(gè)只能用WDM才能解決的問(wèn)題,而頭痛不已;另一方面WDF也對(duì)舊的NT內(nèi)核的操作系統(tǒng)兼容,這使得一個(gè)驅(qū)動(dòng)能同時(shí)支持win2000、XP、Vista、Win7這些系統(tǒng),而不用在代碼中進(jìn)行判斷,更不用給每個(gè)系統(tǒng)各寫(xiě)一個(gè)驅(qū)動(dòng)。
WDF包含兩套子框架:KMDF(內(nèi)核驅(qū)動(dòng))和UMDF(用戶驅(qū)動(dòng)),大部分情況,WDF僅指KMDF[2]。本文所述的程序是在內(nèi)核模式框架下研發(fā)的。無(wú)論內(nèi)核模式的驅(qū)動(dòng)程序或者用戶模式的驅(qū)動(dòng)程序,都使用同一環(huán)境進(jìn)行構(gòu)建,這一環(huán)境稱為WDK;都采用同一套對(duì)象模型構(gòu)建,采用同一個(gè)基礎(chǔ)承載,這個(gè)基礎(chǔ)就是WDF。
2 過(guò)濾驅(qū)動(dòng)技術(shù)原理
過(guò)濾驅(qū)動(dòng)無(wú)處不在。通常它被稱作Hook,是一種Hack手段;很多時(shí)候它又是必不可少的,這種技術(shù)甚至被操作系統(tǒng)自己使用。
過(guò)濾驅(qū)動(dòng)程序有兩種:一種是高層過(guò)濾驅(qū)動(dòng)程序(High FDO),處于功能驅(qū)動(dòng)(FDO)之上;一種是低層過(guò)濾程序,處于功能驅(qū)動(dòng)之下,總線驅(qū)動(dòng)之上。WDF根據(jù)設(shè)備對(duì)象堆棧來(lái)完成驅(qū)動(dòng)程序的分層,過(guò)濾驅(qū)動(dòng)程序則通過(guò)過(guò)濾流經(jīng)它的IRP來(lái)捕捉、修改或者攔截設(shè)備傳輸?shù)臄?shù)據(jù)。
WDF模型將原先WDM模型里的IRP進(jìn)行了封裝,成為WDFREQUEST對(duì)象,表示一個(gè)I/O請(qǐng)求。WDF還提供了另外一個(gè)對(duì)象也就是WDFQUEUE,用來(lái)存放IRP請(qǐng)求。
如果將過(guò)濾程序附在功能驅(qū)動(dòng)(FDO)的下面,這樣介于FDO和PDO之間的過(guò)濾驅(qū)動(dòng)稱為低層過(guò)濾驅(qū)動(dòng)程序,一般記為L(zhǎng)ower-level FiDO。如果被附在功能驅(qū)動(dòng)(FDO)的上面,則稱上層過(guò)濾驅(qū)動(dòng)程序,一般記為Upper-level FiDO[3]。
在WMF框架中的過(guò)濾驅(qū)動(dòng)可以相互嵌套,層層疊加,即上層過(guò)濾驅(qū)動(dòng)程序之上可以再附加更高層的過(guò)濾驅(qū)動(dòng)程序,同理低層過(guò)濾驅(qū)動(dòng)程序可以被更低層的過(guò)濾驅(qū)動(dòng)所過(guò)濾。
過(guò)濾驅(qū)動(dòng)可以在設(shè)備棧的任何層次中插入。I/O管理器發(fā)出的請(qǐng)求將會(huì)沿著圖1的順序從上往下傳遞并返回。因此,我們可以使用過(guò)濾驅(qū)動(dòng)程序來(lái)檢查、修改、完成它接收到的IRP,或者構(gòu)造自己的IRP。
圖1 設(shè)備對(duì)象和驅(qū)動(dòng)程序?qū)ο蠓謱咏Y(jié)構(gòu)圖
3 系統(tǒng)實(shí)現(xiàn)
3.1 系統(tǒng)結(jié)構(gòu)
本文所述的監(jiān)視程序所用的過(guò)濾驅(qū)動(dòng)即為下層過(guò)濾驅(qū)動(dòng),處于功能驅(qū)動(dòng)和總線驅(qū)動(dòng)之間,數(shù)據(jù)在傳送過(guò)程中必然會(huì)經(jīng)過(guò)過(guò)濾驅(qū)動(dòng),因此過(guò)濾驅(qū)動(dòng)可以捕獲這些數(shù)據(jù),并實(shí)時(shí)顯示出來(lái),達(dá)到監(jiān)控的目的。
下層過(guò)濾驅(qū)動(dòng)掛接一個(gè)過(guò)濾器對(duì)象到目標(biāo)設(shè)備對(duì)象上,即功能設(shè)備驅(qū)動(dòng)對(duì)象Fdo,目標(biāo)設(shè)備與總線驅(qū)動(dòng)對(duì)象之間任何交互的IRP流都會(huì)被過(guò)濾驅(qū)動(dòng)捕獲,并發(fā)給監(jiān)視系統(tǒng)分類(lèi)顯示。
圖2 監(jiān)視系統(tǒng)數(shù)據(jù)流架構(gòu)
3.2 模塊功能實(shí)現(xiàn)
3.2.1 設(shè)備識(shí)別
設(shè)備的識(shí)別是通過(guò)檢查接入設(shè)備所加載驅(qū)動(dòng)的名稱來(lái)完成的:過(guò)濾驅(qū)動(dòng)截獲設(shè)備接入時(shí)的PnP請(qǐng)求, 即可獲取接入設(shè)備的DriverObject結(jié)構(gòu),其中的DriverName項(xiàng)就是設(shè)備加載的驅(qū)動(dòng)名稱。
deviceObject=WdfDeviceWdmGetDeviceObject(Device);
driverName=DriverObject->DriverName.buffer;
DriverName項(xiàng)的值為“BULK Device”時(shí),即為本文所需要監(jiān)視的設(shè)備。
3.2.2 入口函數(shù)
WDF封裝了大部分WDM接口,保留了入口函數(shù)的名稱DriverEntry,但是大大簡(jiǎn)化了入口函數(shù)的代碼書(shū)寫(xiě)量。WDF模型里實(shí)現(xiàn)一個(gè)接口函數(shù),完成驅(qū)動(dòng)程序的默認(rèn)初始化。用戶只需在默認(rèn)初始化的基礎(chǔ)上,增加自定義的初始化;如果沒(méi)有,WDF的默認(rèn)初始化也能保證驅(qū)動(dòng)程序的正常穩(wěn)定運(yùn)行。代碼如下:
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath ) ;
WDF_DRIVER_CONFIG config;
3.2.3 設(shè)備綁定
進(jìn)行過(guò)濾的首要的步驟是對(duì)一個(gè)設(shè)備對(duì)象進(jìn)行綁定。本文所述過(guò)濾驅(qū)動(dòng)會(huì)生成一個(gè)虛擬的設(shè)備對(duì)象,并“綁定”在真實(shí)的設(shè)備上。一旦綁定,發(fā)送給真實(shí)設(shè)備的請(qǐng)求,也會(huì)發(fā)送到這個(gè)虛擬設(shè)備上。
生成的過(guò)濾設(shè)備對(duì)象函數(shù)例程:
status=IoCreateDevice (driver,0,‘FilterBulkDevice”,p->DeviceType,0,F(xiàn)ALSE,fltobj);
p->DeviceType設(shè)備類(lèi)型要和被綁定設(shè)備一致,本例綁定設(shè)備是自定義設(shè)備,值為0xFF。
綁定函數(shù)的例程:
status=IoAttachDeviceToDeviceStackSafe(sourceDevice,targetDevice,attached_device);
sourceDevice是過(guò)濾驅(qū)動(dòng)生成的用來(lái)過(guò)濾的虛擬設(shè)備,本例為“FilterBulkDevice”,targetDevice是被綁定的目標(biāo)設(shè)備,本例為“BulkDevice”。
3.2.4 過(guò)濾數(shù)據(jù)功能實(shí)現(xiàn)
過(guò)濾驅(qū)動(dòng)對(duì)數(shù)據(jù)流進(jìn)行過(guò)濾的函數(shù)為InternalDeviceControl(WDFREQUEST Request),InternalDeviceControl函數(shù)根據(jù)捕獲到的WFDREQUEST對(duì)象,可獲知該WDFREQUEST的功能代碼。根據(jù)該功能代碼可知此次I/O請(qǐng)求數(shù)據(jù)是批量傳輸還是中斷傳輸,是輸入請(qǐng)求還是輸出請(qǐng)求,并因此分類(lèi)存儲(chǔ)于自身驅(qū)動(dòng)的buffer中;每個(gè)WDFREQUEST對(duì)象獲取完后,調(diào)用函數(shù)WdfRequestSend(Request)將這個(gè)WDFREQUEST對(duì)象原封不動(dòng)地傳遞到下一層驅(qū)動(dòng)即可。
本文所監(jiān)控設(shè)備采用三個(gè)管道(Pipe5、Pipe6、Pipe7)和PC進(jìn)行數(shù)據(jù)交互,因此獲取的功能碼應(yīng)分別是輸入碼0x85(批量傳輸),輸入碼0x86(中斷傳輸),輸出碼0x87(中斷傳輸)。
//I/O請(qǐng)求完成例程
NTSTATUS FilterBULKDevice::InternalDeivceControl(WDFREQUEST Request)
{
NTSTATUS status;
PREQUEST_CONTEXT pRequestContext;
PVOID buffer;
BOOLEAN ret;
INT I,buffer_len,res_len;
FilterBULKDevice_EvtRequestIoctlCompletionRoutine(IN WDFREQUEST Request,IN WDFIOTARGET Target,PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,IN WDFCONTEXT Context
)
//判斷獲取的IO請(qǐng)求是否是目標(biāo)設(shè)備發(fā)出,或是發(fā)給目標(biāo)設(shè)備
If(Request!=Target)
{
WdfRequestSend(Request)
}
Else
{
pRequestContext=GetRequestContext(Request);
status=WdfRequestRetrieveOutputBuffer(Request,20,buffer,buffer_len);
If(!NT_SUCCESS(status))
{
WdfRequestComplete(Request,STATUS_UNSUCCESSFUL);
return;
}
}
res_len=strlen(pRequestContext->cout)+1;
//獲取足夠大的緩沖區(qū)
status=WdfRequestRetrieveOutputBuffer(Request,res_len,buffer,NULL);
if(!NT_SUCCESS(status))
{
WdfRequestComplete(Request,STATUS_UNSUCCESSFUL);
return;
}
//獲取數(shù)據(jù)并保存至緩沖區(qū)
Strncpy((PCHAR)buffer,pRequestContext->cout,res_len);
status=WdfRequestRetrieveInputBuffer(Request,1,buffer,NULL);
if(!NT_SUCCESS(status))
{
WdfRequestComplete(Request,STATUS_UNSUCCESSFUL);
return;
}
strncpy((PCHAR)buffer,pRequestContext->rd,1);
//將IO請(qǐng)求再次傳遞下去,并在請(qǐng)求發(fā)送到下一層驅(qū)動(dòng)前,做相應(yīng)的數(shù)據(jù)格式化處理
WdfRequestFormatRequestUsingCurrentType(Request);
//設(shè)置IO完成例程
WdfRequestSetCompletionRoutine(Request,F(xiàn)ilterBULKDevice_EvtRequestIoctlCompletionRoutine,NULL);
WdfRequestSetCompletionRoutine(Request,F(xiàn)_REQUEST_COMPLETION_ROUTINE,pRequestContext)
//發(fā)送IO請(qǐng)求至下一層驅(qū)動(dòng)
ret=WdfRequestSend(Request,Target,WDF_NO_SEND_OPTIONS);
if(!ret)
{
status=WdfRequestGetStatus(Request);
WdfRequestComplete(Request,status);
}
return;
}
說(shuō)明:獲取功能碼標(biāo)識(shí)為中斷傳輸?shù)臄?shù)據(jù)包長(zhǎng)為32,獲取功能碼標(biāo)識(shí)為中斷傳輸?shù)臄?shù)據(jù)包長(zhǎng)為64。
3.3 加載過(guò)濾驅(qū)動(dòng)及數(shù)據(jù)傳遞
監(jiān)控系統(tǒng)分為兩部分:一部分主要用于動(dòng)態(tài)加載卸載過(guò)濾驅(qū)動(dòng)程序及傳遞驅(qū)動(dòng)程序所需的重要參數(shù)。另一部分主要用于實(shí)時(shí)監(jiān)視接收過(guò)濾驅(qū)動(dòng)程序得到監(jiān)視數(shù)據(jù).并將其格式化后在顯示在應(yīng)用程序。
本文所述監(jiān)控系統(tǒng)采用動(dòng)態(tài)加載過(guò)濾驅(qū)動(dòng)的方法:使用服務(wù)控制程序(SCP),其執(zhí)行對(duì)服務(wù)程序(SP)的開(kāi)啟、控制和狀態(tài)查詢功能,在應(yīng)用程序里調(diào)用API實(shí)現(xiàn)。加載例程如下:
HANDLE hSCManager;
HANDLE hService;
SERVICE_STATUS ss;
//開(kāi)啟服務(wù)控制管理器SCM,SCM維護(hù)著注冊(cè)表中的服務(wù)數(shù)據(jù)庫(kù),在注冊(cè)表中數(shù)據(jù)庫(kù)的位置為:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services。它包括很多子鍵,每個(gè)子鍵的名字就代表一個(gè)對(duì)應(yīng)的服務(wù)。
hSCManager=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
if(hSCManager)
{
//獲取服務(wù)對(duì)象的句柄,本文所述的服務(wù)對(duì)象句柄為Filter。此函數(shù)顧名思義產(chǎn)生一個(gè)新的Service,即將過(guò)濾驅(qū)動(dòng)程序作為一個(gè)系統(tǒng)服務(wù)動(dòng)態(tài)運(yùn)行
hService=CreateService(hSCManager,
“Filter”,
“FilterBulkDevice”,SERVICE_START|DELETE|SERVICE_STOP,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_IGNORE,“c:\WINDOWS\SYSTEM32\FilterBulkDevice.sys”,NULL,NULL,NULL,NULL,NULL);
}
If(!hService)
{
//為服務(wù)對(duì)象“Filter”開(kāi)啟服務(wù),加載過(guò)濾驅(qū)動(dòng)程序
hService=OpenService(hSCManager,“FilterBulkDevice”,SERVICE_ALL_ACCESS)
}
…
//使用CreateFile函數(shù)打開(kāi)FilterBULKDevice驅(qū)動(dòng),獲取句柄m_hDevice = CreateFile(“\\\\.\\FilterBULKDevice”,GENERIC_READ|GENERIC_WRITE,F(xiàn)ILE_SHARE_READ|FILE_SHARE_WRITE,OPEN_EXISTING,F(xiàn)ILE_ATTRIBUTE_NORMAL,NULL);
//使用DeviceIoControl函數(shù)與過(guò)濾驅(qū)動(dòng)進(jìn)行數(shù)據(jù)交互,存儲(chǔ)數(shù)據(jù)的緩沖區(qū)要能用于記錄一次讀取操作的數(shù)據(jù)長(zhǎng)度。
res=DeviceIoControl(m_hDevice,IO_GET_SHAREMEMORY_ADDR,NULL,NULL,szOutputBuffer,sizeof(szOutputBuffer),dwReturn,NULL);
//卸載時(shí)要先通知過(guò)濾驅(qū)動(dòng)清理交互參數(shù)所占內(nèi)存,然后使用API函數(shù)DeleteService()動(dòng)態(tài)卸載過(guò)濾驅(qū)動(dòng)程序。
DeviceloControl(m_hDevice,IO_CLEAN_SHAREMEMORY_ADDR,NULL,0,NULL,0,dwReturn,NULL);//清理用于記錄數(shù)據(jù)的地址。
hSCManager=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);DeleteService(hService)
4 結(jié)束語(yǔ)
WDF提供了比WDM更高層次抽象的高度靈活、可擴(kuò)展、可診斷的驅(qū)動(dòng)程序框架。WDF框架管理了大多數(shù)與操作系統(tǒng)相關(guān)的交互,實(shí)現(xiàn)了公共的驅(qū)動(dòng)程序功能,隔離了設(shè)備驅(qū)動(dòng)程序與操作系統(tǒng)內(nèi)核,降低了驅(qū)動(dòng)程序?qū)?nèi)核的影響。鑒于WDF眾多優(yōu)良的性能特點(diǎn),使它成為逐漸替代WDM的新一代驅(qū)動(dòng)模型。本項(xiàng)目選用自主研發(fā)的BULKDevice設(shè)備作為監(jiān)控目標(biāo)。在設(shè)計(jì)實(shí)現(xiàn)過(guò)程中,本人感觸最為深刻的是對(duì)WDF驅(qū)動(dòng)模型和過(guò)濾驅(qū)動(dòng)的學(xué)習(xí)和使用。過(guò)濾驅(qū)動(dòng)的應(yīng)用非常廣泛,要全面掌握并靈活應(yīng)用并非易事,因此對(duì)于應(yīng)用軟件研發(fā)人員來(lái)說(shuō)非常值得學(xué)習(xí)研究。雖然本項(xiàng)目所針對(duì)的USB設(shè)備接口專有性比較強(qiáng),但是舉一反三,稍加改動(dòng)即可不僅用于監(jiān)控其他設(shè)備,甚至是攔截改動(dòng)數(shù)據(jù)。另外在初次接觸驅(qū)動(dòng)程序的時(shí)候,缺乏開(kāi)發(fā)經(jīng)驗(yàn),雖然開(kāi)發(fā)出來(lái)的程序可以順利運(yùn)行,但是設(shè)計(jì)方式和源碼實(shí)現(xiàn)可能談不上最優(yōu);而且本程序的測(cè)試時(shí)間比較短,估計(jì)在某些方面還存在一些小的bug,仍有待改進(jìn)。
參考文獻(xiàn):
[1]武安河.Windows設(shè)備驅(qū)動(dòng)程序WDF開(kāi)發(fā)[M].北京:電子工業(yè)出版社,2009.
[2]李正平,徐超,陳軍寧.WDF設(shè)備驅(qū)動(dòng)程序的設(shè)計(jì)與實(shí)現(xiàn)[M].計(jì)算機(jī)技術(shù)與發(fā)展,2007(05).
[3]徐敏,劉字峰,王衍波.基于過(guò)濾驅(qū)動(dòng)的鍵盤(pán)嗅探器的設(shè)計(jì)與實(shí)現(xiàn)[C].中國(guó)電子學(xué)會(huì)第十四屆信息論學(xué)術(shù)年會(huì),2007,12,15:18
作者簡(jiǎn)介:蔡旸(1977-),女,湖北宜城人,軟件研發(fā)工程師,主管工程師,工學(xué)碩士,研究方向:汽車(chē)電子軟件研發(fā)。
作者單位:東風(fēng)電子科技股份有限公司,上海 201114