Отладка драйверов в visual studio

Обновлено: 07.07.2024

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

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

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

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

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

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

Отладка в VirtualBox
Добрый вечер. Есть Microsoft Visual Studio 2010 и требуется отладить программу, которая запущена в.

отладка/прошивка stm8 из virtualbox
У меня STVP+STVD+Cosmic установлены внутри WinXP-32bit виртуалки. Host-OS у меня Win7-64bit. Хочу.

Отладка драйвера для Windows 7 64: нет вывода из DbgPrint
Вот, после долгого копания везде, где только можно, решила всё-таки спросить тут, на форуме. Может.

Kali Linux (VirtualBox) : где скачать и как установить драйвера на wi-fi карту?
Здравствуйте, на днях установил Kali Linux на Virtual Box. Можете помочь, где скачать и как.

Обрати внимание вот на эту цитату:

.
Provisioning is not supported for virtual machines. Пишут, что для виртуальных машин не поддерживается.
Мне удавалось делать provisioning/deployment на Hyper-V (если память не изменяет),
но больше использовать эти "чудные" фичи Visual Studio и WDK не хочется.
Старый-добрый способ с map-файлами и отладкой с WinDBG через com/pipe -
вот то, что нужно, и с ним никогда не возникает проблем. Старый-добрый способ с map-файлами и отладкой с WinDBG через com/pipe -
вот то, что нужно, и с ним никогда не возникает проблем.

Я правильно понимаю, что речь идет о разработке и тестировании на одной машине без всяких связок host и target компьютеров?

Добавлено через 1 минуту

не заметил сразу. Есть ли где-нибудь пошаговый туториал для чайника по настройке всего этого?

Добавлено через 9 минут
В общем, вопросов пока нет. Нашел ваши предыдущие ответы в других темах. Попробую сам разобраться. Спасибо

Я правильно понимаю, что речь идет о разработке и тестировании на одной машине без всяких связок host и target компьютеров? Либо два разных компьютера, либо все в пределах одной машины.
Во втором случае таргетом выступает виртуалка (Hyper-V, VMware, VirtualBox - не важно). Во втором случае таргетом выступает виртуалка (Hyper-V, VMware, VirtualBox - не важно).

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

Например, зачем здесь devcon, если можно было обойтись простейшим legacy-драйвером для
примера, который элементарно регистрируется и запускается двумя командами из консоли?

1. Создать в настройках виртуальной машины COM-порт (с редиректом в пайп на хосте).

2. Перевести виртуальную машину в режим отладки. Для систем NT 5.x (XP, Server 2003)
это делается через msconfig, для NT 6.x (Vista и выше) можно еще использовать bcdedit.exe.

4. Запускаем WinDBG в режиме kernel-отладки, запускаем виртуалку. Все!

Я для пунктов 2 и 3 использую вот такой bat-файл (запускать из проводника на
виртуальной машине и с повышением прав, работает только на Vista и выше):

  • Вы не можете создать новую тему
  • Вы не можете ответить в тему

Программирование драйверов устройств для чайников

Введение

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

Также эти уроки могут помочь аудитории владельцев видеокарт с графическими чипами, произведенными компанией 3dfx, разобраться в доступных исходных кодах драйверов этих видеокарт. Упомянутые исходники могут быть с наименьшим количеством переделок откомпилированы под Windows XP/Windows 2003 Server, которые и будут являться основной целевой системой. В более современной Windows Vista/7 несколько изменилась модель драйверов, наиболее сильно это затронуло графическую подсистему. Это означает, что для их корректной работы придется переписать весьма значительную часть кода. Кроме того, под х64 версиями Vista/7 и более старших операционных систем просто так нельзя загрузить драйвер, не имеющей цифровой подписи. Подпись стоит порядка 500 долларов в год (150, если найти купон на скидку), их не любят давать частным лицам и даже небольшим организациям. Кроме того, если в подписанном драйвере обнаружится уязвимость и ей воспользуются хакеры – то подпись данного [почти всегда юридического] лица аннулируется и у него возникают проблемы с получением новой. Еще есть такая процедура, как WHQL-сертификация, но ее я вообще не собираюсь пока здесь обсуждать.

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

