作者:0x7F@知道創宇404實驗室
時間:2023年4月18日
0x00 前言
隨著 windows 系統的更新迭代,windows 驅動開發技術也是不斷的升級:從最早期的 VXD(Virtual X Driver)(已廢棄)到 windows 2000 上推出的 WDM(Windows Driver Model)驅動模型,隨后從 windows vista 推出的 WDF(Windows Driver Foudation)驅動模型,沿用至今;WDF 是 WDM 的升級版,并且在一定程度上兼容,WDF 是微軟目前推薦的驅動開發模型。
WDF 還可以細分為內核模式 KMDF(Kernel-Mode Driver Framework) 和用戶模式 UMDF(User-Mode Driver Framework),顧名思義 UMDF 將受到更多的限制從而換來更高的操作系統穩定性,其二進制擴展名為 *.dll
;UMDF 和 KMDF 開發基本相同,本文這里僅介紹使用更廣泛的 KMDF 開發。
本文實驗環境
windows 10 專業版 x64 1909
Visual Studio 2019
SDK 10.0.19041.685
WDK 10.0.19041.685
0x01 搭建驅動開發環境
首先配置 Visual Studio 2019
的 C/C 開發環境(https://visualstudio.microsoft.com/),按 Visual Studio 官方教程,自動下載安裝「使用C的桌面開發」,其中 SDK 默認為 10.0.19041.0
:
開發軟件需要 SDK(Software Development Kit),而開發 windows 驅動則需要 WDK(Windows Driver Kit);現在我們來配置 WDK 環境,從官網(https://learn.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk)下載 WDK 在線安裝包(版本必須和 SDK 一致),按如下進行安裝:
對于 WDF 驅動模型其開發環境叫 WDK(Windows Driver Kit)
對于 WDM 驅動模型其開發環境叫 DDK(Driver Development Kit)
安裝完畢后,其窗口會默認勾選為 Visual Studio
安裝 WDK 擴展插件,按照指導進行安裝即可,隨后我們可以在 Visual Studio
的創建項目頁面,就看到 KMDF/UMDF 等選項,表示 windows 驅動開發環境配置成功。
windows 驅動開發環境可能會受操作系統、Visual Studio、SDK、WDK 的版本影響,配置過程需要多留心這些環節,如遇見問題可以參考如下版本信息 https://learn.microsoft.com/en-us/windows-hardware/drivers/other-wdk-downloads。
0x02 HelloWorld開發
根據官方教程,我們在 Visual Studio
中創建空的 KMDF
項目,并在其中創建 Driver.c
文件,編寫代碼如下:
#include <ntddk.h>
#include <wdf.h>
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
// NTSTATUS variable to record success or failure
NTSTATUS status = STATUS_SUCCESS;
// Allocate the driver configuration object
WDF_DRIVER_CONFIG config;
// Print "Hello World" for DriverEntry
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n"));
// Initialize the driver configuration object to register the
// entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd
WDF_DRIVER_CONFIG_INIT(&config,
KmdfHelloWorldEvtDeviceAdd
);
// Finally, create the driver object
status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
WDF_NO_HANDLE
);
return status;
}
NTSTATUS
KmdfHelloWorldEvtDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
// We're not using the driver object,
// so we need to mark it as unreferenced
UNREFERENCED_PARAMETER(Driver);
NTSTATUS status;
// Allocate the device object
WDFDEVICE hDevice;
// Print "Hello World"
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n"));
// Create the device object
status = WdfDeviceCreate(&DeviceInit,
WDF_NO_OBJECT_ATTRIBUTES,
&hDevice
);
return status;
}
在「配置管理器」中設置為 Debug/x64
,編譯生成項目發現如下錯誤:
Visual Studio
默認開啟了緩解 Spectre 攻擊的機制,可在 VS 安裝器中選擇安裝指定的支持庫,我們實驗環境下可以直接關閉該功能,在 項目屬性-C/C++-代碼生成-Spectre Mitigation
設置為 Disable
:
隨后便可以成功編譯,在 [src]/x64/Debug/
下,可以看到生成的二進制文件 KmdfHelloWorld.sys
。
0x03 部署和測試
驅動程序一般以服務(service)或設備(device)的方式運行工作,這點和普通應用程序不同,所以不能像應用程序那樣進行調試和測試。官方指導中提供一種基于雙機調試環境的驅動調試方法,這種方式較為繁瑣,我們這里進行簡要介紹;想了解更方便的本機調試驅動程序的小伙伴,可跳轉至「0x04 本機調試驅動程序」。
按照官方指導,我們將驅動程序作為設備進行運行調試,在此之前需要再提供一臺主機作為被調試機(debugee
),驅動程序將在被調試機上(debugee
)進行部署和測試,而本臺主機即作為開發主機同時作為調試機(debugger
),如下:
首先在被調試機(debugee
)上也安裝上 WDK 環境,隨后在 WDK 的安裝目錄下運行該工具 WDK Test Target Setup
,默認路徑:C:\Program Files (x86)\Windows Kits\10\Remote\x64\WDK Test Target Setup x64-x64_en-us.msi
;在之后調試機(debugger
)中的 Visual Studio
將連接被調試機(debugee
)的 WDK Test Target Setup
的工具,自動完成雙機調試環境的配置。
在開發主機上(debugger
)初始化被調試機的相關信息,在 項目屬性-Driver Install-Deployment
中添加新設備,我們這里使用網絡雙機調試的方式,默認參數即可:
隨后將自動完成配置,如下:
在 Visual Studio
中將被調試機(debugee
)添加完畢后,在如下窗口選擇該主機并設置驅動的硬件 ID 為 Root\KmdfHelloWorld
,如下:
配置完成后,我們在 Visual Studio
菜單中 生成-部署解決方案
,驅動程序將自動部署在被調試機上(debugee
)并進行測試運行:
在被調試機(debugee
)上我們在設備管理器中可以看到 KmdfHelloWorld
已經成功部署了:
如果想調試驅動程序,則可以使用 WinDBG 依據以上的雙機調試環境對驅動程序進行調試。
0x04 本機調試驅動程序
官方提供的驅動程序部署和測試方法,雖然有效的隔離開發環境和調試環境,但實在是過于繁瑣了,更不用說其中雙機調試環境下的各種問題。而驅動程序還可以以服務的方式進行運行,我們通過這種方式可以更加方便的在本機調試驅動程序。
在日常安全工作中,我更喜歡使用這種方式,因為大多數情況我只需要工作在內核層的驅動代碼,而不關心其是否是完整的 windows 驅動設備,這種方式能幫助我快速進行安全驗證工作。
創建 KMDF 項目并編寫代碼如下:
#include <ntddk.h>
#include <wdf.h>
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "test: unload driver\n"));
}
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "test: driver entry\n"));
DriverObject->DriverUnload = OnUnload;
return STATUS_SUCCESS;
}
在「配置管理器」中設置為 Debug/x64
,編譯生成項目。
在運行測試前,我們需要在本機(即開發主機)上打開測試模式(重啟生效),使得操作系統可以加載我們編譯的驅動程序,使用管理員權限打開 powershell:
# 打開測試模式
bcdedit /set testsigning on
重啟主機后,使用管理員權限打開 powershell,通過 sc.exe
命令為驅動程序創建服務(命令詳解請參考:https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/sc-create):
# 創建服務名為 test,類型為 kernel,啟動方式為 demand 的服務,并指定驅動程序路徑
# (注意參數等號后必須有一個空格)
sc.exe create test type= kernel start= demand binPath= C:\Users\john\Desktop\workspace\kmdf_test\x64\Debug\kmdf_test\kmdf_test.sys
# 使用 queryex 查看創建的服務信息
sc.exe queryex test
創建服務如下:
隨后便可以使用 sc.exe
命令啟動驅動程序運行,并使用 DebugView
查看調試輸出(需要勾選 Capture Kernel
和 Enable Verbose Kernel Output
才能看到輸出):
# 啟動運行驅動程序
sc.exe start test
# 停止運行驅動程序
sc.exe stop test
運行如下:
當我們更新了驅動代碼、編譯項目后,可以再次 start/stop
這個服務,便可以快捷的進行驅動程序代碼的測試和調試。
0x05 References
https://learn.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/
https://github.com/microsoft/Windows-driver-samples
https://visualstudio.microsoft.com/
https://learn.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/sc-create
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/2063/