使用Rust进行Windows驱动开发(HelloWorld)

383次阅读

使用Rust进行Windows驱动开发(HelloWorld)

依赖

往常进行Windows驱动开发一般使用Visual Studio和WDK,使用Rust开发Windows驱动的话就不用VS了,可惜宇宙第一IDE不支持Rust,要是支持的话使用VS写Rust也未尝不可,嗯。

虽然说不用VS,但是使用Rust开发Windows驱动也需要依赖一些工具。

winget

在安装其它工具之前建议大家安装winget,可以方便后续的操作。

下载安装可以直接去
https://github.com/microsoft/winget-cli/releases
下载Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle
双击运行安装即可。
安装软件需要Windows版本在Windows 10 1809 (或更高版本),如果系统版本太低,那么后续的操作也无法进行了。

修改source

默认winget下载包是从github的https://github.com/microsoft/winget-pkgs这个仓库下载的
如果你的网络访问Github慢的话可以考虑修改源(不过我测试效果不怎么好)。

winget source remove winget

winget source add winget https://mirrors.ustc.edu.cn/winget-source

安装好winget即可快速安装以下依赖。

Rust和Rust-MSVC-Toolchain

Rust

winget安装

Rust的安装可以直接通过winget安装

winget install Rustlang.Rustup
官网安装(推荐)

上面会直接安装rustup,然后执行rustup-init去下载rust相关的工具链,但是由于rustup源在境外,也会很慢。

去到Rust官网https://www.rust-lang.org/learn/get-started下载Rust-init.exe,
执行后选择1、Quick install via the Visual Studio Community installer,将会引导你安装VS、MSVC编译器和WDK
选择2则自定义安装,选择3则会默认安装msvc工具链。

如果选择1的话则会安装VS,选择2则自定义安装,选择3则标准按照,这里推荐选择3默认按照。

如果下载慢,可以在终端执行前设置源

set RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
./rust-init.exe

Rust-MSVC—Toolchain

Rust安装后一般都会安装一个toolchain,如果是安装的Mingw的GNU-Toolchain,则需要安装MSVC-Toolchain,通过命令查看当前Rust的toolchain以及安装切换其它的toolchain。不过上面如果选择3标准安装的话,默认应该装的是msvc toolchain,下面就不用操作了。

# 查看当前所有的toolchain
rustup toolchain list

#
 安装msvc toolchain
rustup toolchain install stable-x86_64-pc-windows-msvc

#
 切换toolchain
rustup default stable-x86_64-pc-windows-msvc

LLVM

LLVM主要使用用于生成Windows驱动的绑定,安装也可以直接通过winget即可

winget install llvm

下载慢可以手动下载安装:
https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.5/LLVM-18.1.5-win64.exe

SDK

安装了msvc toolchain还需要下载它的链接器,可以下载C++生成工具安装,不用安装VS。
https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/

这个工具和VS安装差不多,但是可以不安装VS,我们只需要安装以下几个:
1、C++生成工具核心功能
2、Windows SDK 和WDK版本选择一致(最低22621)
3、MSVC生成工具(随意版本)

安装这三个之后,需要的相关工具就都有了。

WDK

WDK有几个版本,可以选择安装

# 搜索WDK版本列表
winget search wdk

#
 搜索WDK得到ID,安装WDK
winget install Microsoft.WindowsWDK.10.0.22621

也可以去微软官网下载安装。

SDK和WDK版本选择22621,因为这个版本的安装路径才符合wdk-build的搜索规则,不然执行cargo make会报找不到文件的错误

Cargo-Make(可选)

如果需要执行cargo make则需要安装

cargo install cargo-make

工程

1、新建工程

cargo new rust_driver --lib

2、添加依赖

cd rust_driver
cargo add --build wdk-build
cargo add wdk wdk-sys wdk-alloc wdk-panic

3、工程文件

cargo.toml修改

[package]
name = "rust_driver"
version = "0.1.0"
edition = "2021"


[dependencies]
wdk = "0.2.0"
wdk-alloc = "0.2.0"
wdk-panic = "0.2.0"
wdk-sys = "0.2.0"

[build-dependencies]
wdk-build = "0.2.0"

[lib]
crate-type = ["cdylib"]

[package.metadata.wdk]

[profile.dev]
panic = "abort"
lto = true # optional setting to enable Link Time Optimizations

