windows 驱动程序开发环境配置(USB连接的双机开发)

概述:

本文基于微软官方教程 编写 Hello World Windows 驱动程序 (KMDF) ,结合笔者在配置开发环境过程中踩的坑,详细讲解了如何使用 USB 连接主计算机和目标计算机,并配置相应的双机开发环境。最后,通过在 Hello World 程序入口添加断点来验证环境正确性。(ps:主计算机用于开发和调试,目标计算机用于运行编写的驱动程序)

核心步骤:

  • 在主计算机上安装 visual studio 2022 + Windows 驱动程序工具包
  • 编写 Hello World 驱动程序
  • 生成 Hello World 驱动程序
  • 本地测试 Hello World 驱动程序 (可选)
  • 预配目标计算机
  • 在目标计算机上部署解决方案
  • 在目标计算机上安装驱动程序
  • 使用 WinDBG + USB 的模式调试目标计算机

Step1:在主计算机上安装 visual studio 2022 + Windows 驱动程序工具包

  • visual studio官方网址如下:https://visualstudio.microsoft.com/zh-hans/
  • 个人学习使用的话,下载社区版本即可
  • 安装时 工作负载 选择 使用 C++ 的桌面开发
  • 安装时 单个组件 添加:
    • MSVC v143 - VS 2022 C++ ARM64/ARM64EC Spectre 缓解库(最新版本)
    • MSVC v143 - VS 2022 C++ x64/x86 Spectre 缓解库(最新版本)
    • 带有 Spectre 缓解库的适用于最新 v143 生成工具的 C++ ATL (ARM64/ARM64EC)
    • 适用于具有 Spectre 缓解功能的最新 v143 生成工具的 C++ ATL (x86 & x64)
    • 带有 Spectre 缓解库的适用于最新 v143 生成工具的 C++ MFC (ARM64/ARM64EC)
    • 适用于具有 Spectre 缓解措施的最新 v143 生成工具的 C++ MFC (x86 & x64)
  • WDK下载链接,按照默认参数直接安装,一路 Next 即可

Step2:编写 Hello World 驱动程序

  1. 打开 Microsoft Visual Studio。 在“文件” 菜单上,选择“新建”>“项目” 。
  2. 在“新建项目”对话框的左侧下拉列表中,选择“C++”,在中间下拉列表中,选择“Windows”,然后在右侧下拉列表中,选择“驱动程序” 。
  3. 从项目类型列表中选择“内核模式驱动程序,空(KMDF)”。 选择“下一页”。
  4. 在“配置新项目”对话框中,在“项目名称”字段中输入“KmdfHelloWorld” 。
  5. 在“位置”字段中,输入要在其中创建新项目的目录。
  6. 选中“将解决方案和项目置于同一目录中”,然后选择“创建” 。
  7. 在“添加新项目”对话框中,选择“C++ 文件”。 对于“名称”,请输入“Driver.c”。
    • 注意:名称以 .c 结尾!编写的是 c 文件!
  8. 将下列代码粘贴到 “Driver.c” 文件中。这是一份非常简单的 windows 驱动代码,在初始化绑定设备时会打印相应的提示信息,详细的讲解请参考官方文档。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#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;
}

Step3:生成 Hello World 驱动程序

  1. “解决方案资源管理器”窗口中,选择并按住 (或右键单击) 解决方案“KmdfHelloWorld” (1 个项目) ,然后选择“Configuration Manager”。 为驱动程序项目选择配置和平台。 在本练习中,我们选择“调试”和“x64”。
  2. “解决方案资源管理器”窗口中,选择并按住 (或右键单击“) KmdfHelloWorld”,然后选择“属性”。 在“Wpp 跟踪”>“所有选项”中,将“运行 Wpp 跟踪”设置为“否”。 依次选择“应用”、“确定” 。
  3. “生成”菜单中选择“生成解决方案”。 Visual Studio 在“输出”窗口中显示生成进度。
  4. 若要查看生成的驱动程序,请在 文件资源管理器 中转到 KmdfHelloWorld 文件夹,然后转到 x64\Debug\KmdfHelloWorld。 该文件夹包括:
    • KmdfHelloWorld.sys - 内核模式驱动程序文件
    • KmdfHelloWorld.inf - 在安装驱动程序时 Windows 使用的信息文件
    • KmdfHelloWorld.cat - 安装程序验证驱动程序的测试签名所使用的目录文件

