Драйвер — это просто
Многие считают что самому создать драйвер для Windows это что-то на грани фантастики. Но на самом деле это не так. Конечно, разработка драйвера для какого-то навороченного девайса бывает не простой задачей. Но ведь тоже самое можно сказать про создание сложных программ или игр. В разработке простого драйвера нет ничего сложного и я попытаюсь на примерах это показать.
Сперва нам нужно определится в чем мы же будем создавать наш первый драйвер. Поскольку материал ориентирован на новичков, то язык программирования был выбран один из простых, и это не Си или ассемблер, а бейсик. Будем использовать один из диалектов бейсика — PureBasic. Из коробки он не обучен создавать драйверы, но у него удачный набор файлов, используемых для компиляции и небольшое шаманство позволяет добавить эту возможность. Процесс компиляции состоит из нескольких этапов. Если кратко, то он происходит следующим образом: Сначала транслятор «перегоняет» basic-код в ассемблер, который отдается FASM’у (компилятор ассемблера), который создает объектный файл. Далее в дело вступает линкер polink, создающий исполняемый файл. Как компилятор ассемблера, так и линкер могут создавать драйверы и если немного изменить опции компиляции, то получим не исполняемый файл, типа EXE или DLL, а драйвер режима ядра (SYS).
Скачать немного модифицированную бесплатную демо версию PureBasic 4.61 x86 можно на файлопомойке, зеркало.
Если нужно создать драйвер для x64 системы, качайте эту версию, зеркало.
Дистрибутивы имеют небольшие размеры, около 3 МБ каждый. С помощью этой версии можно создавать только драйвера.
Скачиваем, распаковываем и запускаем, кликнув по файлу «PureBasic Portable». При этом запустится IDE и вылезет окошко с сообщением что это демо-версия и списком ограничений. Из него наиболее существенным является ограничение числа строк кода, равное 800, а для создания простых драйверов этого может хватить. Остальные ограничения в нашем случае, не существенны.
Окно IDE с загруженным кодом драйвера показано на скрине.
Компиляция драйвера выполняется через меню «Компилятор» (это если кто не понял).
Теперь определимся что будет делать наш первый драйвер. Обычно при изучении программирования начинают с простых вещей, скажем, выполнения математических операций и вывода результата. Вот пусть наш драйвер делает тоже самое, ведь банальная математика производимая в режиме ядра это очень круто!
Может показаться что это куча бессмысленного кода, но это не так.
У каждого драйвера должна быть точка входа, обычно у нее имя DriverEntry() и выполнена она в виде процедуры или функции. Как видите, в этом драйвере есть такая процедура. Если посмотрите на начало кода, то в первых строках увидите как ей передается управление. В этой процедуре происходит инициализация драйвера. Там же назначается процедура завершения работы драйвера, которая в нашем случае имеет имя UnloadDriver(). Процедуры CreateDispatch() и CloseDispatch() назначаются обработчиками соединения и отсоединения проги из юзермода.
Процедура DeviceIoControl() будет обрабатывать запросы WinAPI функции DeviceIoControl(), являющейся в данном драйвере связью с юзермодом. В конце кода расположена так называемая ДатаСекция (DataSection), в которой находятся имена драйвера, сохраненные в формате юникода (для этого использована одна из фишек ассемблера FASM).
Теперь рассмотрим как драйвер будет взаимодействовать с внешним миром. Это происходит в процедуре DeviceIoControl(). В ней отслеживается одно сообщение, а именно — #IOCTL_MyPlus, которое отправляет юзермодная прога, когда ей нужно сложить два числа в режиме ядра (круто звучит, правда?). Когда такое сообщение получено, то считываем из системного буфера, адрес указателя на структуру со слагаемыми, производим сложение и результат помещаем в системный буфер. Собственно это основная задача нашего первого драйвера.
Видите сколько понадобилось кода для выполнения простейшей математической операции — сложения двух чисел?
А теперь рассмотрим программу, работающую с этим драйвером. Она написана на том же PureBasic.
При старте программы вызывается функция OpenDriver(), которая загружает драйвер. Для упрощения, имя драйвера, имя службы и описание службы заданы одинаковыми — «pbDrPlus». Если загрузка неудачная, то выводится соответствующее сообщение и программа завершает свою работу.
Процедура Plus() осуществляет связь с драйвером. Ей передаются хэндл, доступа к драйверу и слагаемые числа, которые помещаются в структуру и указатель на указатель которой, передается драйверу. Результат сложения чисел будет в переменной «Result».
Далее следует код простейшего GUI калькулятора, скопированного из википедии.
Когда закроют окно, то перед завершением работы программы, закрывается связь с драйвером и производится его деинсталляция из системы.
Результат сложения чисел 8 и 2 на скриншоте.
Исходные коды драйвера и программы, можно найти в папке «Examples», PureBasic на файлопомойке, ссылку на который давал в начале статьи. Там так же найдете примеры драйвера прямого доступа к порам компа и пример работы с памятью ядра.
PS.
Помните, работа в ядре чревата мелкими неожиданностями аля, BSOD (синий экран смерти), поэтому экспериментируйте осторожно и обязательно всё сохраняйте перед запуском драйвера.
За возможную потерю данных, я ответственности не несу!
Написание драйвера в подробностях №1
Скоро здесь, возможно, будет стоять твоё имя.
1) Способ работы с драйверами как файлами (подробнее см. ниже)
2) Драйвер, как легко заменяемая честь ОС (учитывая уже сказанные выше примечания)
3) Существование режима ядра
Теперь касательно первого пункта. Это значит,
что функции, используемые при взаимодействии с файлами,
как и с драйверами, практически идентичные (имеется в виду лексически):
open, close, read и т.д. И напоследок стоит отметить идентичность механизма
IOCTL (Input/Output Control Code-код управления вводом-выводом)
-запросов.
Ну и напоследок разъясним ещё несколько терминов:
Ну вот, пожалуй, и хватит терминов. В будущем,
если нужны будут какие-нибудь уточнения по теме,
я обязательно их укажу. А теперь, раз уж эта статья
теоретическая, давай-ка взглянем на архитектуру Windows NT с высоты птичьего полёта.
Краткий экскурс в архитектуру Windows NT
1) System Service Interface (Интерфейс системных служб )
2) Configuration Manager (Менеджер конфигурирования)
3) I/O Manager (Диспетчер ввода-вывода,ДВВ)
4) Virtual Memory Manager,VMM (Менеджер виртуальной памяти)
5) Local Procedure Call,LPC (Локальный процедурный вызов )
6) Process Manager (Диспетчер процессов)
7) Object Manager (Менеджер объектов)
Теперь можно поговорить о сторонних утилитах. Некоторые уже включены в стандартную поставку
Windows: редактор реестра. Но их в любом случае не хватит. Надо будем инсталлить отдельно.
Множество наиполезнейших утилит создали патриархи системного кодинга в
Windows: Марк Руссинович, Гарри Нэббет, Свен Шрайбер. и т.д. Вот о них и поговорим.
Марк Руссинович создал много полезных утилит:
RegMon, FileMon (мониторы обращений к реестру и файлам соответственно), WinObj (средство просмотра директорий имен
объектов), DebugView,DebugPrint (программы просмотра, сохранения и т.д. отладочных сообщения) и проч. и проч. Все эти утилиты и огромное количество других можно найти на знаменитом сайте Руссиновича
http://www.sysinternals.com.
Напоследок нельзя не упомянуть такие хорошие проги, как PE
Explorer, PE Browse Professional Explorer, и такие незаменимые, как дизассемблер IDA и лучший отладчик всех времён и народов SoftICE.
Пишем свой первый Windows-драйвер
Итак, после моей предыдущей статьи я понял что тема про программирование драйверов Windows интересна хабровчанам, поэтому продолжу. В этой статье я решил разобрать простую программу-драйвер, которая делает только то, что пишет отладочное сообщение «Hello world!» при старте драйвера и «Goodbye!» при завершении, а также опишу те средства разработки, которые нам понадобятся для того, чтобы собрать и запустить драйвер.
Итак, для начала приведем текст этой несложной программы.
Итак, теперь сначала разберемся, что делает каждая инструкция. Перво-наперво мы подключаем заголовочный файл ntddk.h. Это один из базовых подключаемых файлов во всех драйверах: в нем содержатся объявления типов NTSTATUS, PDRIVER_OBJECT, PUNICODE_STRING, а также функции DbgPrint.
Далее идет объявление двух функций: DriverEntry и UnloadRoutine. Расскажу о первой поподробнее. Итак, как уважаемые читатели знают, в каждой программе есть точка входа, в программах на языке C это функция main или WinMain. В драйвере роль точки входа выполняет функция DriverEntry, которая получает на вход указатель на структуру DriverObject, а также указатель на строку реестра, соответствующую загружаемому драйверу.
Структура DriverObject содержит множество полей, которые определяют поведение будущего драйвера. Наиболее ключевые из них — это указатели на так называемые вызываемые (или callback) функции, то есть функции, которые будут вызываться при наступлении определенного события. Одну из таких функций мы определяем: это функция UnloadRoutine. Указатель на данную функцию помещается в поле DriverUnload. Таким образом при выгрузке драйвера сначала будет вызвана функция UnloadRoutine. Это очень удобно, когда драйвер имеет какие-то временные данные, которые следует очистить перед завершением работы. В нашем примере эта функция нужна только чтобы отследить сам факт завершения работы драйвера.
Для того, чтобы выводить отладочные сообщения мы используем функцию DbgPrint, которая имеет синтаксис, аналогичной функции printf из пользовательского режима (userspace).
В этом простом примере мы использовали также директивы #pragma alloc_text(INIT, DriverEntry) и #pragma alloc_text(PAGE, UnloadRoutine). Объясню что они означают: первая помещает функцию DriverEntry в INIT секцию, то есть как бы говорит, что DriverEntry будет выполнена один раз и после этого код функции можно спокойно выгрузить из памяти. Вторая помечает код функции UnloadRoutine как выгружаемый, т.е. при необходимости, система может переместить его в файл подкачки, а потом забрать его оттуда.
Вы можете задуматься, мол ну с первой-то директивой понятно, типа оптимизация и все такое, но зачем мы используем вторую директиву, зачем помечать код как возможный к выгрузке в файл подкачки? Поясню этот вопрос: каждый процесс в системе имеет такой параметр, как IRQL (подробнее читаем по ссылке Interrupt request level ибо это материал отдельной статьи), то есть некоторый параметр, отвечающий за возможность прерывания процесса: чем выше IRQL тем меньше шансов прервать выполнение процесса. Возможности процесса так же зависят от IRQL: чем выше IRQL тем меньше возможности процесса, это вполне логично, т.е. такой подход побуждает разработчиков выполнять только самые необходимые операции при высоком IRQL, а все остальные действия делать при низком. Вернемся к основной теме, о том, почему мы делаем для функции UnloadRoutine возможность выгрузки в файл подкачки: все опять же сводится к оптимизации: работа с файлом подкачки недоступна при высоком IRQL, а процедура выгрузки драйвера гарантированно выполняется при низком IRQL, поэтому мы специально указываем руками что код функции выгрузки драйвера можно поместить в своп.
Ух, вроде как с обсуждением кода этой, казалось бы небольшой программки закончено, теперь разберемся как скомпилировать и запустить наш драйвер.
Теперь последовательность действий: сначала мы пишем два файла, один называется MAKEFILE, с таким содержимым
а второй называется sources и содержит в себе следующее:
Эти файлы нужны для сборки драйвера. Да, забыл сказать, что в WDK нет встроенной среды разработки, поэтому и нужен текстовый редактор, чтобы набирать текст драйверов. Для этой цели можно использовать и Visual Studio (некоторые даже интегрируют возможность сборки драйверов из VS), и любой другой текстовый редактор.
Данная команда соберет нам драйвер TestDriver.sys и положит его в папку «objchk_wxp_x86\i386».
Теперь нам нужно запустить программу DbgView чтобы увидеть сообщения, которые будет выдавать драйвер. После запуска данной программы нам нужно указать, что мы хотим просматривать сообщения из ядра (Capture->Capture Kernel).
Теперь запукаем программу KmdManager, указываем путь к нашему драйверу (файл TestDriver.sys) нажимаем кнопку Register, затем Run. Теперь драйвер зарегистрирован в системе и запущен. В программе DbgView мы должны увидеть наше сообщение «Hello World!». Теперь завершаем работу драйвера кнопкой Stop и убираем регистрацию драйвера кнопкой Unregister. Кстати, в DbgView дожна появиться еще одна строка.
Итак, чего же мы достигли: мы написали, скомпилировали и запустили свой первый Windows-драйвер! Добавлю только, что при написании сложный драйверов для отладки используется двухмашинная конфигурация, когда на одном компьтере ведется написание драйвера, а на другом — запуск и тестирование. Это делается из-за того, что неправильно написанный драйвер может обрушить всю систему, а на ней может быть очень много ценных данных. Часто в качестве второго компьютера используется виртуальная машина.
сложно ли написать свой драйвер для видеокарты?
а исходники драйверов этих есть?Например уже завтра выйдет Catalyst 11.7
За счет чего это достигается?Каким образом задействуется GPU, какие комманды есть для его вызова?
ага, там скажут что это тайна.А вот тут как раз могут подсказать, специалисты Доктор вэб могут драйвера дисазамблировать, или еще что то сделать.Плюс те форумы на английском, а я ее не знаю толком
Здравствуйте!Вопрос к тем кто шарит: сложно ли написать драйвер для видеокарты (аля Catalyst)
Архитектуру данного GPU.
Архитектуру API ОС (OpenGL, Direct3D самых разных версий).
Cамо собой разумеется, вы должны быть экспертом в области написания kernel mode кода.
И т.д. и т.п.
Бывает что при выходе новых драйверов повышается производительность в играх.За счет чего это достигается?
В основном оптимизация под конкретные программы, т.е. оптимизируется конкретный порядок работы с видео буферами или определённый порядок команд в шейдере, или ещё что. Из оптимизируемых программ у производителей наиболее популярны тесты(типа 3DMark).
Каким образом задействуется GPU, какие комманды есть для его вызова?
В push буфер(память доступная GPU и CPU одновременно) запихивают команды для GPU. Ситуации связанные с его полным вычитыванием, разруливаются через IO порты и прерывания.
а исходники драйверов этих есть?
Дрова NVidia под линух, насколько я помню, поставляются в бинарном виде, без сорцов. И кастрированы по функциональности.
сложно ли написать драйвер для видеокарты (аля Catalyst)
проще не юзать богомерзкий линукс
Имхо, официальные дрова пишутся отнюдь не ламерами,
Боянистый отзыв о качестве видео-драйверов, от программиста пишущего игры под эти дрова.
Как правильно поставить драйвера на дискретную видеокарту?
Здравствуйте, прошу помощи.
В ноутбуке две видеокарты, встроенная и дискретная:
1. Intel(R) HD Graphics 3000
2. AMD Radeon HD 6650M
Ноутбук: Packard Bell EasyNote LS11HR
Система Win 10
Устанавливаю драйвера с сайта AMD, дискретная видюха начинает называться AMD Radeon 6600M and 6700M Series
НО продукты Adobe говорят что не видят GPU
Пробовал также устанавливать через Driver Pack Solution (Он вообще скачивает какие-то драйвера 2018 года, хотя на офф сайте AMD последние доступные 2016)
Еще устанавливал драйвера с офф сайта ноутбука, тогда в диспетчере устройств напротив видюх горит желтый треугольник (windows не может найти драйвера для этих видеоадаптеров или как то так).
Параметры электропитания везде выставлены на максимальную производительность.
В catalyst control center тоже все на макс производительность.
Продукты Adobe поддерживают эту видеокарту.
Такие проблемы появились уже давно. Но раньше, танцами с бубном все как то решалось.
Сейчас, соответственно после очередной переустановки винды, не получается решить.
Помогите пожалуйста, может кто сталкивался.
Простой 3 комментария
дискретная видюха начинает называться AMD Radeon 6600M and 6700M Series
Ну и что? Ведь твоя 6650 и принадлежит к серии 6600 (и, видимо, к 6700). Вдобавок название это просто текстовая строка в драйвере, а они сейчас «универсальные» то есть собраны чуть не под все выпускавшиеся видеокарты (ну, утрированно).
НО продукты Adobe говорят что не видят GPU
А еще лучше ставьте модифицированные, например помогают снять «замочек» на некоторых процессах, гуглите автора leshcat’s
https://leshcatlabs.net/forums/showthread.php?tid=999 возможно этот, я уже не помню
По поводу желтого треугольника: не будут стоять дрова на intel hd/ intel hd будет выключена
Radeon не будет работать
Всякие драйвер паки ставят не те версии драйверов ( стявят версии новее, которые не поддерживаются видеокартой)
Для начала лучше бы почистить все дрова с помощью DDU в безопасном режиме
Потом накатить сначала intel hd
Потом radeon
Ссылки можно для начала брать с официальных источников ( и ни в коем случае больше не обновлять этот радеон, интел можно)
p.s. Разумеется все настройки электропитания и все приоритеты в CCC стоят на макс. производительность
Yan, фш 19, Windows 10 Домашняя 64-bit (10.0, Build 18362) (18362.19h1_release.190318-1202)