Kernel Mode Driver Framework >> Pascal (PNP)
DriverEntry()
C/C++程式的進入點是main(),而對於KMDF驅動程式而言,它的進入點則是DriverEntry(),如果沒有export DriverEntry(),系統將無法正確載入驅動程式。
DriverEntry()定義如下:
function __DriverEntry(:PDRIVER_OBJECT; :PUNICODE_STRING):NTSTATUS; stdcall;
系統透過呼叫DriverEntry()載入驅動程式並傳入兩個參數,第一個參數PDRIVER_OBJECT是指向該驅動程式的資料位置,該位置會包含驅動程式的所有資訊,這些資訊並非全部都提供給使用者使用,有些欄位是Undocument的,保留給系統調度使用,Microsoft可能隨系統不同而修改,因此,建議使用者不要使用這些Undocument欄位的資料。而另一個參數PUNICODE_STRING則是該驅動程式的Registry位置,驅動程式在安裝時,系統都會產生一個註冊表項目(位於CurrentControlSet\Services\),該項目就是當Windows系統啟動時,用來載入驅動程式使用的,Windows系統依據註冊表來決定哪些驅動程式需要被載入以及載入的順序,因此,隨意修改註冊表,可能會導致驅動程式無法被正確載入,嚴重時,可能無法啟動系統。
使用者需要注意的是,就算有多個相同的裝置(如:兩個一樣型號的USB滑鼠插入電腦),系統只會在發現第一個裝置時,載入該驅動程式並產生一份Driver Object,之後發現的裝置,系統還是使用那份已經產生的Driver Object,使用者可能很好奇,相同裝置使用同一份Driver Object資料?那每個裝置的驅動程式資料不就會亂掉嗎?答案是:不會的,因為每個裝置必須Create自己的Device Object(跟Driver Object是不一樣的東西),每個裝置的資料必須存在自己Create的Device Object中,因此,每個Device Object各代表不同的裝置。
那在DriverEntry()副程式,需要做哪一些事情呢?
KMDF驅動程式 | WDM驅動程式 | Legacy驅動程式 |
---|---|---|
基於WDF架構,需要產生WDF專用的Driver Object並且設定AddDevice Callback副程式,值得注意的是,其餘Callback副程式已經改成在AddDevice()做設定 | 需要註冊使用到的Callback副程式,如:AddDevice()、DriverUnload()等 | 除了需要註冊Callback副程式以外,還要產生Device Object,因為Legacy驅動程式沒有AddDevice() Callback,所以需要在DriverEntry()產生Device Object |
範例:
function __DriverEntry(pOurDriver:PDRIVER_OBJECT; pOurRegistry:PUNICODE_STRING):NTSTATUS; stdcall; var config: WDF_DRIVER_CONFIG; begin WDF_DRIVER_CONFIG_INIT(@config, AddDevice); config.EvtDriverUnload:= DriverUnload; WdfDriverCreate(pOurDriver, pOurRegistry, WDF_NO_OBJECT_ATTRIBUTES, @config, WDF_NO_HANDLE); Result:= STATUS_SUCCESS; end;
基本上,PNP類型的驅動程式不需設置DriverUnload Callback副程式,除非需要在DriverEntry()和DriverUnload()配置或刪除資源,因為PNP類型的驅動程式主要以AddDevice()為主要入口,資源的配置應該改在這個地方,需要注意DriverEntry()的回傳值部份,因為回傳值會決定載入驅動程式的成功或失敗,另外,除了DriverEntry()名稱必須固定以外,其餘Callbak的名稱可以隨意命名。
不知使用者有無發現DriverEntry()為何要使用兩個底線(變成__DriverEntry())?這是因為FxDriverEntryWorker()無法找到對應的DriverEntry(),加上dcc32.exe缺少Buffer Security Check編譯選項,因此,司徒只好使用MASM32寫了一個Loader,當作中間媒介,WDF呼叫Loader的DriverEntry(),最後在由Loader的DriverEntry()呼叫Pascal的_DriverEntry(),而因為是Export "C",所以Pascal的DriverEntry()才會變成__DriverEntry(),Loader程式碼如下:
.386p .model flat, stdcall option casemap:none include c:\masm32\include\w2k\ntdef.inc include c:\masm32\include\w2k\ntstatus.inc include c:\masm32\include\w2k\ntddk.inc include c:\masm32\include\w2k\ntoskrnl.inc include c:\masm32\include\w2k\ntddkbd.inc include c:\masm32\include\wdf\umdf\1.9\wudfddi_types.inc include c:\masm32\include\wdf\kmdf\1.9\wdf.inc include c:\masm32\include\wdf\kmdf\1.9\wdftypes.inc include c:\masm32\include\wdf\kmdf\1.9\wdfglobals.inc include c:\masm32\include\wdf\kmdf\1.9\wdffuncenum.inc include c:\masm32\include\wdf\kmdf\1.9\wdfobject.inc include c:\masm32\include\wdf\kmdf\1.9\wdfdevice.inc include c:\masm32\include\wdf\kmdf\1.9\wdfdriver.inc includelib c:\masm32\lib\wxp\i386\BufferOverflowK.lib includelib c:\masm32\lib\wdf\kmdf\i386\1.9\wdfldr.lib includelib c:\masm32\lib\wdf\kmdf\i386\1.9\wdfdriverentry.lib public DriverEntry extern _DriverEntry:proc DriverEntry proc pOurDriver:PDRIVER_OBJECT, pOurRegistry:PUNICODE_STRING push pOurRegistry push pOurDriver call _DriverEntry ret DriverEntry endp end DriverEntry .end
只要編譯成Loader.obj,最後在跟Pascal的main.obj做Link即可。