Step4:本地测试 Hello World 驱动程序 (可选)

  1. Windows 系统要求安装的驱动程序必须有微软的数字签名,但很显然我们在开发过程中写的驱动不会有签名。因此,为了在本地测试,我们需要先关闭 Windows 系统的强制签名检查

    • 开始菜单,按住shift,鼠标左键单击重新启动。该操作会打开 Windows 高级启动菜单
    • 依次选择:疑难解答->高级选项->启动设置->按数字键7选择禁用驱动程序强制签名
  2. 以管理员身份运行cmd,执行如下命令,创建一个服务(driverpath需要替换成上面工程生成的.sys文件路径)

    • sc create MyFirstDriver binPath= "driverpath" type= kernel
  3. 以管理员身份运行DebugView.exe,在菜单栏中勾选Capture KernelEnable Verbose Kernel Output

    • Debugview 是一个应用程序,支持你监视本地系统上或可通过TCP/IP 访问的网络上任何计算机上的调试输出
    • DebugView下载链接
  4. 在控制台输入如下命令,启动我们之前创建好的服务,可以在DebugView中看到DriverEntry函数输出的内容

    • sc start MyFirstDriver
  5. 测试完毕,使用如下命令关闭服务(卸载驱动)

    • sc stop MyFirstDriver

Step5:预配目标计算机

因为后续我们需要在主计算机端将生成的驱动文件(包)通过网络发送到目标计算机,并进行远程部署。所以我们需要预先对主计算机和目标计算机进行配置,下载一些工具包,并保证两者间网络连接畅通(能够互相ping通)。具体配置流程分主计算机侧和目标计算机侧两部分,我们先学习目标计算机侧的配置:

目标计算机侧操作

  1. 下载安装 WDKWDK下载链接,按照默认参数直接安装,一路 Next 即可。
  2. 在 BIOS 中关闭 安全启动(如何进入 bios 请结合主板品牌 google 之)
  3. 运行与目标计算机平台匹配的 WDK 测试目标设置 MSI。可以在 Remote 下的 Windows 驱动程序工具包 (WDK) 安装目录中找到该 MSI。
    • 默认路径:C:\Program Files (x86)\Windows Kits\10\Remote\x64\WDK Test Target Setup x64-x64_en-us.msi
  4. ping 通主计算机
    • 开启防火墙命令:NetSh Advfirewall set allprofiles state on
    • 关闭防火墙命令:NetSh Advfirewall set allprofiles state off

主计算机侧操作

  1. ping 通目标计算机
  2. 在主机计算机上的 Visual Studio 中,选择“扩展”菜单,依次指向“驱动程序”“测试”,然后选择“配置设备”
  3. “配置设备”对话框中,选择“添加新设备”
  4. 设备名称根据个人喜好任取。对于网络主机名,输入目标计算机的名称或本地 IP 地址。 选择“预配设备并选择调试程序设置”
image-20240306174436450
  1. 点击 下一步,跳转至如下界面,此时需要注意以下几点:

    • Port NumberKey 后续可能会用到(如果使用 WinDBG + 以太网 调试的话),建议保存下来。
    • 如果主计算机有多个 IP 地址,要在 Host IP 下拉框中选择能 ping 通的!
    image-20240306174601687
  2. 点击 下一步,正式开始配置目标计算机,此过程大概持续2-3分钟。预配成功的话,结果如下图所示。

    image-20240306180000794
    • 笔者此前配置时,出现过Failed installing components的错误,但经过尝试,它并不影响后续的操作。

    • 建议大家先点击完成,回到Configure Devices界面,看看Driver test configuration的状态,如果为Configured for driver testing则可以进行后续操作。

      image-20240306180500822

