安装 Visual Studio 2022

下载 Visual Studio 2022,这里选用的社区版,选择使用 C++ 的桌面开发,然后在单个组件中选择以下两项:

  • MSVC v143 – VS 2022 C++ x64/x86 Spectre 缓解库 (最新)
  • Windows 驱动程序工具包 (WDK)
vs-installer-2022-for-driver-components

安装好后在 Visual Studio Installer 中显示,截止到目前 WDK 并未适配 Visual Studio 2026,请勿在 Visual Studio 2026 上进行驱动程序开发

vs-installer-2022-for-driver

上述的缓解库是必须的,否则编译不通过。Windows 驱动程序工具包 (WDK) 为驱动开发的 VSIX 扩展,扩展安装好后会在菜单上显示

vs-driver-vsix-extension

安装 SDK

默认选择使用 C++ 的桌面开发带的 SDK 不是最新的,例如我这里的 SDK 版本为 Windows 11 SDK (10.0.26100.7175),截止到目前,官网已发布的最新 SDK 版本号为 Windows 11 SDK (10.0.26100.7463)

vs-default-sdk

版本号说明,例如 10.0.26100.7175,26100 为构建号,7175 为 QFE 号,在后面安装 WDK 时需要记住这个构建号,因为 SDK 的构建号要与 WDK 的构建号对应,以免出现莫名其妙的错误。

安装 WDK

下载最新的 WDK 的安装程序,截止到目前,WDK 最新版本号为 10.0.26100.6584,与上一步中的 SDK 版本的构建号相同,说明两者是匹配的,至于 QFE 号不同,如果不需要用到最新 QFE 号更新的内容则可忽略

wdk-installer

从 Visual Studio 17.11.0 版本开始,WDK VSIX 作为独立包组件包含在 Visual Studio 中。安装 WDK 前,安装程序会检查是否已安装兼容版本的 VSIX。如果找不到 WDK VSIX,安装程序会提示您安装,VSIX 已在安装 Visual Studio 2022 时已安装好了,所以检测通过,接下来选择安装目录一路 Next 即可。

这里还需要提一下,WDK 中包含了 windbg 调试器,这个后面在调试驱动程序时需要用到

wdk-contains-windbg

当然也可以从微软商店下载安装 Windbg

windbg-from-microsoft-store

编译第一个驱动程序

启动 Visual Studio 2022,创建一个空的 WDM 驱动工程 (这里以 WDM 框架为例),工程名 MyDriver1

create-empty-wdm-project

在 Source Files 下创建 First.c 文件,内容如下

#include <ntddk.h>

VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
    if (DriverObject != NULL) {
        DbgPrint("[%ws]Driver Upload, Driver Object Address: %p", __FUNCTIONW__, DriverObject);
    }
    
    return;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    DbgPrint("[%ws]Hello Kernel World.\n", __FUNCTIONW__);
    
    if (RegistryPath != NULL) {
        DbgPrint("[%ws]Driver RegistryPath: %wZ\n", __FUNCTIONW__, RegistryPath);
    }
    if (DriverObject != NULL) {
        DbgPrint("[%ws]Driver Object Address: %p\n", __FUNCTIONW__, DriverObject);
        DriverObject->DriverUnload = DriverUnload;
    }
    
    return STATUS_SUCCESS;
}

右键项目,点击生成,在生成之前,删除默认的 inf 文件,这是配置驱动程序安装文件,在这里暂时不需要它

build-first-driver-program-generate-files

.sys 文件是我们需要的,它代表的是一个驱动程序文件,驱动程序文件不能像 exe 程序那样直接双击运行,它是以服务方式运行。另外还有一点区别是 64 位的操作系统能运行 64 位和 32 位 exe 程序,但是不能运行 32 位驱动程序,64 位只能运行 64 位,32 位只能运行 32 位。

运行驱动程序

如何运行 MyDriver1.sys,能够实时看到 DbgPrint 函数输出的内容。在运行之前,需要做以下操作:

  • 操作系统需以测试模式启动,除非你有正式签名 (需要购买申请)
  • 加载驱动程序方式 (sc 命令或第三方驱动程序加载工具)
  • dbgview 工具

如果没有正式签名,操作系统会默认禁止加载,所以需要开启测试模式,使用 bcdedit 命令

bcdedit /set testsigning on

接着重启,重启后桌面右下角会有测试模式的水印,为了方便操作及安全,测试环境用虚拟机环境,这里我用的是 Windows10 22H2,将 MyDriver1.sys 拖放到目标测试机上,以管理员方式打开命令行,执行 sc 命令

