• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Creating a Windows Driver in Rust

stihl

Moderator
Регистрация
09.02.2012
Сообщения
1,167
Розыгрыши
0
Реакции
508
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.


So, you want to write a Windows driver in Rust​


Fair challenge. There isn’t a great amount of documentation out there for writing Windows drivers in Rust. There is the awesome Windows rs project that Microsoft have open-sourced, but even then, only a fraction of coding in the kernel is covered.


I’m hoping this series will be a good step in documenting how I have implemented a driver in Rust, and to learn more about EDR’s (Endpoint Detection and Response) and hopefully come up with my own solutions to some problems malware poses.

Before we get cracking, make sure to check out my Rust Windows driver on Для просмотра ссылки Войди или Зарегистрируйся!

What is a driver​


A driver is a software component that runs in the kernel, an area of the operating system that exists below the normal version of the operating system you are used to, either as a end user, or as a developer.


A good analogy would be a modern car - most people use a car by sitting in it, pressing a button to switch it on, driving it with pedals and the steering wheel. Heck, you might not even have a gear stick these days! This can be what we consider ‘userland’, the area where every day things take place. Developers will also work in this ‘userland’, such as programming the radio, satnav, Apple CarPlay, lights, etc. This can be thought of as usermode development; in computing terms, this would be making Windows applications such as Spotify, web browsers, etc.


Using the analogy, the kernel would be the computer systems running the car - calculating whether you are about to hit the car in front of you, orchestrating the vehicle switching on by taking the input from the driver hitting the button, managing the engine, power inputs/outputs, handling energy surges, etc. In general, people other than the company who builds the car, or its hardware, will ever have cause to mess with the inner workings of the car. This is the kernel. The Windows kernel is built and maintained by Microsoft, however, third party developers may add capabilities near the kernel through drivers.


These drivers are often used to handle IO operations, such as your keyboard, USB drives, hard drives, network devices, etc. If you use a VPN for example, you will likely have a kernel driver for it, as it needs to intercept communications at the lowest levels to handle encryption / routing, you cannot do this without a driver component.


Getting started​


To get started with driver development you need a few things:


  • A Windows virtual machine (VM) (or an old spare computer, wouldn’t recommend this option, go for the VM).
  • Для просмотра ссылки Войди или Зарегистрируйся installed to your dev PC.
  • Rust toolchain, set to Nightly for this project.
  • The crates listed on the Для просмотра ссылки Войди или Зарегистрируйся GitHub page.
  • Up-to-date LLVM via winget install -i LLVM.LLVM --version 17.0.6 --force
  • Cargo make via cargo install --locked cargo-make --no-default-features --features tls-native
  • Visual Studio 2022 (for developer command prompt, and possibly auto installation of the Windows Driver Kit)
[/QUOTE]


Configuring your VM​


To run and test a driver, you should ideally deploy it to a VM or onto a test device (NOT your development device). A ‘blue screen of death’ is caused by a bug in a driver; so imagine locking yourself out of your PC because the driver you are developing blue screens…


Once you have your Windows VM setup, you need to do the following in an elevated (admin) terminal:


(please please please please please do not do these on your normal Windows computer, only in your VM, as it’s disabling some settings to allow you to test an unsigned driver and poses a security risk)


  1. bcdedit -set TESTSIGNING ON
  2. bcdedit /debug ON
  3. bcdedit /dbgsettings serial debugport:1 baudrate:115200

Before you restart the VM, open the settings and create a new serial port, the serial port number should equal the debugport number in the above instructions. Your settings should match:

Посмотреть вложение 4781
This serial port will be used from your host to communicate between the debugger (windbg) and the VM kernel. This will allow you to see DbgPrints (basically a printf for the kernel) but also interact with kernel memory.


You will also need to disable secure boot in your VM settings.


You should also download into your VM Для просмотра ссылки Войди или Зарегистрируйся from SysInternals which will allow you to view DbgPrint messages from within the VM without requiring to attach WinDbg to the VM kernel from your host.
[/QUOTE]


Creating a simple driver​


To create the Windows driver in Rust the first thing you need to do is set up a new project via cargo new <Driver-name> --lib, adding in the following requirements:


  • cargo add --build wdk-build
  • cargo add wdk wdk-sys wdk-alloc wdk-panic

Add the following to your Cargo.toml:

[package]
build = "build.rs"

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

[package.metadata.wdk.driver-model]
driver-type = "WDM"

[features]
default = []
nightly = ["wdk/nightly", "wdk-sys/nightly"]

[profile.dev]
panic = "abort"
lto = true

[profile.release]
panic = "abort"
lto = true

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


Create a build.rs file in your project root:

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

[/QUOTE]


The driver entry file​


Just like usermode programs usually start with a main() function, a driver begins with the symbol DriverEntry. This is the entry point into the driver. Before we move on to the code, we need to set up a few things.


Firstly, we specify that we’re operating in a no_std (no standard library) environment. This is because the standard Rust library isn’t available in kernel mode, so we can’t rely on it. Instead, we’ll import specific components from the Windows Driver Kit (WDK) that provide the functionality we need. Whilst we cant use the standard library, we can use the core library.