Для начала работы требуется иметь некоторое знакомство с любым языком программирования. Думаю, для этого вполне достаточно школьного / вузовского курса для непрограммистов. Если же в современных программах образования знакомство с программированием не было предусмотрено/за давностью лет курс забыт или просто что-то непонятно – отписывайтесь в теме, я напишу другие уроки, подробней раскрывающие непонятную тему.

Хотелось бы также видеть уроки по работе с драйверами для Windows 9x. Например, для AALchemy есть драйвер для Windows XP. Но вот для Windows 9x - нет.

Урок 0. Установка и настройка программного обеспечения

Список используемого программного обеспечения:

Все используемое мною программное обеспечение – на английском языке. В большинстве случаев можно использовать и русские версии, но может возникнуть путаница в командах или другие проблемы с совместимостью. Для начала работы потребуется компьютер с установленной обычной 32-разрядной Windows XP/2003. Продвинутые пользователи могут пользоваться необычной – отладочной, она же - checked build. Эта версия показывает больше информации при отладке устройств. Обычная же версия Windows носит название free build. Компьютер для экспериментов может быть как реальным, так и виртуальным. Разрабатываемые программы я буду проверять на одной реальной машине и одной виртуальной. На обоих установлен Windows Server 2003 R2 Enterprise Edition c SP2. Виртуальная машина запущена под VMWare Workstation 8.0.2.

Установка Visual Studio
Я буду использовать Visual Studio 2010 Ultimate. В принципе, ставить ее не обязательно, так как все необходимые инструменты, включая компилятор и заголовки есть и в WDK. Но для написания прикладных программ, в особенности для новичка, она намного удобней вызова компилятора из консоли. Если ее все же планируется установить, то лучше делать это перед установкой WDK.

Изображение


Рис 0.1 – Выбор пакетов в установщике Visual Studio

Установка WDK
Монтируем скачанный iso-образ аналогично тому, как это было сделано с Visual Studio. Запускаем KitSetup.exe, на этапе выбора пакетов устанавливаем галочки в соответствии с рисунком:

Изображение


Рис 0.2 – Выбор пакетов в установщике WDK

В дальнейшем рассмотрим и драйверы для Windows 9x. Для этого потребуется другое программное обеспечение, но программирование драйверов для Windows 9x проще.

Для того чтобы начать разрабатывать драйверы устройств Windows на компьютере дол жен быть установлен пакет Windows DDK (Driver Development Kit) фирмы Microsoft, желатель но последней версии (на момент написания книги таковой являлась версия 2003 SP1). Этот пакет включает все необходимые средства для разработки и отладки драйверов. Кроме того, нужно иметь под рукой хороший компилятор C/C++ (подойдет бесплатная версия Microsoft Visual Studio Express Edition) для разработки приложения, используемого при тестировании драйвера. Поскольку для драйвера потребуется отладчик, то в качестве такого можно вы брать DebugView (www.microsoft.com), который, несмотря на свою простоту, довольно удобен в работе.

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

В качестве первого примера разработаем драйвер «виртуального» устройства Test, кото рый «ничего» не делает, а только выводит в окно отладчика текстовую строку о выполненной операции.

Вот исходный текст нашего простейшего драйвера:

Test_Unload (IN PDRIVER_OBJECT DriverObject)

DbgPrint("%s","Test: UNLOADING!\n"); RtlInitUnicodeString(&DosDeviceName, DOS_DEVICE_NAME); IoDeleteSymbolicLink(&DosDeviceName); IoDeleteDevice(DriverObject->DeviceObject);

Test_Create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;

Test_Close(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;

DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)

PDEVICE_OBJECT DeviceObject; UNICODE_STRING NtDeviceName; UNICODE_STRING DosDeviceName; NTSTATUS status;

status = IoCreateSymbolicLink(&DosDeviceName, &NtDeviceName);

DO_DEVICE_INITIALIZING; DeviceObject->Flags |= DO_BUFFERED_IO;

