Windows Kernel Rust 中的驱动程序开发简介
内容
- 简介
- 依赖关系
- 发展
- 配置
- 实施
- 部署和测试
- 总结
- 参考文献
简介
嗨!很高兴你在这里。在这篇文章中,我们将使用以下命令构建一个小型的 Windows 内核驱动程序 Rust。是的,就是Rust。时尚、响亮且实际有用。
本文停留在安全、基础的方面:工具链设置、与 WDK 的链接、 no_std、最小驱动程序入口点和内核调试输出 DbgPrint。从这里您可以继续进行合法的后续步骤,例如 IOCTL 处理、设备对象、IRP 调度例程和真正的驱动程序体系结构。
如果您还不知道 Rust,也没关系:我还将展示一个类似的 C++ 示例。您所需要的只是时间、C/C++ 经验以及对内核模式开发的基本了解。
为什么是Rust?因为它可以提供接近 C/C++ 的性能,同时在语言级别提供强大的内存安全保证。许多大型组织正在投资内存安全代码,特别是 Rust,Rust 生态系统快速交付,每约 6 周发布一次稳定版本,并有一个单独的夜间频道。
⚠️ 道德与安全说明内核驱动程序以极高的权限运行。这些技术仅用于学习、研究和合法开发。
依赖关系
要继续操作(或复制/粘贴),请安装:
- Rust: https://www.rust-lang.org/tools/install
- Windows SDK: https://developer.microsoft.com/windows/downloads/windows-sdk/
- Windows WDK: https://learn.microsoft.com/windows-hardware/drivers/download-the-wdk
- Sysinternals 套件(调试视图): https://learn.microsoft.com/sysinternals/downloads/sysinternals-suite
您还需要一个虚拟机(VMWare、Hyper-V、VirtualBox、QEMU) Windows 10 x64 or Windows 11, 启用测试签名,并启用内核调试。
启用测试模式和内核调试,然后重新启动:
⚠️ 以管理员身份运行终端
# Enable Test Signing mode
bcdedit -set TESTSIGNING ON
# Enable kernel debugging
bcdedit /debug on
发展
配置
创建一个新的 Rust 库项目:
cargo new hello-win-kernel --lib
项目结构:
hello-win-kernel/
src/lib.rs
.gitignore
Cargo.toml
在Rust中, main.rs 是二进制文件的入口点,并且 lib.rs 是库的入口点。我们将构建一个驱动程序作为动态库工件。
编辑 Cargo.toml:
- 添加一个
[lib]部分和集合crate-type = ["cdylib"]所以 Rust 生成一个与 C 兼容的动态库。 - 套装
panic = "abort"对于调试和发布配置文件以避免展开行为。 - 添加
winreg作为 建立依赖关系 用于从注册表中读取 WDK/Windows 套件路径build.rs.
Cargo.toml
[package]
name = "hello-win-kernel"
version = "0.1.0"
edition = "2021"
[lib]
path = "src/lib.rs"
crate-type = ["cdylib"]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
[build-dependencies]
winreg = "0.50.0"
创建一个 rust-toolchain 项目根目录中的文件以强制每晚(我们在链接期间需要仅每晚标志):
rust-toolchain
nightly
添加构建脚本以查找 WDK 库
要构建驱动程序,我们需要链接一些 WDK 库(例如, ntoskrnl)。我们将在构建时添加库搜索路径 build.rs.
创建 build.rs:
构建.rs
extern crate winreg;
use std::env::var;
use std::error::Error;
use std::fmt;
use std::path::{Path, PathBuf};
use winreg::enums::*;
use winreg::RegKey;
const WIN_KITS_KEY: &str = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
#[derive(Debug)]
struct BuildError {
msg: String,
}
impl Error for BuildError {}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "BuildError: {}", self.msg)
}
}
fn get_windows_kits_dir() -> Result<PathBuf, BuildError> {
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
match hklm.open_subkey(WIN_KITS_KEY) {
Ok(key) => match key.get_value::<String, _>("KitsRoot10") {
Ok(dir) => Ok(dir.into()),
Err(_) => Err(BuildError {
msg: format!("Cannot get value: {}", WIN_KITS_KEY),
}),
},
Err(_) => Err(BuildError {
msg: format!("Cannot open subkey: {}", WIN_KITS_KEY),
}),
}
}
fn get_km_dir(windows_kits_dir: &PathBuf) -> Result<PathBuf, BuildError> {
match Path::new(windows_kits_dir).join("lib").read_dir() {
Ok(read_dir) => {
match read_dir
.filter_map(|dir| dir.ok())
.map(|dir| dir.path())
.filter(|dir| {
dir.components()
.last()
.and_then(|c| c.as_os_str().to_str())
.map(|c| c.starts_with("10.") && dir.join("km").is_dir())
.unwrap_or(false)
})
.max()
.ok_or_else(|| format!("Cannot find a valid km dir in `{:?}`", windows_kits_dir))
{
Ok(max_lib_dir) => Ok(max_lib_dir.join("km")),
Err(msg) => Err(BuildError { msg }),
}
}
Err(_) => Err(BuildError {
msg: format!("Cannot read dir: {:?}", windows_kits_dir),
}),
}
}
fn main() {
let windows_kits_dir = get_windows_kits_dir().unwrap();
let km_dir = get_km_dir(&windows_kits_dir).unwrap();
let target = var("TARGET").unwrap();
let arch = if target.contains("x86_64") {
"x64"
} else if target.contains("i686") {
"x86"
} else {
panic!("Only x64 and x86 are supported!");
};
let wdk_lib_dir = km_dir.join(arch);
println!(
"cargo:rustc-link-search=native={}",
wdk_lib_dir.to_str().unwrap()
);
}
驱动程序构建的链接器配置
创建一个 .cargo 文件夹并添加配置文件。货物支撑 config.toml (首选)及以上 config.
.cargo/config.toml
[build]
target = "x86_64-pc-windows-msvc"
rustflags = [
# Pre-link args (nightly-only via -Z)
"-Z", "pre-link-arg=/NOLOGO",
"-Z", "pre-link-arg=/NXCOMPAT",
"-Z", "pre-link-arg=/NODEFAULTLIB",
"-Z", "pre-link-arg=/SUBSYSTEM:NATIVE",
"-Z", "pre-link-arg=/DRIVER",
"-Z", "pre-link-arg=/DYNAMICBASE",
"-Z", "pre-link-arg=/INCREMENTAL:NO",
"-Z", "pre-link-arg=/MANIFEST:NO",
"-Z", "pre-link-arg=/PDBALTPATH:none",
# Post-link args
"-C", "link-arg=/OPT:REF,ICF",
"-C", "link-arg=/ENTRY:driver_entry",
"-C", "link-arg=/MERGE:.edata=.rdata",
"-C", "link-arg=/MERGE:.rustc=.data",
"-C", "link-arg=/INTEGRITYCHECK",
]
实施
Rust 在内核模式下不提供可用的标准库,因此我们禁用它 #![no_std]。我们还实现了一个自定义紧急处理程序,并提供了几个符号来满足这个最小设置中的 MSVC 链接器。
创建 src/lib.rs:
src/lib.rs
#![no_std]
mod dbg;
mod ntstatus;
mod types;
use ntstatus::*;
use types::*;
// Fixes linker expectations in some configurations.
#[no_mangle]
pub extern "system" fn __CxxFrameHandler3(
_: *mut u8,
_: *mut u8,
_: *mut u8,
_: *mut u8,
) -> i32 {
0
}
// Fixes linker expectations. Floating-point is generally discouraged in kernel drivers.
#[export_name = "_fltused"]
static _FLTUSED: i32 = 0;
// Panic handler (no std, no unwinding)
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
// Driver entry point
#[no_mangle]
pub extern "system" fn driver_entry(_driver: PVOID, _path: PVOID) -> NTSTATUS {
kd_print!("Hello, world!\n");
STATUS_SUCCESS
}
添加基本的 Windows 类型别名:
src/types.rs
pub type NTSTATUS = u32;
pub type PCSTR = *const u8;
pub type PVOID = *mut core::ffi::c_void;
添加一个最小的 NTSTATUS 常量:
src/ntstatus.rs
use crate::types::*;
pub const STATUS_SUCCESS: NTSTATUS = 0x00000000;
使用 DbgPrint 进行内核调试输出
现在我们将导入 DbgPrint from ntoskrnl 并用一个小宏包装它,这样我们就不会忘记空终止符。
src/dbg.rs
use crate::types::*;
#[link(name = "ntoskrnl")]
extern "C" {
#[allow(dead_code)]
pub fn DbgPrint(format: PCSTR, ...) -> NTSTATUS;
}
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! kd_print {
($string: expr) => {
unsafe {
$crate::dbg::DbgPrint(concat!("[hello_win_kernel.sys] ", $string, "\0").as_ptr())
}
};
($string: expr, $($x:tt)*) => {
unsafe {
$crate::dbg::DbgPrint(
concat!("[hello_win_kernel.sys] ", $string, "\0").as_ptr(),
$($x)*
)
}
};
}
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! kd_print {
($string: expr) => {};
($string: expr, $($x:tt)*) => {};
}
此时就可以构建成功了。接下来我们将自动重命名/签名和测试加载。
部署和测试
要在虚拟机中测试驱动程序,通常需要:
- 构建
.dllartifact - 将其重命名为
.sys - 签名(测试证书足以用于测试模式)
- 将其加载为服务并验证输出
我们将自动化这些步骤 cargo-make.
安装它:
cargo install cargo-make
配置Windows SDK工具路径
创建一个名为的系统环境变量 WIN_SDK_TOOLS 指向您的 Windows SDK 工具文件夹。
步骤:
- 打开 查看高级系统设置
- 前往 高级 tab
- 点击 环境变量
- 下 系统变量,点击 新
- 命名它
WIN_SDK_TOOLS - 将值设置为 Windows SDK 工具路径
- 点击 OK