Step6:在目标计算机上部署解决方案

  1. “解决方案资源管理器”窗口中,选择并按住(或右键单击)KmdfHelloWorld 项目,然后选择“属性”

  2. “KmdfHelloWorld 属性页”窗口中,转到“配置属性”>“驱动程序安装”>“部署”,如下所示。

  3. 选中“部署前删除以前的驱动程序版本”

  4. 对于“目标设备名称” ,请选择先前配置的用于测试和调试的计算机名,本例中为MyComputer

  5. 选择“硬件 ID 驱动程序更新” ,然后输入驱动程序的硬件 ID。

    • 本例中,硬件 ID 不标识真实的硬件。 它标识了虚构设备。

    • 该虚拟设备的硬件 ID 可以在驱动程序的信息 (INF) 文件中看到

      image-20240306181215750
    • 在文件中搜索[Standard.NT$ARCH$]可以看到如下内容,其中红色框框内即为硬件 ID

      image-20240306181337978
  6. “生成”菜单上,选择“部署解决方案”。 Visual Studio 会自动将安装和运行驱动程序所需的文件复制到目标计算机。 部署可能需要一两分钟。

  7. 切到目标计算机,访问C:\drivertest\drivers文件夹,验证驱动文件是否成功复制。

Step7:在目标计算机上安装驱动程序

  1. 目标计算机上找到 DevCon 工具。笔者是在C:\DriverTest文件夹找到的DevCon.exe文件。如果找不到也可以在主计算机的 WDK 文件夹C:\Program Files (x86)\Windows Kits\10\Tools\x64\devcon.exe 中找到并复制到目标计算机

  2. 目标计算机上,导航到包含驱动程序文件的文件夹(本例中为:C:\drivertest\drivers),然后运行 DevCon 工具,以安装驱动程序。

    • 使用 DevCon 工具安装驱动程序的语法:devcon install <INF file><hardware ID>
    • 管理员身份启动终端,输入命令:..\devcon.exe install KMDFHelloWorld.inf root\kmdfhelloworld
  3. 可以在目标计算机上使用 driverquery 命令验证 HelloWorld 驱动是否安装成功。

Step8:使用 WinDBG + USB 的模式调试目标计算机

什么是 WinDBG ?

  • WinDbg是Microsoft Windows上的多用途调试器,可从微软网站上免费下载安装使用。用于调试用户态下的应用程序、驱动程序,以及核心态下的操作系统自身
  • WinDBG 支持以下类型的双机连接模式:
    • 以太网
    • USB 2.0 / USB 3.0
    • 串行(也称为零调制解调器)
  • 如果使用 以太网USB 进行双机调试,对 目标计算机 的 网卡型号 或 USB端口 有一定要求。笔者一开始按照微软官方教程搭建环境,一直卡在双机通信这一步,两台机器虽然能够相互 ping 通,但是 WinDBG 始终连接不上。后来才发现是因为 目标计算机 的网卡型号不满足要求,故转战 USB 连接。下面将分别给出针对 网卡型号 和 USB 端口 的检查方式,请读者结合实际情况选择合适的连接方案。

如何检查网卡型号?

  • WinDBG 仅对 目标计算机 的网卡型号有要求哦,因此后续步骤均在 目标计算机 执行。

  • 以管理员身份启动终端,并切换到 Windows 驱动程序工具包(WDK)目录,默认路径为: C:\Program Files (x86)\Windows Kits\10\Debuggers\x64

  • 执行如下命令:.\kdnet.exe

  • 网卡型号符合要求 则输出情况如下:

    image-20240307112315521
  • 网卡型号不符合要求 则输出情况如下:

    image-20240307112403820

