原创Paper | Windows 驱动开发入门
2023-4-23 17:14:30 Author: Seebug漏洞平台(查看原文) 阅读量:11 收藏

作者:[email protected]知道创宇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

作者名片

往 期 热 门
(点击图片跳转)


“阅读原文”更多精彩内容!

文章来源: http://mp.weixin.qq.com/s?__biz=MzAxNDY2MTQ2OQ==&mid=2650967907&idx=1&sn=004cbd11c2e833ab402e3d5a8817e38c&chksm=8079cd51b70e44476e6b36403c3a613a3bd8fa2f472c95e4730a80c2d43a790411dd6d48e51f#rd
如有侵权请联系:admin#unsafe.sh