DriverObject->MajorFunction[IRP_MJ_CLOSE] = Test_Close; DriverObject->DriverUnload = Test_Unload;

Вспомним, что каждый драйвер обрабатывает только те пакеты запросов, которые про

граммист для него определил. В нашем случае мы создали драйвер «виртуального» устрой

ства Test, который обрабатывает только запросы IRP_MJ_CREATE, IRP_MJ_CLOSE и отдельно Unload. Функция Unload выгружает драйвер из системы и обрабатывается несколько иным способом, чем пакеты запросов (в WDM драйверах эта функция вообще не используется).

Проанализируем исходный текст функции обработчика IRP_MJ_CREATE:

Test_Create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;

Первое – все функции обработчики пакетов запросов принимают два параметра – адрес структуры DEVICE_OBJECT устройства и адрес пакета запроса PIRP. Оба параметра содержат поля, которые функция может использовать при обработке запроса.

Функция DbgPrint – это специальная функция, предназначенная для вывода информации в отладчик. Эта функция используется только для трассировки программного кода и может быть перехвачена только в отладчике, таком, например, как DebugView. В функции Testdrv_Create также ничего существенного не делается. В поле Status пакета запроса заносится значение STATUS_SUCCESS, которое затем будет возвращено приложению как свидетельство успешно выполненной операции. Функция IoCompleteRequest завершает обработку пакета запроса. Сама функция возвращает значение STATUS_SUCCESS.

Точно так же работает и функция Testdrv_Close, которая, по запросу приложения (функ

ция WINAPI CloseHandle()) закрывает дескриптор устройства.

Функция Testdrv_Unload удаляет символическую ссылку на устройство, о которой мы упо

минали ранее (IoDeleteSymbolicLink()) и удаляет устройство из системы (IoDeleteDevice()).

Мы уже упоминали о функции DriverEntry, которая вызывается операционной системой при инициализации драйвера устройства. Функции передается ссылка на структуру DriverObject, со зданной для нее операционной системой (первый параметр) и указатель на ключ данного драйвера в системном реестре (в записях этого ключа можно хранить или из записей извле кать при необходимости какие то дополнительные параметры). Эта функция последовательно выполняет такие шаги (которые являются стандартными для этого класса драйверов):

1) инициализируется константная строка с именем устройства в пространстве имен си

стемы (функция RtlInitUnicodeString(&NtDeviceName, NT_DEVICE_NAME);

2) создается программный объект описания устройства при помощи функции IoCrea teDevice(). Если объект устройства создать не удалось, функция заканчивает работу с ошибкой, код которой передается в переменной status, а сам объект устройства удаляется;

3) инициализируется константная строка с именем устройства в пространстве имен,

доступном пользовательской программе (функция RtlInitUnicodeString(&DosDevice Name, DOS_DEVICE_NAME));

4) создается символическая ссылка DOS имени на имя из пространства имен операци

онной системы для доступа к устройству из приложения пользователя (функция status

= IoCreateSymbolicLink(&DosDeviceName, &NtDeviceName). В случае неудачи симво лическая ссылка и объект устройства удаляются, а функция DriverEntry заканчивается с ошибкой;

5) устанавливаются флаги устройства;

6) определяются функции обработчики пакетов запроса.

Поместим исходный текст драйвера в файл test.c и сохраним его. Теперь в каталог, где находится файл, поместим еще два файла:

С их помощью компилятор создаст (если нет ошибок в исходном тексте) файл драйвера устройства с именем test.sys. Смысл записей в этих файлах я объяснять не буду – это все есть в документации. Вы можете просто взять любую пару этих файлов из каталогов, куда помеще ны примеры, и изменить имя файла в sources следующим образом:

TARGETPATH=obj TARGETTYPE=DRIVER INCLUDES=..\

После этого выберите консоль для запуска компилятора в среде Windows DDK, например, Windows XP Checked Build Environment и перейдите в каталог, где находятся ваши рабочие файлы. Затем наберите команду

Если в исходном тексте файла test.c нет ошибок, то файл будет откомпилирован успешно


Вид окна отладчика DebugView

Естественно, что для отладки драйвера можно использовать и другие отладчики, имею