[profile.release]
panic = "abort"
lto = true # optional setting to enable Link Time Optimizations

build.rs

同cargo.toml同一级

// Copyright (c) Microsoft Corporation
// License: MIT OR Apache-2.0

use std::fs::{OpenOptions};

use std::io::Write;

fn p(s: String){
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open("output.txt")
        .expect("Unable to open file");

    writeln!(file, "{}", s).expect("Unable to write to file");
}

fn main() -> Result<(), wdk_build::ConfigError> {

    p(format!("This is a debug message from build.rs"));


    let wdk_sys_crate_dep_key =
        format!("DEP_WDK_{}""wdk_config".to_ascii_uppercase());
    let wdk_crate_dep_key = format!(
        "DEP_WDK-SYS_{}",
        "wdk_config".to_ascii_uppercase()
    );

    let wdk_sys_crate_config_serialized = std::env::var(&wdk_sys_crate_dep_key);
    let wdk_crate_config_serialized = std::env::var(&wdk_crate_dep_key);

    match wdk_sys_crate_config_serialized {
        Ok(s) =>{
            p(format!("{}",s.replace("\"KMDF\":{\"kmdf_version_major\":1,\"kmdf_version_minor\":33}","\"WDM\":[]")));
            std::env::set_var(&wdk_sys_crate_dep_key,s.replace("\"KMDF\":{\"kmdf_version_major\":1,\"kmdf_version_minor\":33}","\"WDM\":[]"));
        }
        Err(_) =>{}
    }

    match wdk_crate_config_serialized {
        Ok(s) =>{
            p(format!("{}",s.replace("\"KMDF\":{\"kmdf_version_major\":1,\"kmdf_version_minor\":33}","\"WDM\":[]")));
            std::env::set_var(&wdk_crate_dep_key,s.replace("\"KMDF\":{\"kmdf_version_major\":1,\"kmdf_version_minor\":33}","\"WDM\":[]"));
        }
        Err(_) =>{}
    }

    wdk_build::Config::from_env_auto()?.configure_binary_build();
    Ok(())
}

build.rs这里修改了驱动类型,默认是KMDF类型的驱动,改成WDM。
微软的wdk项目目前0.2.0版本是无法指定驱动类型的,maybe。

lib.rs

驱动程序的入口

// 不使用标准库
#![no_std]

// panic处理
#[cfg(not(test))]
extern crate wdk_panic;

// 内存分配
use core::alloc::{GlobalAlloc, Layout};

use wdk_sys::{
    ntddk::{ExAllocatePool, ExFreePool},
    SIZE_T,
    _POOL_TYPE::NonPagedPool,
};

pub struct WDKAllocator;

unsafe impl GlobalAlloc for WDKAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        let ptr =
            unsafe {
                ExAllocatePool(NonPagedPool, layout.size() as SIZE_T)
            };
        if ptr.is_null() {
            return core::ptr::null_mut();
        }
        ptr.cast()
    }

    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
        unsafe {
            ExFreePool(ptr.cast());
        }
    }
}

#[cfg(not(test))]
#[global_allocator]
static GLOBAL_ALLOCATOR: WDKAllocator = WDKAllocator;

use wdk::println;

use wdk_sys::{
   DRIVER_OBJECT,
   NTSTATUS,
   PCUNICODE_STRING,
};

pub unsafe extern "C" fn driver_unload(_driver: *mut DRIVER_OBJECT) {
    println!("driver_unload");
}

// 入口
#[export_name = "DriverEntry"] // WDF expects a symbol with the name DriverEntry
pub unsafe extern "system" fn driver_entry(
   driver: &mut DRIVER_OBJECT,
   _registry_path: PCUNICODE_STRING,
) -> NTSTATUS {
    println!("Hello World");

    driver.DriverUnload = Some(driver_unload);
    0
}

wdk crate默认内存分配器用的是微软的wdk_alloc中的WDKAllocator,它使用了ExAllocatePool2申请非分页内存,如果你的操作系统版本较低,则这里会有问题,因为低版本的内核没有ExAllocatePool2这个导出函数,加载驱动时会报0xc0000263错误。

解决办法是自己实现WDKAllocator,不使用ExAllocatePool2。至少目前0.2.0的版本不支持,后续可能会有更新_NT_TARGET_VERSION,就可以针对指定windows版本开发驱动了(maybe)。最好是通过条件编译针对不同的windows版本选择不同的API。