sc create FirstDriver binPath= "C:\Users\zer0daysec\Desktop\MyDriver1.sys" type= kernel start= demand

sc create 表示创建一个服务,binPath 指定要测试 sys 驱动程序的位置,type 为驱动的类型,start 表示服务启动类型,demand 为手动启动,注意,上述命令 = 后面需要有一个空格。

接下来启动 dbgview 程序,以管理员方式启动,打开后设置以下两项

dbgview-setting

然后启动服务

sc start FirstDriver

启动后再停止删除

sc stop FirstDriver
sc delete FirstDriver

dbgview 程序输出

dbgview-dbgprint

当然也可以使用第三方驱动程序加载工具进行加载运行,这样就省去了每次输 sc 命令,这里放个第三方驱动加载工具截图

a1pass-driver-loader

第三方驱动加载工具方便点,这里说下 sc 和工具原理,因为驱动只能以服务方式运行,上述都是通过调用与服务相关的 API 实现:

  • OpenSCManager
  • CreateService
  • OpenService
  • StartService
  • ControlService
  • DeleteService

你也可以根据以上 API 自己编写一个驱动加载工具,具体使用说明请参阅微软 API 文档。

调试驱动程序

如何对驱动程序进行调试,在上面这一节中,使用 DbgPrint 方式是一种,但这种方式效率是非常低的,在开发驱动程序过程中需要进行动态调试,以下讲解如何动态调试驱动程序。

使用 Net 方式

测试目标虚拟机 (VMware):

  • 操作系统:Windows10 22H2
  • IP:192.168.254.133

物理机:

  • 操作系统:Windows11 25H2
  • IP:192.168.1.101

目标机防火墙需要关闭,两端需要互相 Ping 通,在目标机上以管理员方式执行如下命令

bcdedit /dbgsettings net hostip:192.168.254.133 port:50010

执行完后,会生成一个 key,将这个 key 复制到物理机上文本中,后面要用到这个 key,再到 Visual Studio 中设置

vs-driver-vsix-extension

点击 Configure Devices,设置好以下值

device-configuration

Next

config-debugger-settings

完成后,Configure Devices 显示刚添加的配置

configure-devices

一切准备就绪后,为了方便调试,需要先在 DriverEntry 中的开始部分添加以下这句

KdBreakPoint();

KdBreakPoint() 函数只会在 Debug 版下有效,Release 版需换成 DbgBreakPoint()。一旦调试启动,会自动在此处断下,重新编译生成 sys 文件并将其拖放到目标机桌面上,在 Visual Studio 中打开调试菜单,选择附加到进程

attach-process-to-debug

连接目标选择 MyDriver1,选中 Kernel 后启动

attach-process-settings

接着就会等待连接,重启目标机,重启后就会连上

debugger-immediate-window

连上后还需加载 sys 文件,我这里就不用 sc 命令了,直接用第三方驱动加载工具,加载后 Visual Studio 自动断在开始处,目标机处于 “卡死” 状态

visual-studio-2022-debug-mydriver1

后面就可以按正常方式进行动态调试了。不过 Visual Studio 调试有个问题,无法在源码文件下一行一行调试,提示帧不在堆栈上,如果你知道原因可以告知我。当调试完毕后,不要急着在 Visual Studio 中点击停止调试 (用的是第三方驱动加载工具),先点击运行,此时目标机 “卡死” 状态恢复。再到目标机上的驱动加载工具点击停止,再到 Visual Studio 中点击停止调试,否则目标机继续 “卡死” 一小会,最后在工具处进行卸载。

虽然在 Visual Studio 能直接调试,但还是喜欢用 Windbg 调试,WDK 自带 Windbg 程序,但我这里没用它,用的是微软商店下载的,界面更加现代化。

在 Windbg 中设置 Net 方式调试设置,在设置此方式前,先设置下源代码文件路径及调试符号路径

source-and-debug-symbol-path

最后选择 Attach to kernel,再设置如下数值

windbg-net-setting

启动后等待连接,连上后加载驱动,自动断在 KdBreakPoint 处

windbg-debug-mydriver

后面就可以按正常方式进行动态调试了。

通过串口方式

将目标虚拟机关机,添加一个串行端口并设置好参数

add-c-port-vmware

使用命名管道,管道名为

\\.\pipe\com_1

在 Windbg 端,设置好以下参数值

set-c-port-on-windbg

启动目标机后,以管理员方式执行以下命令

using-cmd-set-c-port-for-debug

重启目标机,选择 Debug,也就是上面 /d 参数后面的值

select-debug-system

稍等片刻,加载驱动后,Windbg 自动断在入口处

using-c-port-debug-on-windbg

后面就可以按正常方式进行动态调试了。