添加货物制作脚本
创建 makefile.toml 在项目根目录中:
生成文件.toml
[env.development]
TARGET_PATH = "target/x86_64-pc-windows-msvc/debug"
[env.release]
TARGET_PATH = "target/x86_64-pc-windows-msvc/release"
BUILD_FLAGS = "--release"
[tasks.build-driver]
script = [
"cargo build %BUILD_FLAGS%"
]
[tasks.rename-driver]
dependencies = ["build-driver"]
ignore_errors = true
script = [
"cd %TARGET_PATH%",
"rename hello_win_kernel.dll hello_win_kernel.sys"
]
[tasks.sign-driver]
dependencies = ["build-driver", "rename-driver"]
script = [
""%WIN_SDK_TOOLS%\\makecert.exe" -r -pe -ss PrivateCertStore -n "CN=hello_win_kernel_cert" hello_win_kernel_cert.cer",
""%WIN_SDK_TOOLS%\\signtool.exe" sign /fd sha256 /v /s PrivateCertStore /n "hello_win_kernel_cert" /t http://timestamp.digicert.com /a %TARGET_PATH%/hello_win_kernel.sys"
]
[tasks.deploy]
dependencies = ["sign-driver"]
script = [
"mkdir bin",
"move hello_win_kernel_cert.cer bin\\hello_win_kernel_cert.cer",
"move %TARGET_PATH%\\hello_win_kernel.sys bin\\hello_win_kernel.sys"
]
[tasks.cleanup]
script = [
"rmdir /s /q bin",
"cargo clean"
]
构建、重命名、签名和部署:
cargo make deploy
清洁一切:
cargo make cleanup
仅签名:
cargo make sign-driver
在 Windows 虚拟机中进行测试
确保启用测试签名和内核调试(如果更改设置,则重新启动):
bcdedit -set TESTSIGNING ON
bcdedit /debug on
复制到虚拟机:
bin/hello_win_kernel.sysbin/hello_win_kernel_cert.cer- 系统内部
DebugView.exe
在调试视图中:
- 启用 捕捉
- 检查 捕获内核
- 启用 详细内核输出
注册驱动服务:
sc create hello_win_kernel binPath= "FullPath\hello_win_kernel.sys" type= kernel start= demand
启动它:
sc start hello_win_kernel
如果一切正确,DebugView 将显示:
[hello_win_kernel.sys] Hello, world!

