Статья Создание драйвера под Windows. Часть 1: Введение

MLNK

Mod. Ethical Hacking
Red Team
23.01.2018
560
706
BIT
7

Предыстория

Для очередного проекта возникла необходимость написать простенький софтверный драйвер под Windows, но так как опыта в написании драйверов у меня примерно столько же, сколько и в балете, я начал исследовать данную тему. В таких делах я предпочитаю начинать с основ, ибо если кидаться сразу на сложные вещи, то можно упустить многие базовые понятия и приёмы, что в дальнейшем только усложнит жизнь.

После 20 минут поисков по сети я наткнулся на Github Павла Иосифовича (zodiacon - Overview). Личность легендарная в своих кругах, достаточно посмотреть на его репозиторий, публикации и выступления на именитых конференциях. Помимо этого, Павел является автором/соавтором нескольких книг: «Windows Internals» (книга, имеющаяся у меня на полке, которая принесла немало пользы), и «Windows Kernel Programming» 2019 года выпуска (бегло пролистав 11 Глав или 390 страниц, я понял – это то, что нужно!).
Кстати, книгу вы можете купить прямо на сайте Павла

Книгу я приобрёл в бумажной версии, чтобы хоть и немного, но поддержать автора. Безупречное качество, несмотря на то, что она издается в мягком переплете. Хорошие плотные листы формата А4 и качественная краска. (книга без проблем пережила вылитую на нее кружку горячего кофе).
Пока я сидел на балконе и читал четвёртую главу книги, в голову пришла мысль: а почему бы не сделать ряд статей на тему «Программирования драйвера под Windows», так сказать, совместить полезное, с еще более полезным.

И вот я здесь, пишу предысторию.

Как я вижу этот цикл статей и что от него ожидать:
Это будут статьи, которые будут базироваться на вышеупомянутой книге, своеобразный вольный и сокращенный перевод, с дополнениями и примечаниями.

Базовые понятия о внутреннем устройстве Windows (Windows Internals)

Для того, чтобы начать разрабатывать Драйвер под Windows, то есть работать на уровне с ядром ОС, необходимо базовое понимание того, как эта ОС утроена. Так как я хочу сосредоточиться на написании драйвера, а не на теории об операционных системах, подробно описывать понятия я не буду, чтобы не растягивать статью, вместо этого прикреплю ссылки для самостоятельного изучения.

Следовательно, нам стоит ознакомиться с такими базовыми понятиями как:


Процесс – это объект, который управляет запущенной инстанцией программы.​


Технология, позволяющая создавать закрытые пространства памяти для процессов. В своем роде - это песочница.​


Это сущность, которая содержится внутри процесса и использует для работы ресурсы, выделенные процессом - такие, как виртуальная память. По сути, как раз таки потоки и запускают код.​


В своем роде это прокладка, которая позволяет программе отправлять запросы в Ядро операционной системы, для выполнения нужных ей операций.​


Это сложно описать словами коротко, проще один раз увидеть картинку.​
В упрощённом виде это выглядит так:​
image1.png

Дескрипторы и объекты необходимы для регулирования доступа к системным ресурсам.​
Объект — это структура данных, представляющая системный ресурс, например файл, поток или графическое изображение.​
Дескриптор – это некая абстракция, которая позволяет скрыть реальный адрес памяти от Программы в пользовательском режиме.​

Для более глубокого понимания Операционных систем могу посоветовать следующие материалы:
Книги:
  • Таненбаум, Бос: Современные операционные системы
  • Windows Internals 7th edition (Part 1)
Видео:

Настройка рабочего пространства

Для разработки драйвера, как и любого другого софта необходима подходящая среда.
Так как мы работаем в операционной системе Windows, её средствами мы и будем пользоваться.

Что нам понадобится:

1. Visual Studio 2017 и старше.​

(Community Version хватает с головой) Также во вкладке „Individual components” необходимо установить
Код:
MSVC v142 - VS 2019 C++ ARM build tools (Latest)
MSVC v142 - VS 2019 C++ ARM Spectre-mitigated libs (Latest)
MSVC v142 - VS 2019 C++ ARM64 build tools (Latest)
MSVC v142 - VS 2019 C++ ARM64 Spectre-mitigated libs (Latest)
MSVC v142 - VS 2019 C++ ARM64EC build tools (Latest - experimental)
MSVC v142 - VS 2019 C++ ARM64EC Spectre-mitigated libs (Latest - experimental)
MSVC v142 - VS 2019 C++ x64/x86 build tools (Latest)
MSVC v142 - VS 2019 C++ x64/x86 Spectre-mitigated libs (Latest)

image10.png


и далее по списку.

2. Windows 10/11 SDK (последней версии)​



image3.png


Тут все просто. Качаем iso файл, монтируем и запускаем установщик.

3. Windows 10/11 Driver Kit (WDK)​



image4.png


В конце установки вам будет предложено установить расширение для Visual Studio. Обязательно установите его!

image11.png


После закрытия окна установки WDK появится установщик Расширения VisualStudio

image2.png


4. Sysinternals Suite

Скачайте и распакуйте в удобное для вас место. Это набор полезных утилит, которые пригодятся для исследования Windows, дебага драйвера и прочего.

image8.png


5. Виртуальная Машина с Windows для тестов.​

Выбор ПО для виртуализации на ваше усмотрение. Я буду использовать «VMware Workstation 16 pro».
Написанные драйверы лучше тестировать именно в виртуальной машине, так как Ядро - ошибок не прощает, и вы будете часто улетать в синий экран смерти.

После того, как все было установлено, пора запускать Visual Studio и начинать писать драйвер.

Создание проекта

Запускаем Visual Studio и создаем новый проект. Создадим пустой проект „Empty WDM Driver“

image21.png


Называем его как душе угодно.

image24.png


И вот он, наш свеженький чистенький проект для нашего первого драйвера.

image27.png


Теперь необходимо создать cpp файл, в котором мы будем писать сам драйвер.

image7.png


image14.png


Вот и все. Настройку системы и среды мы закончили.

image22.png


Первый драйвер

Сначала импортируем ntddk.h эта одна из базовых библиотек для работы с ядром. Больше информации . Как и у любой программы, у драйвера должна быть точка входа DriverEntry, как функция Main в обычной программе. Готовый прототип этой функции выглядит так
C++:
#include <ntddk.h>
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
/*
    In_ это часть SAL(Source Code Ananotation Language) Аннотации не видимы для компилятора,
    но содержат метаданные которые, улучшают анализ и чтение кода.
*/
    return STATUS_SUCCESS;
}
Если мы попробуем собрать наш проект, то получим следующие ошибки и предупреждения.

image15.png


В данном случае пункт 1 является следствием пунктов 2 и 3. Дело в том, что по дефолту в Visual Studio некоторые “предупреждения” расцениваются как ошибки.
Чтобы решить эту проблему есть 2 пути.
  1. Отключить эту фичу в Visual Studio, что делать не рекомендуется. Так как сообщения об ошибках могут быть полезны и сэкономят вам время и нервы в дальнейшем.
  2. Более правильный и классический метод это использовать макросы в c++. Как видно из сообщения с кодом C4100 объекты RegistryPath и DriverObject не упомянуты в теле функции. Подробнее .
Для того, чтобы избавиться от предупреждений, и заставить наш код работать, стоит поместить объекты в макрос UNREFERENCED_PARAMETER(ObjectName)
C++:
include <ntddk.h>

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);
    return STATUS_SUCCESS;
}
Теперь, если пересобрать проект, то мы увидим, что ошибка С220 и предупреждение C4100 пропали, но к ним на смену пришли LNK2019 и LNK1120. Однако это уже не ошибки компиляции - это ошибки линкера. А кто говорил что будет легко?
О том, что такое линкер можно почитать .

Дело в том, что наша функция не представлена в стандартном линкере С++ и вообще она девушка капризная и хочет Си-линкер. Удовлетворим желание дамы и дадим ей то, чего она хочет.

Делается это просто. Перед функцией надо добавить extern "C" так наш линкер будет понимать, что эта функция должна линковаться С-линкером.

Собираем проект заново и вуаля - Драйвер собрался.

image17.png


Что на данный момент умеет наш драйвер? Сейчас это по сути пустышка, которая после загрузки, в случае успеха, вернет нам сообщения об удачном запуске. Давайте заставим его нас поприветствовать и проверим его работоспособность. Выводить сообщения мы будем при помощи функции KdPrint(()); да именно в двойных кавычках.

Итоговый код драйвера будет выглядеть так:
C++:
#include <ntddk.h>

//Указываем линкеру, что DriverEntry должна линковаться С-линкером
extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    //Убираем варнинг C4100 и связанную с ним ошибку C220
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);
    //Выводим сообщение
    KdPrint(("Hi Codeby, this is our first driver! Yuhu!\n"));
   
    return STATUS_SUCCESS;
}
Собираем или пересобираем драйвер.

image6.png

Важно! Сборка драйвера должна происходить в режиме Debug!!!
image12.png


После чего в папке нашего проекта мы сможем найти результаты нашего труда. Вы только посмотрите на него, какой маленький и хорошенький.

image5.png


Но что делать дальше? Как проверить его работоспособность?
Для этого нам и понадобится наша виртуальная машина с Windows, но перед запуском на ней драйвера, нам придется проделать пару манипуляций. Дело в том, что в Windows есть встроенная защита, и если драйвер не подписан "нужной" подписью ака сертификатом, то драйвер просто не загрузится.

Дальнейшие действия нужно проделать в Windows на виртуальной машине.
Чтобы отключить эту проверку подписи, а точенее перевести Windows в тестовый режим, запустите cmd.exe от имени администратора и введите следующую команду bcdedit /set testsigning on.

image13.png


Перезагрузите виртуальную машину.
Если все прошло удачно, в правом нижнем углу вы увидите следующую надпись (2 нижнее строчки могут отличиться в зависимости от версии Windows)

image9.png


