PDA

Просмотр полной версии : Создание драйвера под windows: часть 1 – введение


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

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

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

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

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

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

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

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

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

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

Системные службы (System Services a.k.a System Calls)
В своем роде это прокладка, которая позволяет программе отправлять запросы в Ядро операционной системы, для выполнения нужных ей операций.

Архитектура системы (System Architectures)
Это сложно описать словами коротко, проще один раз увидеть картинку.
В упрощённом виде это выглядит так:


https://forum.antichat.xyz/attachments/4908528/img_e0217d7fae.png


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

Для более глубокого понимания Операционных систем могу посоветовать следующие материалы:
Книги:

Таненбаум, Бос: Современные операционные системы

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)


https://forum.antichat.xyz/attachments/4908528/img_b8e1340e30.png

и далее по списку.
2. Windows 10/11 SDK (последней версии)
Windows 10 SDK - Windows app development

https://forum.antichat.xyz/attachments/4908528/img_224e42c4eb.png

Тут все просто. Качаем iso файл, монтируем и запускаем установщик.
3. Windows 10/11 Driver Kit (WDK)
Download the Windows Driver Kit (WDK) - Windows drivers

https://forum.antichat.xyz/attachments/4908528/img_61a586b770.png

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

https://forum.antichat.xyz/attachments/4908528/img_b5348f8065.png

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

https://forum.antichat.xyz/attachments/4908528/img_890949a0d5.png

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

https://forum.antichat.xyz/attachments/4908528/img_15d7980c74.png

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

После того, как все было установлено, пора запускать Visual Studio и начинать писать драйвер.
Создание проекта
Запускаем Visual Studio и создаем новый проект. Создадим пустой проект „Empty WDM Driver“

https://forum.antichat.xyz/attachments/4908528/img_1b9ff5a69c.png

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

https://forum.antichat.xyz/attachments/4908528/img_72a161cc39.png

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

https://forum.antichat.xyz/attachments/4908528/img_d9c671f609.png

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

https://forum.antichat.xyz/attachments/4908528/img_893cbced29.png

https://forum.antichat.xyz/attachments/4908528/img_c3f52295e2.png

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

https://forum.antichat.xyz/attachments/4908528/img_d6fcc7fe3a.png

Первый драйвер
Сначала импортируем

ntddk.h

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

DriverEntry

, как функция

Main

в обычной программе. Готовый прототип этой функции выглядит так

C++:



#include
NTSTATUS
DriverEntry
(
_In_ PDRIVER_OBJECT DriverObject
,
_In_ PUNICODE_STRING RegistryPath
)
{
/*
In_ это часть SAL(Source Code Ananotation Language) Аннотации не видимы для компилятора,
но содержат метаданные которые, улучшают анализ и чтение кода.
*/
return
STATUS_SUCCESS
;
}


Если мы попробуем собрать наш проект, то получим следующие ошибки и предупреждения.

https://forum.antichat.xyz/attachments/4908528/img_be8358f458.png

В данном случае пункт 1 является следствием пунктов 2 и 3. Дело в том, что по дефолту в Visual Studio некоторые “предупреждения” расцениваются как ошибки.
Чтобы решить эту проблему есть 2 пути.

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

Более правильный и классический метод это использовать макросы в c++. Как видно из сообщения с кодом C4100 объекты RegistryPath и DriverObject не упомянуты в теле функции. Подробнее тут.
Для того, чтобы избавиться от предупреждений, и заставить наш код работать, стоит поместить объекты в макрос

UNREFERENCED_PARAMETER(ObjectName)


C++:



include

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"

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

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

https://forum.antichat.xyz/attachments/4908528/img_af1f5dc5a9.png

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

KdPrint(());

да именно в двойных кавычках.

Итоговый код драйвера будет выглядеть так:

C++:



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


Собираем или пересобираем драйвер.

https://forum.antichat.xyz/attachments/4908528/img_b57f033c48.png



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


https://forum.antichat.xyz/attachments/4908528/img_8a6f8f2143.png

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

https://forum.antichat.xyz/attachments/4908528/img_3dc5d4a962.png

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

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

bcdedit /set testsigning on

.

https://forum.antichat.xyz/attachments/4908528/img_e1411eaf16.png

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

https://forum.antichat.xyz/attachments/4908528/img_0d15559203.png

Возвращаемся в папку с драйвером и копируем его в виртуальную машину. Теперь нам надо создать службу для запуска драйвер. Открываем консоль от имени администратора и вводим следующую команду:


sc create Name type= kernel binPaht= PATH_TO_DRIVER


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

https://forum.antichat.xyz/attachments/4908528/img_79b215dd57.png

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

https://forum.antichat.xyz/attachments/4908528/img_0ccca3ebe6.png

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


sc start античат Driver


https://forum.antichat.xyz/attachments/4908528/img_97c407b3ff.png

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

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

Debug Print Filter

.

https://forum.antichat.xyz/attachments/4908528/img_5375ebce58.png

В качестве значения задаем

DWORD

с именем

DEFAULT

и определяем данные для значения как

8

.

https://forum.antichat.xyz/attachments/4908528/img_4647a93baa.png

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

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

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

https://forum.antichat.xyz/attachments/4908528/img_5fe345c9d9.png

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

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

https://forum.antichat.xyz/attachments/4908528/img_2b53477288.png

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

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

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

migu
06.07.2021, 19:54
ну круто конечно. особенно про Павла, кто не слышал про утилиты sysinternals

MLNK
06.07.2021, 20:23
Ну да вообще вещи которые могу выручать очень сильно. На самом деле я думаю много кто о них не знаешь, или знает про базовые типо Process explorer

svatostop
07.07.2021, 22:06
Спасибо за статью !! жду следующую часть

MLNK
08.07.2021, 00:07
svatostop сказал(а):

Спасибо за статью !! жду следующую часть


следующие будут покороче ) но уже более технические

rusrst
08.07.2021, 12:24
Есть ещё хорошая книжка на русском windows driver foundation.

MLNK
08.07.2021, 16:00
rusrst сказал(а):

Есть ещё хорошая книжка на русском windows driver foundation.



https://forum.antichat.xyz/attachments/4908587/1625745554022.png

Я так понимаю - ты об этой книге. Просмотрел содержание, выглядит неплохо. Спасибо потом почитаю.

rusrst
08.07.2021, 16:07
M4sc0t сказал(а):

Я так понимаю - ты об этой книге. Просмотрел содержание, выглядит неплохо. Спасибо потом почитаю.


Она самая, к ней в идеале fx2lp брать(но с ней придется много разбираться, это всё-таки не совсем системное программирование и нюансов в ней много, с учётом того что там даже ядро слегка изменено от классической 51 серии), стоимость которой с Алика 450 рублей. Но и так есть что почитать. Кстати, перевода упоминаемой книги нет случайно?

MLNK
08.07.2021, 16:17
rusrst сказал(а):

Она самая, к ней в идеале fx2lp брать(но с ней придется много разбираться, это всё-таки не совсем системное программирование и нюансов в ней много, с учётом того что там даже ядро слегка изменено от классической 51 серии), стоимость которой с Алика 450 рублей. Но и так есть что почитать. Кстати, перевода упоминаемой книги нет случайно?


На сколько я знаю его нет в природе. Книга 2019 года пока не переводилась. Но написано просто, хороший язык без "умничества" так что, владея английским хотя бы на Б1 можно читать редко подсматривая в словарь.

rusrst
09.07.2021, 10:22
А почему с++, чем pure c не угодил то?

MLNK
09.07.2021, 10:24
rusrst сказал(а):

А почему с++, чем pure c не угодил то?


будет скорее гибрид С/С++ как я и писал в статье, я опираюсь на книгу. в книге С/С++

Deniss Matjusevs
09.07.2021, 16:38
На какую конкретно книгу?

https://forum.antichat.xyz/attachments/4908627/1625834284967.png

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

MLNK
09.07.2021, 22:53
Deniss Matjusevs сказал(а):

На какую конкретно книгу?

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



https://forum.antichat.xyz/attachments/4908639/1625856732439.png

насчет ошибки, удали файл inf в проекте.
вот на эту.

UserName011
12.07.2021, 07:44
M4sc0t сказал(а):

На сколько я знаю его нет в природе. Книга 2019 года пока не переводилась. Но написано просто, хороший язык без "умничества" так что, владея английским хотя бы на Б1 можно читать редко подсматривая в словарь.


Не так давно была выпущена на русском Работа с ядром Windows. Это не она?

MLNK
12.07.2021, 08:30
M0r7iF3r сказал(а):

Не так давно была выпущена на русском Работа с ядром Windows. Это не она?


О круто, вроде как она. Вышла только в апреле 2021 потому и не знал. Надо будет поискать.

Gesfer
20.05.2023, 11:53
У меня почему-то в Win 7 при установке wdk не устанавливается расширение для Visual Studio 2019 с такой ошибкой- VSIXInstaller.NoApplicableSKUsException: Это расширение не может быть установлено ни для одного из установленных продуктов.
В проектах шаблонов у меня нет проекта Empty DVM Driver это из-за семерки?

Gesfer
20.05.2023, 20:16
Оказывается проблема была не в Win 7, версия wdk должна соответствовать версии sdk как написано в статье, после установки расширения шаблон проекта Empty DVM Driver, стал доступен!