如何检查USB端口?

  1. 目标计算机上,启动 UsbView 工具。 UsbView 工具包含在适用于 Windows 的调试工具中。

    image-20240307120009804
  2. 在 UsbView 中,展开 xHCI 主机控制器的节点。 查找主机控制器上的端口支持调试的指示。如果 Is Port Debug Capable 字段为 yes 则表明该端口支持 WinDBG。

    image-20240307120111745
  3. 记下要用于调试的 xHCI 控制器的总线、设备和函数号。 UsbView 会显示这些数字。

image-20240307120812626
  1. 找到支持调试的 xHCI 控制器后,下一步是查找与 xHCI 控制器上的端口关联的物理 USB 连接器。 请将任何 USB 3.0 设备插入目标计算机上的任何 USB 连接器(说白了就是试,把每个口都插一插,然后刷新 UsbView,看看连接信息是否更新,如果更新了就找到了)。 刷新 UsbView 以查看设备所在的位置。 如果 UsbView 显示设备连接到所选的 xHCI 主机控制器,则表明你找到了可用于 USB 3.0 调试的物理 USB 连接器。

如何使用 USB 建立双机连接?

  1. ”如何检查USB端口“” 一节我们已经找到了目标计算机上可用的 USB 接口,因此我们要先建立物理链路上的连接,即用数据线连接两台机器

  2. 目标计算机上,以管理员身份打开命令提示符窗口,然后输入以下命令:

    1
    2
    bcdedit /debug on
    bcdedit /dbgsettings usb targetname:<TargetName>
    • TargetName 是为目标计算机创建的名称。 请注意, TargetName 不一定是目标计算机的官方名称;只要满足以下限制,它就可以是创建的任何字符串:
      • 字符串不得在 TargetName 中的任何位置包含“debug”,大小写或大写的任意组合。 例如,如果在 targetname 中的任何位置使用“DeBuG”或“DEBUG”,则调试无法正常工作。
      • 字符串中唯一的字符是连字符 ( ) 、下划线 (_) 、数字 0 到 9,以及字母 A 到 Z (大写或小写) 。
      • 字符串的最大长度为 24 个字符。
  3. 继续执行以下命令:bcdedit /set "{dbgsettings}" busparams <b.d.f>

    • 其中,b,d,f 是 USB 主控制器的总线、设备和功能编号。均采用 10 进制。
    • ”如何检查USB端口“ 一节我们记录过这个 PCIe 域地址哦,忘了的家人可以再去瞅一眼。
  4. “设备管理器”中,导航到 xHCI 主控制器的节点。 右键单击“节点”,然后选择“属性”。 如果有 “电源管理 ”选项卡,请打开该选项卡,然后清除 “允许计算机关闭此设备以节省电源 ”复选框。

  5. 重新启动目标计算机

  6. 主计算机 上下载并安装最新版 WinDBG,最新版的 GUI 界面比老版好的不要太多。下载链接

  7. 管理员身份 运行 WinDBG,选择 file -> Attach to kernel -> USB -> 输入本节第2步起的 targetname -> 选择 Break on connection -> OK

    image-20240307162231068
  8. 随后会进入如下界面,显示 “Waiting to reconnect”

    image-20240307162605583
  9. 重新启动 目标计算机。由于我们之前勾选了 ”连接时中断“,因此在 目标计算机 再次启动加载 Windows 操作系统时会停住。主计算机 WinDBG 窗口显示如下内容。

    至此,WinDBG 成功连接到目标计算机,并让其中断在初始化阶段,我们可以在任何想要的地方打断点!

    image-20240307172708095
  10. 使用 bu 驱动名!函数名命令在 HelloWorld 驱动初始化函数处打断点,使用 bl 命令验证打断点成功(在bd>后面输入命令)

image-20240307172822833

  1. 使用 g 命令启动目标计算机上的 Windows 操作系统,等待几秒后,它会停止在断电处,WinDBG 显示如下图!大功告成!

    image-20240307172957286

debug

  • 如果出现 Failed to USB device driver inf. The debugger must be copy run elevated first for the use of this transport. Error 0x5报错,如下图所示,是因为打开 WinDBG 时没有使用管理员模式!

    image-20240307162522591