щиеся в Интернете.

Таким образом, мы знаем, как написать и отладить простейший, ничего не делающий драйвер. Теперь посмотрим, каким образом можно считывать или записывать данные в уст ройство ввода вывода. Первое, что нужно определить при разработке такого драйвера – указать, в каких областях памяти должны находиться данные для записи чтения. Чтение за пись данных довольно сложные процедуры с точки зрения операций, которые выполняет опе рационная система, но мы рассмотрим упрощенный вариант обмена данными между данны ми и приложением.

Для обмена данными мы использовать метод буферизации, когда данные, которые долж ны быть переданы приложению или которые должны быть прочитаны, приложением предва рительно помещаются в специально выделенную системой область памяти (системный бу

фер) и только затем обрабатываются приложением или драйвером. Далее, мы должны выде лить для самого драйвера область памяти, где будут находиться прочитанные или записыва емые данные. Для относительно небольшого объема данных можно воспользоваться облас тью расширения устройства (Device Extension) – областью памяти, которая может быть выде лена драйверу при его создании и которая является неперемещаемой. Для того чтобы система могла выделить драйверу область памяти из расширения в исходный текст драйвера нужно внести некоторые изменения.

Первое – нужно явным образом определить структуру вместе с переменными, под кото рую и будет выделена область памяти в секции деклараций в начале исходного текста драй вера устройства. Вот пример:

typedef struct _MY_DEVICE_EXTENSION

Здесь определена структура с одной 32 битовой целочисленной переменной L1. Посколь ку требуется всего 4 байта памяти, то система (гипотетически) могла бы выделить эти 4 байта, но, поскольку память выделяется блоками с учетом выравнивания для увеличения быстро действия, то, скорее всего, выделенный объем будет больше. Кроме того, при создании объекта устройства в функции IoCreateDevice в качестве второго параметра должен зада ваться размер области расширения, например:

status = IoCreateDevice(DriverObject, sizeof(MY_DEVICE_EXTENSION),

Здесь вторым параметром функции IoCreateDevice является sizeof (MY_DEVICE_ EXTENSION).

Какие функции используются для операций записи чтения? Что касается приложения пользователя, то мы уже рассматривали этот вопрос. Напомню, что приложение может запи сать данные в дескриптор устройства с помощью функции WINAPI WriteFile(), а прочитать дан ные с помощью функции ReadFile(). Есть и универсальная функция DeviceIoControl(), позволя ющая выполнять как чтение, так и запись данных.

Для манипуляций с данными в драйвере устройства можно использовать одну из функций ядра вида RtlXXX. Очень часто используется функция RtlMoveMemory(), которая перемещает данные из системной области памяти в область памяти драйвера (при записи данных в уст ройство) и наоборот, из области памяти драйвера в системный буфер (при чтении данных из устройства).

При обмене данными приложения и устройства обычно используются пакеты запроса

IRP_MJ_READ, IRP_MJ_WRITE и IRP_MJ_DEVICE_CONTROL. При этом для драйвера должен быть указан тип обмена данными (буферизованный или небуферизованный) в функции DriverEntry. Во всех наших примерах мы будем использовать буферизованный обмен данны

ми, поэтому после успешного создания объекта устройства функцией IoCreateDevice нужно указывать оператор

Еще один важный момент, касающийся операций ввода вывода (чтения записи): Нельзя пытаться выполнять какие либо операции с устройством в функции DriverEntry! Она вызыва ется системой единственный раз при инициализации драйвера устройства и после выполне ния будет выгружена из памяти! Все операции обмена данными в драйвере выполняются при поступлении пакетов запросов посредством соответствующих функций в массиве указателей IRP_MJ_XXX.

И, наконец, последнее перед тем, как перейти к практике – в наших последующих приме рах мы будем для обмена данными использовать функцию DeviceIoControl, для которой Ме неджер ввода вывода операционной системы формирует пакет IRP_MJ_DEVICE_CONTROL.

Источник: Магда Ю. С. Компьютер в домашней лаборатории. – М.: ДМК Пресс, 2008. – 200 с.: ил.

Читайте также: