编写 Windows 文件系统(零)
打算开始一项伟大的任务:在 Windows 下编写一个勉强能用的文件系统。用一系列博客文章记录下这个过程,如果不鸽掉的话。
这篇文章的内容:
- 复现 Microsoft 官方的 编写 Hello World Windows 驱动程序 (KMDF) 教程。
测试环境:
- 宿主机: Windows 10 专业版 21H1 19043.1889
- 虚拟机: Windows 10 专业工作站版 21H1 19043.928
- Visual Studio 2022 Community 17.3.5
- VMWare Workstation 16.1.2
编写驱动代码
- 前往 Windows Driver Kit 的下载链接,执行步骤 1 到步骤 3;
- 步骤 1 中要注意的是 WDK 需要 Spectre 缓解库。如果已经装好了 Visual Studio,那么需要启动 Visual Studio Installer 安装好对应架构的 Spectre 缓解库;
- 步骤 2 和步骤 3 是不一样的:前者是 SDK,而后者是 WDK!需要先装好 SDK 后,再安装 WDK。如果只装了前者,会发现 VS 里没有后者的集成。
- 打开 VS,创建
Kernel Mode Driver, Empty (KMDF)
类型项目,起名为 KmdfHelloWorld,勾选“将解决方案和项目置于同一目录中”; - 在项目中创建 Driver.c 文件,写代码:
1 |
|
- 选择想要的配置(例如 Debug + x64),然后在项目属性的 Wpp Tracing 一栏中将 Run Wpp Tracing 改为“否”,在 Inf2Cat 一栏中将 Use Local Time 改为“是(/uselocaltime)” ;
- 生成解决方案。
配置虚拟机
按照教程上的说法,做 Windows 驱动开发需要两台机器,一台机器开发,一台机器测试。很显然,这是为了避免炸掉开发环境。这里要提一嘴 Linux,Linux 里直接利用系统自带的头文件编译内核模块代码,就可以 insmod 到内核中,将开发和测试两台机器合二为一,确实非常方便,不过一旦写出解引用空指针这样的 bug 就只有重启一条路可走了。可见 Windows 这套还是有它的优势在。
虚拟机的配置比较麻烦,虽然网上有各种教程,我实际配的时候还是踩了一些坑(当然也有可能是我 Windows 版本选的不对)。这里的操作步骤如下:
- 按 VMWare 的引导,创建一台 Windows 10 x64 虚拟机,等待它安装完成;
- 设置共享文件夹,将 Windows Driver Kit 的 SDK 和 WDK 安装文件都复制过去,安装;
- 装好之后,再安装测试目标 MSI。如果上一步是默认安装位置的话,这个 MSI 就在:
1
C:\Program Files (x86)\Windows Kits\10\Remote\x64\WDK Test Target Setup x64-x64_en-us.msi
- 去控制面板 → 系统和安全 → Windows Defender 防火墙 → 高级设置,点“入站规则”,找到“虚拟机监控(回显请求 ICMPv4-In)”,改成“允许连接”。保险起见 ICMPv6 的那个也改一下;
- 虚拟机的网络默认情况下应该是 NAT 模式,去 VMWare 的菜单点“编辑” → 虚拟网络编辑器,找到 NAT 模式的网段,我这里是
192.168.204.0/24
;然后,在宿主机和虚拟机分别ipconfig /all
一下,确认该网段的 IP 地址,我这里是宿主机192.168.204.1
、虚拟机192.168.204.130
; - 验证虚拟机和宿主机之间互相 ping 通。
到这一步,虚拟机就准备好被宿主机配置了。
现在,回到宿主机,去 VS 项目的项目属性中 Driver Install → Deployment 一栏,可以看到一个 Target Device Name,下面是空着的。点击“…”按钮进入 Configure Devices 对话框,我们需要在这里把虚拟机配成一个能接受 VS 自动部署的状态。
- 点 Add New Device,Network host name 填写虚拟机 IP,Provisioning Options 选第一项,然后下一步;
- 调整和确认 Kernel Mode 下面的设置,主要是 Host IP 需要改为能和虚拟机通信的那个网段的 IP(可以从下拉列表里选),Port Number 和 Key 记下备用;
- 确保虚拟机处于正常开启状态,然后下一步,这时应该能马上看到“Copying required files”,如果不是的话那么很大概率等一会就会出错;
- 我之前的出错原因是没有安装测试目标的 MSI,这时候会显示连接某个端口超时没有响应。
- 等待自动配置完成(“完成”按钮亮起),期间虚拟机可能会重启多次,并且变为 WDKRemoteUser 账户。这时你可能会看到有些步骤失败了,但是不要紧,可以点击下面的日志文件的链接,打开日志文件查看具体失败细节。我遇到了以下几个问题:
- Installing desktop driver test framework 失败,原因是虚拟机上 C:\WDTFInstallText.log 这个文件找不到。在虚拟机中以管理员权限打开命令提示符,把上面显示的指令重新输一遍:不报错,就认为是可以了;
1
msiexec.exe /i "%SystemDrive%\DriverTest\Setup\WDTF_Desktop_Kit_Product-x64_en-us.msi" /qb- KITTARGET=1 /l*v "%SystemDrive%\DriverTest\Logs\WDTF_Desktop_Kit_Product-x64_en-us.msi_install.log"
- Installing TAEF service 失败,原因是在虚拟机上执行指令时,用了宿主机上 WDK 的路径,显然是自动部署脚本写出锅了。把路径改对,去虚拟机上执行即可:
1
C:\Program Files (x86)\Windows Kits\10\Testing\Runtimes\TAEF\X64\Wex.Services.exe /install:Te.Service
- Installing desktop driver test framework 失败,原因是虚拟机上 C:\WDTFInstallText.log 这个文件找不到。在虚拟机中以管理员权限打开命令提示符,把上面显示的指令重新输一遍:
- 手动执行完失败了的指令后,点上一步回去,再下一步重新跑一遍。尽管该失败的还是失败,但应该也只有上面这两个失败了。
- 点下一步,如果看到“Status: Configured for driver testing”,即表示虚拟机配置成功。
再回到项目属性中的 Deployment 那一栏:
- 选好 Target Device Name,勾选“Remove previous driver versions before deployment”,在 Driver Install Options 下面选择 Hardware ID Driver Update,然后输入
Root\KmdfHelloWorld
,然后点确定结束设置; - 在生成菜单中选择“部署解决方案”,驱动会被部署到虚拟机的 C:\DriverTest\Drivers 目录下,其中会包含 cat、cer、inf、sys 四个文件。
安装驱动并观测输出
就好像在 Linux 下用 dmesg 观察 printk
等函数的输出一样,Windows 下也可以用 DebugView 查看 KdPrintEx
等函数的输出。在虚拟机中以管理员权限启动 DebugViewer,它就会开始收集内核打印的调试信息。
现在,按照教程中“安装驱动程序”一节的指示:
- 去虚拟机中 Windows Kits 安装目录下的 Tools 目录,找到对应架构的 DevCon 工具。Tools 目录下可能唯独没有 x64 目录,需要再点进去一级:
1
C:\Program Files (x86)\Windows Kits\10\Tools\10.0.22621.0\x64\devcon.exe
- 保持 DebugView 打开状态,执行下列命令安装驱动程序:
1
devcon.exe install C:\DriverTest\Drivers\KmdfHelloWorld.inf Root\KmdfHelloWorld
- 在 DebugView 中可以看到调试输出: 在虚拟机的设备管理器中也能看到驱动对应的设备:
这样一来,我们就走通了开发 Windows 内核驱动的全流程。不过,实际开发过程中,可能还需要用调试器调试驱动,这里一并记录一下。
调试虚拟机
有两种调试方案,它们的区别在于打开调试器的地方不同,但是进入调试之后的操作基本都一样。
Visual Studio
在菜单中选择“调试” → 附加到进程,选择 Windows Kernel Mode Debugger 和之前设好的虚拟机,这时下面会显示一个 Kernel 进程,点击附加即可。
附加成功后,按上方的暂停按钮中断虚拟机运行(int 3
),然后进行各种调试。
WinDbg
前往 Windows Kits 目录下的 Debuggers 目录,启动对应架构的 windbg.exe。这里需要用上我们之前记下的调试用端口号和密钥:
1 | .\windbg.exe -k net:port=50365,key=TQZ6HOHHNX9O.6Y7GTHZJ7OF3.HNO3FYOM25W.0TNVZ9TNVS4 |
参考资料
- Microsoft: 下载 Windows 驱动程序工具包 (WDK)
- Microsoft: 编写 Hello World Windows 驱动程序 (KMDF)
- Microsoft: 预配计算机以便进行驱动程序部署和测试 (WDK 10)
- Microsoft: DebugView v4.90
- CSDN: 系统报“客户没有所需的特权”的解决方法
- CSDN: 驱动开发调试环境配置(WDK)