Найти в Дзене

Как написать простой драйвер для Windows на Rust и сохранить все плюсы языка

Разработка драйверов для Windows традиционно ассоциируется с языком C/C++ и набором инструментов WDK (Windows Driver Kit). Однако с ростом популярности Rust появляется всё больше энтузиастов и профессионалов, желающих использовать безопасный и надёжный язык для столь «низкоуровневых» задач. В недавней статье «Writing a Simple Driver in Rust» автор делится опытом создания простого WDM-драйвера на Rust. Ниже — мой взгляд на основные моменты и технические детали, которые могут быть интересны как новичкам, так и матерым «драйверостроителям». 🦀 Безопасность памяти и потоков. Rust славится встроенной защитой от целого ряда ошибок, традиционно свойственных С/С++ (утечки памяти, гонки данных и прочих проблем). На уровне ядра такая защита особенно ценна: любой сбой может привести к синим экранам (BSOD) и другим неприятным последствиям.
🦾 Системный язык. В отличие от высокоуровневых языков, Rust предоставляет тонкий контроль над ресурсами и компоновкой данных. Этот контроль необходим для разра
Оглавление

Разработка драйверов для Windows традиционно ассоциируется с языком C/C++ и набором инструментов WDK (Windows Driver Kit). Однако с ростом популярности Rust появляется всё больше энтузиастов и профессионалов, желающих использовать безопасный и надёжный язык для столь «низкоуровневых» задач. В недавней статье «Writing a Simple Driver in Rust» автор делится опытом создания простого WDM-драйвера на Rust. Ниже — мой взгляд на основные моменты и технические детали, которые могут быть интересны как новичкам, так и матерым «драйверостроителям».

Почему именно Rust?

🦀 Безопасность памяти и потоков. Rust славится встроенной защитой от целого ряда ошибок, традиционно свойственных С/С++ (утечки памяти, гонки данных и прочих проблем). На уровне ядра такая защита особенно ценна: любой сбой может привести к синим экранам (BSOD) и другим неприятным последствиям.
🦾
Системный язык. В отличие от высокоуровневых языков, Rust предоставляет тонкий контроль над ресурсами и компоновкой данных. Этот контроль необходим для разработки драйверов, где каждая операция может работать напрямую с памятью и аппаратными регистрами.
⚙️
Экосистема. Система сборки (Cargo) и репозиторий пакетов (crates.io) позволяют инкапсулировать повторяющиеся задачи и быстрее подключать полезные библиотеки.

Подготовка окружения и ключевые нюансы

⚙️ Установка WDK
Для Windows-драйверов Rust не отменяет классического окружения: нужен установленный Windows Driver Kit, а также LLVM/Clang. Многие существующие руководства (включая
Windows Drivers-rs) подсказывают, как правильно настроить проект.

📁 Структура проекта
Создаём типичную библиотеку Rust:

new --lib booster

Внутри папки booster создаём файл build.rs и корректируем Cargo.toml, чтобы настроить статическую линковку и зависимости от wdk-, wdk-sys-, wdk-alloc-пакетов. Главная задача — заставить Cargo корректно собрать драйвер как «cdylib» (по сути, аналог DLL), совместимую с ядром Windows.

🚫 no_std
Так как в пространстве ядра отсутствует стандартная библиотека C runtime, Rust-проекты для драйверов включают директиву #![no_std]. При этом «вырубается» часть стандартного функционала. На помощь приходят
alloc-библиотеки, позволяющие использовать Vec, String и другие полезные типы без полного std.

Тонкости реализации и «фишки» Rust-драйвера

🔌 Глобальный аллокатор
Внутри ядра нельзя просто так использовать привычный malloc. Вместо этого Rust-пакеты для WDK предоставляют собственный глобальный аллокатор (WdkAllocator), который делегирует память вызовам вроде ExAllocatePool2. Это важно для корректного управления ресурсами внутри ядра Windows.