We also add a global allocator so we can interact with the heap using Rust’s Для просмотра ссылки Войди или Зарегистрируйся. Even without the standard library, we still need the ability to allocate memory dynamically, and the global allocator makes this possible.


Now, a few things to note about the code below.


Our DriverEntry function takes in a DRIVER_OBJECT. This represents the image of a loaded kernel-mode driver. It’s essentially a data structure that stores state information about our driver, some of which we will need later on.


In the code, we use the println!() macro. This isn’t the standard println!() you’re familiar with in Rust’s usermode applications. Instead, it’s a macro provided by the Windows driver crate that prints output to the kernel debugger. This is really helpful because standard output isn’t available in kernel mode, so this is how we can log messages for debugging purposes. Normally in C, we would make a call to Для просмотра ссылки Войди или Зарегистрируйся, we can do this in Rust, but using println!() is a little more idiomatic.
[/QUOTE]

#![no_std]
extern crate alloc;

#[cfg(not(test))]
extern crate wdk_panic;

#[cfg(not(test))]
use wdk_alloc::WdkAllocator;

use wdk::{nt_success, println};
use wdk_sys::STATUS_SUCCESS;

#[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!");

STATUS_SUCCESS
}


This code sets up our basic driver. We declare that we’re in a no_std environment and bring in the necessary crates from the WDK. The DriverEntry function is marked with #[export_name = “DriverEntry”] so that the linker knows this is the entry point.


Preparing to build​


We aren’t finished just yet!


Next we need to create a makefile.toml in the project root which should look like:

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

[config]
load_script = '''
#!@rust
//!
Код:
cargo
//! [dependencies]
//! wdk-build = "0.3.0"
//!
#![allow(unused_doc_comments)]

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

And add a config file to: .cargo/config.toml:

[build]
rustflags = ["-C", "target-feature=+crt-static"]

[/QUOTE]

Finally, it’s time to create an INX file for your driver. This file should have the same name as your project specified in your Cargo.toml file. An INX file is essentially a template that contains all the necessary information about your driver. When you build your project, this INX file is processed and transformed into an INF file (installation file), which Windows uses to install your driver properly.


So, what exactly does the INX file do? It tells Windows how to install your driver, where to place the files, how to register the driver with the system, and any other setup instructions needed. It’s a critical piece of the puzzle because, without it, Windows wouldn’t know how to handle your driver.


To get started, you can refer to the Для просмотра ссылки Войди или Зарегистрируйся provided by Microsoft. This sample gives you a good baseline to work from. However, you’ll need to make some changes to tailor it to your driver.


One important modification is generating your own GUID (Globally Unique Identifier). This identifier ensures that your driver is uniquely recognized by the system. You can generate a new GUID using Для просмотра ссылки Войди или Зарегистрируйся, which comes with Visual Studio. Just run guidgen.exe, and it will spit out a new GUID for you to use.


Now, the INX file contains several fields and sections, such as:


  • Version Section: Specifies the version and signature of your driver.
  • Manufacturer Section: Indicates the manufacturer details.
  • Driver Installation Sections: Defines how and where your driver files are copied and registered.

Understanding these sections is crucial because they dictate how your driver interacts with the operating system during installation. Misconfiguring any of these fields can lead to installation failures or your driver not functioning correctly.


I highly recommend reading up on the specifics of each field in the INX file. Microsoft provides solid documentation on the structure and syntax of INX/INF files. Getting familiar with these details will save you a lot of headaches down the line.


Building​


Finally, open Developer Command Prompt for VS 2022 as admin (included with VisualStudio 2022), and navigate to your project root. From here, run cargo make and you should see your driver being built in front of your eyes! Be warned, it can take a good 30 seconds even for a hello world driver to compile.


You should now find your driver in /target/debug/drivername_package/drivername.sys. The sys file is your driver :).


If something hasn’t built properly, you may have to add certain WDK folders to your PATH, honestly my first time building a driver in Rust was rough it took some fiddling. But stick to the above and tweak here and there, and hopefully you should get it to build. If you have problems, feel free to reach out to me on Twitter!
[/QUOTE]


Running your driver​


If you haven’t rebooted your VM after making the updates found above, you’ll need to give it a reboot.


Start DebugView which you should have downloaded into your VM - this needs to be run as admin. Once open, in the menu bar -> Capture, ensure you tick Capture Kernel and Enable Verbose Kernel Output.


After this, copy over your .sys driver file to the test VM, and register a service with (in an elevated terminal):

sc.exe create my_driver binPath=C:\Users\User\Desktop\driver\driver.sys type=kernel
sc.exe start my_driver


If all went well, you should now see the DbgPrint messages!


To make sure you can see debug messages in a kernel attached WinDbg from your host, enter the following command once you have broken on attaching:

ed nt!Kd_DEFAULT_Mask 0xFFFFFFFF

[/QUOTE]
 
Activity
So far there's no one here
Сверху Снизу