Возвращаемся в папку с драйвером и копируем его в виртуальную машину. Теперь нам надо создать службу для запуска драйвер. Открываем консоль от имени администратора и вводим следующую команду:
sc create Name type= kernel binPaht= PATH_TO_DRIVER

в моем случае это выглядит так:

image19.png


Также проверить успешность создания можно через реестр.

image16.png


В той же консоли мы можем попробовать запустить нашу службу.
sc start CodebyDriver

image18.png


Отлично, драйвер запустился и мы даже не улетели в синьку, а это всегда приятно. Теперь давайте проверим, выводится ли сообщение от драйвера.
Для этого нам необходимо провести подготовительные работы.

Создадим новый ключ в реестре и назовем его Debug Print Filter.

image23.png


В качестве значения задаем DWORD с именем DEFAULT и определяем данные для значения как 8.

image25.png


Перезагружаем виртуальную машину.

После перезапуска запускаем DebugView данный инструмент находится в архиве Sysinternals, который мы ранее скачали. Ее можно смело скопировать в виртуальную машину.

Запускаем DebugView от имени Администратора и ставим галочку “Capture Kerner”

image20.png


Capture Win32 и Capture Global Win32 можно снять, если летит много сообщений.

Затем запускаем консоль от имени администратора и запускаем службу загрузки драйвера.

image26.png


Все отработало отлично, и мы видим приветствие от нашего драйвера!
На этой приятной ноте первая статья из цикла заканчивается. В дальнейших статьях мы добавим функционала нашему драйверу, научим его выгружаться и получать данные.

Спасибо за чтение!

P.S: Я сам только начал изучать тему работы с драйверами. Так что если у вас есть предложения или правки по технической части статьи, прошу отписать в комментарии, чтобы я мог внести изменения в статью.
P.P.S: Как вы могли заметить, писать мы будем преимущественно на С++, посему могу посоветовать отличный канал с уроками по С++ - The Cherno.
 

migu

Grey Team
14.01.2020
224
61
BIT
27
ну круто конечно. особенно про Павла, кто не слышал про утилиты sysinternals
 

MLNK

Mod. Ethical Hacking
Red Team
23.01.2018
560
706
BIT
7
Ну да вообще вещи которые могу выручать очень сильно. На самом деле я думаю много кто о них не знаешь, или знает про базовые типо Process explorer
 

rusrst

Green Team
04.10.2020
22
27
BIT
0
Есть ещё хорошая книжка на русском windows driver foundation.
 
  • Нравится
Реакции: MLNK

rusrst

Green Team
04.10.2020
22
27
BIT
0
Посмотреть вложение 51166
Я так понимаю - ты об этой книге. Просмотрел содержание, выглядит неплохо. Спасибо потом почитаю.
Она самая, к ней в идеале fx2lp брать(но с ней придется много разбираться, это всё-таки не совсем системное программирование и нюансов в ней много, с учётом того что там даже ядро слегка изменено от классической 51 серии), стоимость которой с Алика 450 рублей. Но и так есть что почитать. Кстати, перевода упоминаемой книги нет случайно?
 
  • Нравится
Реакции: MLNK

MLNK

Mod. Ethical Hacking
Red Team
23.01.2018
560
706
BIT
7
Она самая, к ней в идеале fx2lp брать(но с ней придется много разбираться, это всё-таки не совсем системное программирование и нюансов в ней много, с учётом того что там даже ядро слегка изменено от классической 51 серии), стоимость которой с Алика 450 рублей. Но и так есть что почитать. Кстати, перевода упоминаемой книги нет случайно?
На сколько я знаю его нет в природе. Книга 2019 года пока не переводилась. Но написано просто, хороший язык без "умничества" так что, владея английским хотя бы на Б1 можно читать редко подсматривая в словарь.
 
  • Нравится
Реакции: rusrst
21.12.2017
5
0
BIT
11
На какую конкретно книгу?

1625834284967.png

А у меня вот такая ошибка вылезла. А тех ошибок, что вы описали не было вообще.
 

MLNK

Mod. Ethical Hacking
Red Team
23.01.2018
560
706
BIT
7
  • Нравится
Реакции: Deniss Matjusevs

UserName011

Green Team
01.09.2020
129
2
BIT
478
На сколько я знаю его нет в природе. Книга 2019 года пока не переводилась. Но написано просто, хороший язык без "умничества" так что, владея английским хотя бы на Б1 можно читать редко подсматривая в словарь.
Не так давно была выпущена на русском . Это не она?
 

Gesfer

New member
19.05.2023
2
0
BIT
0
У меня почему-то в Win 7 при установке wdk не устанавливается расширение для Visual Studio 2019 с такой ошибкой- VSIXInstaller.NoApplicableSKUsException: Это расширение не может быть установлено ни для одного из установленных продуктов.
В проектах шаблонов у меня нет проекта Empty DVM Driver это из-за семерки?
 
Последнее редактирование:

Gesfer

New member
19.05.2023
2
0
BIT
0
Оказывается проблема была не в Win 7, версия wdk должна соответствовать версии sdk как написано в статье, после установки расширения шаблон проекта Empty DVM Driver, стал доступен!
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!