MakeFile.toml(可选)

同cargo.toml同一级,主要是执行cargo make时重命名dll文件为sys文件,移动pdb符号文件以及添加测试签名等操作。

extend = "target/rust-driver-makefile.toml"

[env]
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true

[config]
load_script = '''
#!@rust
//! ```cargo
//! [dependencies]
//! wdk-build = "0.2.0"
//! ```
#![allow(unused_doc_comments)]

wdk_build::cargo_make::load_rust_driver_makefile()?
'''

inx文件(可选)

rust_driver.inx,与cargo.toml同一级

;===================================================================
; rust_driver
; Copyright (c) Microsoft Corporation
;===================================================================

[Version]
Signature   = "$WINDOWS NT$"
; 使用你的驱动名称
Class       = rust_driver
; 使用VS生成GUID
ClassGuid   = {5E47E5F6-8CEE-4EC0-B56A-18D92CD6E4D8}
Provider    = %ProviderString%
PnpLockDown = 1

[DestinationDirs]
DefaultDestDir = 13

[SourceDisksNames]
1 = %DiskId1%,,,""

; 你的驱动名称
[SourceDisksFiles]
rust_driver.sys = 1,,

; ================= Class section =====================

[ClassInstall32]
Addreg=SampleClassReg

[SampleClassReg]
HKR,,,0,%ClassName%
HKR,,Icon,,-5

; ================= Install section =================

[Manufacturer]
%StdMfg%=Standard,NT$ARCH$.10.0...16299

[Standard.NT$ARCH$.10.0...16299]
%DeviceDesc%=SampleKMDFDevice, root\SAMPLE_KMDF_HW_ID

[SampleKMDFDevice.NT$ARCH$]
CopyFiles=Drivers_Dir

; 你的驱动名称
[Drivers_Dir]
rust_driver.sys

; ================= Service installation =================
[SampleKMDFDevice.NT$ARCH$.Services]
AddService = SampleKMDFService, %SPSVCINST_ASSOCSERVICE%, Sample_KMDF_Service_Install

[Sample_KMDF_Service_Install]
DisplayName    = %ServiceDesc%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START 
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
; 你的驱动名称
ServiceBinary  = %13%\rust_driver.sys

; ================= Strings =================
[Strings]
SPSVCINST_ASSOCSERVICE = 0x00000002
; 可改可不改
ProviderString         = "TODO-Set-Provider"
StdMfg                 = "(Standard system devices)"
DiskId1                = "Sample KMDF Installation Disk #1"
DeviceDesc             = "Sample KMDF Rust Driver"
ServiceDesc            = "Sample KMDF Rust Service"
ClassName              = "Sample Device"

4、编译

# 不用makefile.toml和inx文件,直接编译出dll后缀无签名的驱动PE文件

cargo build --profile dev
cargo build --profile release

#
 将会执行build,然后重命名dll文件为sys,移动符号文件,并添加签名,需要makefile.toml和inx文件

cargo make default --profile dev
cargo make default --profile release


#
 清理
cargo clean

总结

至此,就可以加载使用Rust编写的HelloWorld驱动了。

还有一些可以说下:

  • rust首次编译很慢,增量速度很快。
  • WinDebug是支持Rust源码调试的,这点对于使用rust编写驱动还是很有意义的。
  • 微软提供的wdk crate提供了println宏可以方便的打印,因为rust默认字符串用的utf8编码,所以字符操作可能不是很方便。
  • 默认的内存分配器是使用的ExAllocatePool2申请的非分页内存,可能最终非分页内存不足导致错误。如果需要申请分页内存的话,还是需要自己操作管理内存。并且ExAllocatePool2在低版本windows内核没有,导入表有这个函数,低版本windows加载驱动时会报错0xc0000263。
  • 目前的wdk crate版本0.2.0还是有很多不足,比如无法指定驱动类型wdm、kmdf等,默认是kmdf,不过可以修改buil.rs支持。还有无法编译指定操作系统版本的驱动,比如ExAllocatePool2,在VS指定目标系统版本后,编译时就会报错。

正文完
 0
liushui
版权声明:本站原创文章,由 liushui 2024-05-09发表,共计8507字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。