C++ 比较(hello world 驱动程序)
作为参考,这里是 C++ 中的最小等效驱动程序条目:
#include <wdm.h>
extern "C" {
DRIVER_INITIALIZE DriverEntry;
}
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#endif
_Use_decl_annotations_
NTSTATUS DriverEntry([[maybe_unused]] PDRIVER_OBJECT DriverObject,
[[maybe_unused]] PUNICODE_STRING RegistryPath)
{
DbgPrint("[hello-win-kernel-cpp.sys] Hello, World!\n");
return STATUS_SUCCESS;
}
总结
您已经在 Rust 中构建了一个最小的 Windows 内核驱动程序:每晚配置,与 WDK 库链接,实现了 no_std 入口点,并通过 DebugView 验证内核输出。如今,在这个领域,Rust 比 C/C++ 感觉更“手动”,但它提供了强大的语言级安全工具,值得设置成本。
从这里开始,最有成效的后续步骤是 合法的驱动程序原语:
- 创建设备对象和符号链接
- 实施 IRP 调度例程
- IOCTL通讯
- 内核的安全包装器 APIs
- 日志记录和结构化错误处理模式
no_std
参考文献
- 货物构建脚本: https://doc.rust-lang.org/cargo/reference/build-scripts.html
- MSVC 链接器选项: https://learn.microsoft.com/cpp/build/reference/linker-options?view=msvc-170
- WDK下载: https://learn.microsoft.com/windows-hardware/drivers/download-the-wdk
- 系统内部套件: https://learn.microsoft.com/sysinternals/downloads/sysinternals-suite