Windows內核開發-2-開始內核開發-2-內核開發入門

Windows內核開發-2-開始內核開發-2-

第一個驅動程序:

直接採用vs2019中的Empty WDM Driver 模塊建立:程序員

 

 

初始的項目文件夾中有一個Driver Files裏面會有一個.inf的文件,沒用直接刪除就好,而後在源文件裏面建立一個.cpp的源文件。windows

DriverEntry和Unload Routines

DriverEntry:

每一個驅動都有一個入口點,叫作DriverEntry,就比如日常寫的C/C++代碼裏面的main函數。DriverEntry是由一個叫作IRQL_PASSIVE_LEVEL(0)的系統進程調用出來的。DriverEntry函數原型:函數

NTSTATUS
DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING
RegistryPath);

代碼原型裏的_In _是源代碼註釋語言(SAL)中的一部分,用來描述函數如何使用其參數,SAL對於編譯器來講能夠直接忽略,可是對程序員頗有幫助。工具

相關連接:Understanding SAL | Microsoft Docs學習

這裏的最小的DriverEntry示例能夠只返回一個狀態,好比:測試

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
return STATUS_SUCCESS;
}

該DriverEntry函數包含在<ntddk.h>頭文件中,可是添加了頭文件後仍然是編譯失敗的,由於編譯器會把警告當場錯誤來報錯,可是不建議刪除該功能,由於有時候警告就是會致使錯誤誕生:spa

 

 

能夠對應修改這些警告,好比這裏將形參刪除,可是這樣僅對於C++好用,由於C++有函數重載,因此這裏用不上。這裏有一個很經典的解決辦法,就是採用一個宏函數:命令行

UNREFERENCED_PARAMETER();

這樣就能夠暫時解決掉前面的報錯說形參沒有使用了:debug

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);

return STATUS_SUCCESS;
}

可是這樣仍然不行:3d

 

 

能夠很明顯得看出,編譯器沒問題,可是Linker連接器出了問題,DriverEntry是一個C函數,必須用C的linker來link,可是這裏咱們採用的是C++的默認,因此必須給該函數設置爲C的默認LInker才行

extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);

return STATUS_SUCCESS;
}

這樣最簡單的驅動程序的源代碼就寫好了,就至關於C語言中的:

#include<stdio.h>

int main()
{

return 0;
}

Unload Routines:

這是一個驅動的卸載函數,就至關於C++中類的析構函數同樣,當驅動被卸載的時候就會自動調用該函數。在DriverEntry函數中建立的東西須要由Unload Routines來釋放,這就很是像C++類中的構造函數和析構函數的關係了。若是沒有該函數來釋放驅動加載時所開闢的內容就會致使泄露,直到下次電腦重啓時內核產生的泄露才會清楚。

該函數的函數指針,必須在DriverEntry給DriverEntry的參數DriverObject中的DriverUnload字段賦值才行。Unload函數和DriverEntry函數同樣都須要接受一個_In _ PDRIVER_OBJECT DriverObject 參數,可是Unload函數不須要返回值,直接用void 定義就好。

好比:

#include <ntddk.h>

void SampleUnload(_In_ PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
}

extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
DriverObject->DriverUnload = SampleUnload;
UNREFERENCED_PARAMETER(RegistryPath);

return STATUS_SUCCESS;
}

就是一個很是簡單可是能夠用的驅動了。

部署驅動程序Deploying the Driver

前面已經寫好了一個驅動程序,可是咱們還須要把它跑起來,驅動程序不像平時寫的普通程序同樣,採用IDE就能夠正常使用了,須要將其加載到系統裏面,一般爲了不風險,採用虛擬機來部署驅動程序。

安裝驅動程序就像是在User用戶態安裝服務.exe同樣,須要調用CreateService API或者採用現有的工具,這裏採用比較經常使用的Sc.exe來進行部署內核驅動。

註冊驅動

採用管理員權限的命令行:

sc create sample type=kernel binPath=C:\DriverTest\MyDriver3.sys

其中 sample是建立的名字,而後type表示建立的權限,binPath後面的是驅動的路徑。若是沒問天會彈出一個成功的標識符。

而且能夠在註冊表裏面查到:

使用Win+R的彈出框裏面輸入regedit.exe,查看路徑\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\sample,這就是咱們剛剛寫好且用sc創造的驅動:

 

 

啓動驅動

前面是註冊了該驅動,還得使用它。這裏也和User用戶態的服務Service相似,須要採用StartService API來使用或者採用一些軟件,這裏也能夠用Sc.exe來繼續使用。

sc start sample

這裏的sample就是前面註冊的驅動的名字。

可是這個一般會失效,由於對於64bit位的windows系統,加載驅動必須得要有驅動的簽名才行,這裏爲了學習方便,避開簽名這個東西,能夠直接把系統置爲測試版本。

bcdedit /set testsigning on

若是你要生成除了Windows10之外的版本,能夠在項目屬性裏面配置你的驅動要部署在的系統環境裏面:

 

 

最後再使用前面sc來加載驅動時會看到一個關於驅動的輸出:

 

 

有了這個輸出就代表咱們的驅動已經成功加載了,可使用Process Explorer工具來確認是否加載成功(下載地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer

 

 

這裏的驅動名稱是你本身的sys驅動名稱。

卸載驅動

不用了將驅動程序卸下,一樣的能夠採用 ControlService API或者Sc.exe來處理。Sc指令:

sc stop sample

就OK了。

簡單跟蹤

爲了確保函數有確切被調用,這裏提供一種基礎的跟蹤辦法來確保函數被使用,驅動採用KdPrint這個宏來輸出相似於printf風格的文本,該宏的內容能夠被內核的調試器,或者其它工具查看到。

KdPrint這個宏只在debug模式下采用,它的底層調用的實際上是DbgPrint 內核Kernel API。

下面更新一下DriverEntry和Unload函數:

void SampleUnload(_In_ PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
KdPrint(("Sample driver Unload called! \n"));
}

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
DriverObject->DriverUnload = SampleUnload;
UNREFERENCED_PARAMETER(RegistryPath);
KdPrint(("Sample driver initialized successfully\n !"));

return STATUS_SUCCESS;
}

須要注意的是該宏函數在調用時採用了兩個括號,由於它是一個宏函數,可是又顯然它是能夠接受任意變量的,因爲宏函數不能接受可變的變量參數,因此編譯器實際上調用的是DbgPrint函數。這裏理解不了不要緊,先這樣用着就行。

從新生成驅動並加載來查看這些Print信息,這裏須要採用一個內核的調試器才行,可是爲了方便,先採用一個系統的內部工具:DebugView來查看。在使用DebugView以前,須要先給它在註冊表裏面配置內容否則用不上。

在\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\裏新建一個Key(項)Key名爲DebugPrintFilter,而且添加一個DWORD類型的值名爲DEFAULT,這個DEFAULT要和默認的一個值區別開來,後面那個默認的值是註冊表中的每個項都有的,那個值暫時先不用管,而後給該名爲DEFAULT類型爲DWORD的變量賦值爲8,以下圖所示:

 

 

 

而後下載DebugView(DebugView - Windows Sysinternals | Microsoft Docs),並用管理員身份打開它,而後再在Capture選項中去掉Capture Win32 和Capture Global Win32,選中Capture Kernel:

 

 

這樣,再使用Sc.exe來從新加載驅動就能夠看到KdPrint打印的內容了。

 

 

總結Summary

這裏明白瞭如何寫一個驅動,以及如何再電腦上部署和查看驅動的消息,算是驅動入門了。

相關文章
相關標籤/搜索