🔐 Обработка сбоев
В пользовательском пространстве сбой в Rust обычно вызывает «развёртывание стека» и завершается сообщением об ошибке. В драйверах такой сценарий недопустим — может быть критически поздно. Поэтому часто используется режим «немедленного завершения при сбое» («abort on panic»), при котором драйвер либо аварийно завершает выполнение, либо вызывается BSOD, предотвращая повреждение ядра.

🔧 Связывание с Windows API
Через wdk_sys подключается низкоуровневый API (например, IoCreateDevice, PsLookupThreadByThreadId и т.д.). Дополнительно в Rust мы имеем дело со структурами UNICODE_STRING, IRP-структурами и другими элементами WDK. Для удобства можно писать вспомогательные функции (например, конвертация UTF-16 ↔ Rust String).

Что делает примерный драйвер «Booster»?

Идея проста: драйвер позволяет изменять приоритет потока в системе. В классических уроках по драйверам часто приводят подобный пример на C/C++. А здесь показано, как выполнить аналогичную задачу на Rust:

📝 Создаём устройство: \Device\Booster, а также символьную ссылку \??\Booster, чтобы доступ к драйверу можно было получить обычным вызовом CreateFile из user-space.
📝
Обрабатываем IRP:

  • 🏗️ IRP_MJ_CREATE и IRP_MJ_CLOSE просто отвечают «успешно», чтобы пользователь мог открывать и закрывать дескриптор устройства.
  • 🏋️ IRP_MJ_WRITE принимает структуру с Thread ID и нужным приоритетом, затем вызывает PsLookupThreadByThreadId, а после — KeSetPriorityThread. Если всё хорошо, приоритет потока меняется.

Личный взгляд: плюсы и минусы подхода

🤔 Плюсы:

  • Безопасность: Rust сводит к минимуму риск случайной порчи памяти и проблем с синхронизацией.
  • Расширяемость: экосистема Rust активно развивается, появляются новые обёртки для системных вызовов, упрощающие код драйверов.
  • Удобство в больших проектах: типичные шаблоны вроде RAII (Resource Acquisition Is Initialization - Инициализация через захват ресурса) становятся более предсказуемыми, чем в C++ без умных указателей.

⚠️ Минусы/Сложности:

  • Ограниченные инструменты отладки: Rust-драйверы всё ещё «экзотика»; многие отладочные инструменты и расширения от Visual Studio ориентированы на C/C++.
  • Ранний этап экосистемы: пакеты wdk и wdk-sys не идеальны; некоторые вещи приходится делать вручную, особенно структуры и функции, на которые ещё нет обёрток.
  • Процесс подписывания и установка: драйвер остаётся драйвером. Потребуется ручная подпись (signtool) и загрузка тестового сертификата для отладки на Test Mode.

Как это собрать и установить?

🔨 Сборка:

  • Настраиваем Cargo (см. пример Cargo.toml и build.rs).
  • Запускаем cargo build --release или cargo build --features nightly при необходимости.
  • Получаем booster.dll (который по сути является драйвером). Можно переименовать его в booster.sys или оставить как есть — Windows поддерживает и «.dll» (хотя это нестандартно).

🔏 Подпись и загрузка:

  • Генерируем или берём тестовый сертификат (например, через Visual Studio) и подписываем signtool sign /n wdk /fd sha256 <путь_к_dll>.
  • Устанавливаем драйвер при помощи sc create booster type= kernel binPath= ..., затем sc start booster.

Что дальше?

Проект «Booster» демонстрирует лишь базовый функционал: создание устройства, обмен данными через IRP и несколько системных вызовов. Однако это лишний раз подтверждает, что Rust подходит для разработки драйверов — пусть и с некоторыми компромиссами и нюансами в виде небезопасных (unsafe) блоков и ручной настройки сборки.

На мой взгляд, массовое принятие Rust для драйверов — лишь вопрос времени. Microsoft уже ведёт эксперименты по внедрению Rust в некоторых модулях Windows. Для нас, разработчиков, это шанс сочетать «низкоуровневую мощь» C/C++ с более безопасным и современным подходом к управлению памятью.

Ссылки на оригинальный материал и дополнительные ресурсы

Пусть ваш Rust-драйвер будет стабилен, надёжен и никогда не увидит «синий экран»!