閱讀1003 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[原創]W2k Driving 學習筆記(二)使用GCC創建 Windows NT 下的內核DLL

   

     再溫習<<Windows 2000 Driving>>分層驅動程序一章的時候,看到了關於緊耦合

驅動連接方式,這種方式不依賴於I/O管理器的串聯,而是直接調用內核例程,這樣可以大

大的提高驅動的執行效率。

     為了實現這樣一種功能,必須提供一種類似於在用戶模式中DLL的機製,隻不過該"DLL"

是加載到內核中的。其實Windows NT 內核本身早就利用了這樣機製,比如Hal.dll、ntoskrnl.exe和某些類驅動程序等,他們分別為第3方驅動程序導出了HAL層、內核層、執行體層的功能函數

      在 Tim Roberts 的 《內核模式的 DLL》一文中,給出了內核DLL的主要特征:


    內核 DLL 就像用戶模式 DLL 一樣:鏈接器在構建 DLL 時生成一個導入庫,然後將此庫包含到將要使

用此DLL 的任何驅動程序的目標庫列表中。既不需要注冊表技巧,也不需要任何特別的動作來起停該 DLL。

內核 DLL將隨任何引用之的其他驅動程序自動加載,而隨最後一個引用之的驅動程序自動卸載(注 1)

 

      為了實現這樣一個DLL,Roberts給出的做法是使用DDK來生成一個TARGETTYPE為

EXPORT_DRIVER的項目我試了一下,如其所願,的確建立一個xxx.sys文件,我還

觀察了一下該sys的PE類型,發現它與標準的內核sys並無兩樣,那麼能不能用gcc來生成一

個內核模式的DLL呢?答案是肯定的!如果你正在用gcc(MinGW)在寫Windows NT 下

的Core Dll的話,那麼也可以寫出DDK中EXPORT_DRIVER類型的模塊了,具體做法從略...

 

  好了,上麵隻是開個玩笑,嗬嗬。下麵就詳細說說如何用MinGW來寫一個CoreDll:

  1. 首先在你的係統中應該安裝一個MinGW,我現在用的是(MinGW 5.1.4);
  2. 在你的係統中還要有一個Masm32V9.0+的環境(這個不是必須的,Gcc也有能力自己生成驅動程序文件,而我采用的是Masm方式),你也可以安裝WINDDK,用它的環境生成最終的的驅動文件。
  3. 用gccNTDrvFrame(注2)建立一個新的驅動包,其中如果要想實現CoreDll動態加載和卸載功能必須在sys.c中提供以下2個例程,你可以將構造和析構的相關內容放在它們裏麵。你同樣要提供一個Driver 必須包含標準的 DriverEntry 入口點,不過實際上係統不會調用它。這個需求是創建係統的人為限製,因為它會為每個內核驅動程序把 /ENTRY:DriverEntry 添加到鏈接器選項中。因此我們為隻導出的 DLL 也必須提供一個偽入口點。 :

           __declspec(dllexport) DDKAPI NTSTATUS DllInitialize(/ IN PUNICODE_STRING RegistryPath); __declspec(dllexport) DDKAPI NTSTATUS DllUnload(void);

   4.  建立一個需要導出函數,我這裏隻是示例一下,所以寫了一個簡單的導出函數:   

 

__declspec(dllexport) DDKAPI NTSTATUS GetMagicNum(IN int *pVal) { if(!pVal) { DbgPrint("err : pVal == NULL!/n"); return STATUS_DEVICE_CONFIGURATION_ERROR; } DbgPrint("*pVal is %d/n",*pVal); *pVal *= 11 * 11; DbgPrint("After OP --- *pVal is %d/n",*pVal); return STATUS_SUCCESS

 

   5. 為連接器建立def文件,內容如下:

NAME CoreDll.sys EXPORTS DllInitialize PRIVATE DllUnload PRIVATE GetMagicNum

 

    6. 運行gccNTDrvFrame的builder,完成CoreDll的編譯和連接,如果沒有錯誤將會生

      成若幹個文件,其中隻會用到2個:一個是xxx.sys,這個不用說就是供其它驅動調用

      的CoreDll,另一個是xxx.lib,它是所有需要調用xxx.sys的其它驅動程序建立時需要

      的文件,它和hal.lib、ntoskrnl.lib完全一樣。

 

   7. 新建另一個Driver工程,在b.bat中的link連接選項中添加xxx.lib。聲明和調用外部

       函數的方法如下:

       __declspec(dllimport) DDKAPI NTSTATUS GetMagicNum(IN int *pVal); DDKAPI NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,/ PUNICODE_STRING pRegistryPath) { PDEVICE_OBJECT pDevObj; NTSTATUS status = STATUS_DEVICE_CONFIGURATION_ERROR; PRINT("[%s]enter DriverEntry.../n",__func__); RtlInitUnicodeString(&DevName,DEVNAMEW); RtlInitUnicodeString(&SymlnkName,SYMLNKNAMEW); if(IoCreateDevice(pDriverObject,0,&DevName,FILE_DEVICE_UNKNOWN,/ 0,false,&pDevObj) != STATUS_SUCCESS) { PRINT("[%s]IoCreateDevice Failed!/n",__func__); goto QUIT; } if(IoCreateSymbolicLink(&SymlnkName,&DevName) != STATUS_SUCCESS) { IoDeleteDevice(pDevObj); PRINT("[%s]IoCreateSymbolicLink Failed!/n",/ __func__); goto QUIT; } pDevObj->Flags |= DO_BUFFERED_IO; pDriverObject->DriverUnload = DriverUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = / DrvDispatchCreateClose; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = / DrvDispatchCreateClose; pDriverObject->MajorFunction[IRP_MJ_READ] = / DispatchRead; pDriverObject->MajorFunction[IRP_MJ_WRITE] = / DispatchWrite; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = / DrvDispatchControl; pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = / DrvShutdown; if(!NT_SUCCESS(IoRegisterShutdownNotification(/ pDriverObject->DeviceObject))) { PRINT("[%s]err : Register Shutdown Failed!/n",/ __func__); } int i = 11; GetMagicNum(&i); PRINT("[%s]Addr of KeServiceDescriptorTable : %p/n",/ __func__,KeServiceDescriptorTable); PRINT("[%s]leave DriverEntry.../n",__func__); status = STATUS_SUCCESS; QUIT: return status; }

       你當然不一定非要在DriverEntry中調用,因為xxx.sys由係統在調用sys之前自動

       動態加載.

 

         8. 用builder建立這個sys,然後整個過程結束了。

 

   結束語:這裏隻是一個最簡單的示例,如果是隻幹這樣的"小"事而用CoreDll的話,未免

殺雞用牛刀的意思。CoreDll的用途可以被極大的拓展,至於如何發揮您的激情與才幹,就

不是我力所能及的了。(^o^)

 

注1 :不過,在 Windows 98 第二版或者 Windows Me 中內核 DLL 永遠也不會卸載。

注2 gccNTDrvFrame是我寫的一個gcc建立windows NT 驅動的框架包,稍後會在新文章中介紹。

 

(PS : 文章參考https://sluttery.spaces.live.com/blog/cns!3569FEA80C717FD4!519.entry

 

最後更新:2017-04-02 00:06:42

  上一篇:go 存儲過程與函數的區別
  下一篇:go Linux之間傳送文件的SCP命令