Как писать драйвера
Как писать драйвера (часть 3)
Когда мы программируем под Windows API, мы ставим на обработку сообщений от Windows функцию WindowProc, которая регистрируется в момент создания класса окна. Примерно так же при создании экземпляра драйвера, в системе происходит регистрация всех необходимых функций.
Для регистрации и инициализации всего необходимого используется входная функция, которая так и называется: DriverEntry.
Для тех, кому тяжело качать Win2000 DDK размером 41Мб мы выложили базисный код драйвера в архиве. Скачайте архив для более полного ознакомления с текстом и работы с нашим текстом.
Сама DriverEntry запускается один раз, но важность правильной регистрации, думаю, понятна всем. Рассмотрим ее текст полностью.
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
// Register the miniport with NDIS. Note that it is the miniport
// which was started as a driver and not the protocol. Also the miniport
// must be registered prior to the protocol since the protocol’s BindAdapter
// handler can be initiated anytime and when it is, it must be ready to
// We will disable the check for hang timeout so we do not
// Either the Send or the SendPackets handler should be specified.
// If SendPackets handler is specified, SendHandler is ignored
// Make sure the protocol-name matches the service-name under which this protocol is installed.
// This is needed to ensure that NDIS can correctly determine the binding and call us to bind
Майкрософт утверждает что нам нужны 4 функции минимум, присутствующие в DriverEntry:
NdisMInitializeWrapper – функция указывает системе NDIS, что пришло время инициировать miniport service в ее системе. Возвращаемое значение необходимо сохранить на будущее. Обязательно надо обратить внимание, что если происходит ошибка при инициализации любого объекта, то при уже нормально отработавшей функции NdisMInitializeWrapper нужно вызвать NdisTerminateWrapper для высвобождения ресурса.
NdisIMRegisterLayeredMiniport Функция, регистрирующая все функции уровня miniport
NdisRegisterProtocol Функция, регистрирующая все функции протокола
NdisIMAssociateMiniport. Функция, информирующая NDIS о том, что есть, существует два уровня, минипорт и протокол, и говорящая об экспорте функций, если таковой присутствует.
Теперь, для полной расшифровки этой шарады, посмотрим, как работает система драйвера, уже на уровне кода.
После того, как драйвер зарегистрирует функции группы miniport, он должен зарегистрировать полученный HANDLE, для правильной un-регистрации этих функций в момент окончания работы драйвера. Это делает функция NdisMRegisterUnloadHandler. Затем мы регистрируем протокольную группу и связываем эти две группы, сообщая NDIS об их существовании – функцией NdisIMAssociateMiniport.
На этом в нашем примере функция DriverEntry закачивается. Стоит немного пояснить еще несколько моментов работы инициализации. При работе на уровне kernel не стоит использовать функции: malloc, realloc, memset и т. д. Для этого существуют NdisZeroMemory, NdisAllocateMemory и др. Они более конкретно работают, и предназначены именно для использования в драйверах связанных с NDIS.
Конкретно сами функции, которые принадлежат группам, мы с вами будем рассматривать в процессе написания кода, да и то не все. Полное их описание вы уже посмотрите в DDK help. А мы с вами посмотрим на неявную третью группу.
Третья группа функций используется для коммуникации с аппликацией. Иногда и для непосредственного создания пакетов, и их отправки, как в случае с драйвером СОМ порта. В нашем случае драйвер пока никак не взаимодействует с уровнем аппликаций, поэтому их нет. Но, любой драйвер нуждается в управлении, наш пример, особенно, ибо он является тестовым и нам необходимо уметь давать нужные команды. Поэтому мы создадим и проинициализируем следующие функции:
extern NTSTATUS FilterOpen(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
Внесите их в файл passthru.h.
Теперь внесем изменения в нашу функцию DriverEntry.
PDRIVER_DISPATCH MajorFunctions [IRP_MJ_MAXIMUM_FUNCTION + 1];
Этот массив будет содержать все указатели функций для регистрации.
И добавим еще несколько переменных:
NDIS_STRING ntDeviceName; //имя для вызова виртуального device-а для NT
NDIS_STRING win32DeviceName; // тоже для win32
PDEVICE_OBJECT deviceObject; // и объект.
Затем начнем инициализацию. Перед завершающей регистрацией протокольных функций и связкой их с группой miniport и NDIS, функцией NdisIMAssociateMiniport, добавим следующий код:
// DeviceName that names the device object.
// SymbolicName that is the Win32-visible name of the device
//Связываем имена функций с массивом
Создание драйверов. Часть 1
В данной статье мне хотелось бы показать, как создавать драйвера, если Вам необходима теоретическая информация то советую прочесть книгу Дэвида Соломона и Марка Руссиновича «Внутреннее устройство Microsoft Windows 2000». Для начала мы сделаем простой драйвер для Windows 2000, который послужит нам каркасом для дальнейших экспериментов. Для работы понадобится «Windows 2000 Driver Development Kit», именно с помощью данного пакета создаются драйвера, в его состав входят компилятор, линкер, заголовочные файлы, lib-файлы, документация, полезные инструменты и конечно множество примеров.
Для написания и редактирования исходных текстов драйверов можно воспользоваться редактором, входящим в состав MS Visual Studio или Блокнотом, незаменимым инструментом программиста. Для просмотра объектов операционной системы понадобиться программа WinObj, автором которой является Марк Руссинович, программу можно найти на сайте http://www.sysinternals.com/, так же с этого адреса можно скачать программу DebugView, которая позволяет просматривать диагностические сообщения, посылаемые функциями OutputDebugString и DbgPrint.
Начнем, создайте на диске С: папку и назовите её «MyFirstDriver», затем в ней папку «sys» и «loader», это нужно для того чтобы легче было работать с путями к бинарным файлам. В папке «sys» создайте три файла, с именами «makefile», «sources» и «MyFirstDriver.c». В файле «makefile» напишите следующее:
Этот файл нужен для работы программы Build. В файле «sources» напишите следующее:
TARGETNAME=MyFirstDriver
TARGETTYPE=DRIVER
TARGETPATH=obj
SOURCES=MyFirstDriver.c
Этот файл нужен для настройки процесса компиляции:
TARGETNAME – имя драйвера;
TARGETTYPE – тип бинарного файла, который мы хотим создать, может иметь следующие значения: DRIVER, GDI_DRIVER, MINIPORT, LIBRARY, DYNLINK (for DLLs).
TARGETPATH – путь для папки с временными файлами;
SOURCES – путь к файлу с исходным текстом драйвера.
Теперь переходим к исходному файлу «MyFirstDriver.c», в нем напишите следующее:
Как Вы видите драйвер очень прост и имеет основные функции:
DriverEntry – данная функция есть в каждом драйвере и является точкой входа.
Параметры:
IN PDRIVER_OBJECT DriverObject – адрес объекта драйвера;
IN PUNICODE_STRING RegistryPath – путь в реестре, где содержится информация о драйвере.
Тип возвращаемого значения NTSTATUS, на самом деле это просто тип LONG, может принимать следующие значения:
STATUS_SUCCESS – успешно;
STATUS_N – где, N – номер ошибки.
В функции DriverEntry происходит заполнение полей структуры DriverObject:
DriverUnload – здесь регистрируется функция выгрузки драйвера;
MajorFunction[IRP_MJ_CREATE] – здесь регистрируется функция обработки запросов открытия драйвера;
MajorFunction[IRP_MJ_CLOSE] — здесь регистрируется функция обработки запросов закрытия драйвера;
MajorFunction[IRP_MJ_DEVICE_CONTROL] — здесь регистрируется функция обработки IOCTLзапросов. Так же в DriverEntry создается символьная ссылка и устройство. Для вывода отладочной информации с помощью функции DbgPrint мы используем возможности условной компиляции, то есть конструкция:
#if DBG
DbgPrint(«»);
#endif
будет присутствовать только в версиях Checked.
Для компиляции запустите «Checked Build Environment»,
Появится консольное окно, далее с помощью команды «cd» перейдите в папку с файлами драйвера и наберите команду build –cZ
Теперь драйвер готов, следующим шагом будет создание программы, которая будет динамически загружать наш драйвер с помощью SCM (Service Control Manager), подробно о нем написано в MSDN`е. Для этого создайте обыкновенную консольную версию программы и поместите все файлы проекта в папку «loader», в исходнике загрузчика напишите следующее:
Перед тем как запускать программу, запустите DebugView, только после этого запускайте загрузчик драйвера.
Как Вы видите наш драйвер заработал, на этом пока все.
Как писать драйвера (часть 5)
Как писать драйвера (часть 5)
Итак, мы возвращаемся к драйверам.
Справедливости ради, стоит отметить, что на сайте эта тема – одна из самых популярных, так что, кому нужны более глубокие знания, может обращаться к нам на форум, там обсуждаются конкретные проблемы.
Сегодня мы поговорим о коммуникации программы с драйвером.
В одной из предыдущих статей описаны были функции типа Filter:
extern NTSTATUS FilterOpen(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
extern NTSTATUS FilterIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
В любом драйвере необходима система управления, для указания самому драйверу, какого типа операцию стоит выполнить в текущий момент времени.
Скажем для нашего примера, драйверу фильтра потока данных по сети стоит указывать степень фильтрации, типы портов для перехвата, адреса, запрещенные к обращению и т.п.
Для этого используются вышеназванные функции.
FilterOpen вызывается когда в программе вызвано обращение к драйверу с помощью функции CreateFile
FilterIoControl – DeviceIoControl() соответственно.
Для правильного завершения работы каждой из этих функций – нужно обеспечить передачу в программу необходимых данных.
NTSTATUS FilterOpen(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) <
// инициализация любых управляющих параметров, например структуры управляющих данных
Irp->IoStatus.Status = NDIS_STATUS_SUCCESS; // Возращаемое значение – ошибка или нормальное. При нормальном завершении – будет передан HANDLE на устройство, в нашем случае на драйвер.
Irp->IoStatus.Information = 0; // Количество переданных вверх байт – в нашем случае 0 потому как нет передаваемых параметров.
IoCompleteRequest(Irp, IO_NO_INCREMENT); // Завершение работы.
return NDIS_STATUS_SUCCESS; // Нормальный код возврата.
Эта форма будет использоваться в том или ином виде в каждой из этих функций.
Точно так же выглядит и функция закрытия:
NTSTATUS FilterClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) <
// Код окончания работы с драйвером.
В драйвере можно создать например просто элемент для нашего примера – например, описать глобальную переменную DWORD Port; Ее будем испрользовать для задания номера порта для перехвата.
Определим Default значение для порта равное 80 (стандартный порт http протокола) и проведем его инициализацию в функции Open и де инициализацию в Close. Так мы сможем контролировать старт активной фазы работы драйвера и ее окончание.
Функции Write & Read можно использовать для передачи любого количества информации в драйвер и обратно.
Для сложных случаев и частой передачи необходимо использовать именно эти функции, из-за того, что переключение контекста драйвера не происходит. При использовании для этих целей DeviceIoControl приведет к постоянному переключению контекста драйвера и замедлит работу системы.
В нашем примере передавать в драйвер и назад нечего поэтому напишем Write/Read функции в виде готовых болванок, но так как наша цель построить работающий пример – то передадим абстрактную последовательность вверх в аппликацию.
Для этого создайте в этом же месте глобальную переменную вот такого вида:
unsigned char TestStr[10] = «abcdefghi»;
NTSTATUS FilterRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) <
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG BytesReturned = 0;
// Get input parameters
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation( Irp ); // Содержит стек Irp
ULONG BytesRequested = IrpStack->Parameters.Read.Length; // Длина принятых данных –равна параметру максимально запрошенной длины в функции ReadFile
if (BytesRequested AssociatedIrp.SystemBuffer, TestStr, 10);
NTSTATUS FilterWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) <
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG BytesReturned = 0;
// Здесь все работает аналогично.
Теперь мы подошли к функции управления.
Она сама работает точно также как и все функции перечисленные выше – с одной разницей: В ней принимаемым параметром является код операции, который надо установить в драйвер.
Можно конечно придумать свой формат данных – передаваемых в WriteFile, который расшифровывать внутри драйвера и так решать, что делать или не делать, однако стоит использовать уже готовый механизм, предоставленный Microsoft-ом.
Описание кода комманды выглядит так:
#define IOCTL_SET_COMMAND1 // наш код управления
CTL_CODE ( FILE_DEVICE_UNKNOWN, // Тип кода
0xF07, // Цифровое значение
METHOD_BUFFERED, // Метод операции
FILE_ANY_ACCESS ) // Права доступа.
Итак мы описали код управления, вызов которого будет выглядеть в программе так:
hFile, // Handle драйвера из функции CreateFile
(DWORD) IOCTL_SET_COMMAND1, // Комманда
(LPVOID)0, (DWORD)0, (LPVOID)NULL, (DWORD)0, (LPDWORD)&bytesReturned, NULL
Вызов такой функции приведет к обращению драйвером к функции FilterIoControl!
NTSTATUS FilterIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) <
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG BytesReturned = 0;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG IoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
PVOID InfoBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG InputBufferLen = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
ULONG OutputBufferLen = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
// Здесь мы можем сменить наш номер порта с 80 на, к примеру, 25.
Описания Input/Output буферов привожу для того, чтобы при необходимости получения и еще каких либо сопутствующих параметров, было ясно, где их получать, скажем, в драйвере нашего примера, команда 1 может нести в качестве параметра новый номер порта для перехвата.
Давайте теперь опишем логику управления драйвером перехватчиком.
Для перехвата определяются в начале параметры перехвата, адрес, порт и т.д.
Далее вносится тип рабочего состояния – перехват – прозрачный режим.
Вносится список возможных портов к перехвату (по необходимости).
В процессе работы системы, пока управляющая аппликация не запущена, то драйверу необходимо указать стартовые параметры.
Например, тип режима – прозрачный, в этом случае не перехватывается ничего.
Когда стартует управляющая программа – то она открывает драйвер CreateFile() и запускает, если это необходимо, другие стартовые условия, например перевод в режим перехвата и номер порта для этого.
Затем по желанию клиента из программы выставляются любые нужные условия от отключения перехвата, до перехвата всех номеров портов и всех адресов.
По завершению работы контрольной программы, можно выставить спец код управления, который укажет, как жить драйверу, когда управление не запущено, отключить все настройки и вернуться к прозрачному режиму, или остаться в необходимом режиме до дальнейших указаний.
На сегодня пока все.
Читайте также
О чем писать не нужно
О чем писать не нужно Не следует искажать данные: умышленно указывать больший опыт работы, сильно преувеличивать свои возможности, кривить душой относительно образования. Тайное, как правило, становится явным. В итоге и вы, и работодатель просто впустую потеряете
Что писать?
Что писать? Мост длиною в блог Метки: блоговедение, темы блога, что такое блогПисать обо всем сразу – это как плыть в броуновском движении, не сдвигаясь с места. Блог должен быть похож на мост: когда по нему идешь или едешь, то понимаешь, что рано или поздно окажешься на
Как писать
Как писать 33 буквы в руках мастера Метки: блогословие, приманкиВ любом учебнике по журналистике написано о том, что статья теряет читателей после каждого абзаца. А абзац, встречающий читателя, – это заголовок. В некоторых RSS-агрегаторах вообще не видно ничего, кроме
О чем, когда и как много писать
О чем, когда и как много писать Текстовый формат — основной для блогов и самый простой в реализации, поэтому каждый блогер должен владеть искусством написания текстов.Вопросы: о чем, через какой промежуток времени и в каком объеме писать в блог — волнуют очень многих
На каком языке писать сообщения?
На каком языке писать сообщения? Если ваш бизнес ориентирован на одну страну или языковую группу, то эта проблема перед вами не стоит. Другой вопрос, если группа международная. Вот скриншот (см. рис. 49), демонстрирующий подобный пример. В группе из 70 тысяч друзей 40 %
§ 55. Как писать слово «интернет»?
§ 55. Как писать слово «интернет»? — Ну что, Знайка? — стали спрашивать коротышки, подбегая к нему. — Как ты объяснишь это? — Что же тут объяснять? — развел Знайка руками?… Н. Носов. Незнайка на Луне В Академии наук Заседает князь Дундук. Говорят, не подобает Дундуку
Как писать драйвера (часть 1)
Как писать драйвера (часть 1) Предисловие. Драйвера под Windows являются для большей массы программистов, «тайной за семью печатями». И вовсе не потому, что это что-то архисложное, сколько по причине абсолютной недокументированности идеологии.Начав заниматься этой темой я
Часть первая: «Что нужно для компиляции простейшего драйвера?»
Часть первая: «Что нужно для компиляции простейшего драйвера?» Для разных типов Windows вам понадобиться разный набор программ.В любом случае надо скачать Win DDK (Driver Development Kit), для той платформы, под которую пишется драйвер. Его можно брать с разных источников, лично я
Как писать драйвера (часть 2)
Как писать драйвера (часть 2) Прежде, чем хвататься за описание самого драйвера, давайте определимся с типами существующих драйверов.По существующему в DDK разделению сам Microsoft подразделяет драйвера на следующие типы:– Kernel-Mode Drivers;– Kernel Streaming Drivers;– Graphics Drivers;– Network
Как писать драйвера (часть 5)
Как писать драйвера (часть 5) Итак, мы возвращаемся к драйверам.Справедливости ради, стоит отметить, что на сайте эта тема – одна из самых популярных, так что, кому нужны более глубокие знания, может обращаться к нам на форум, там обсуждаются конкретные проблемы.Сегодня мы
Часть 2. API для WDM драйвера.
Часть 2. API для WDM драйвера. Большинство функций драйверного API, которые нас интересуют, предоставляются модулем ntoskrnl.exe.Для их использования надо сделать следующее:1) Объявить типы данных и определить константы.Большинство определений для C находятся в файлах ntdef.h и wdm.h.2)
Совет 3 Умения писать код мало
Совет 3 Умения писать код мало Недостаточно выбрать технологию, на которую стоит сделать ставку. В конце концов, разве знание технологии — не товар, который нужно продать? Ты не сможешь расслабленно совершенствовать навыки программирования, оставив связанные с бизнесом
ПИСЬМОНОСЕЦ: Писать или дарить?
ПИСЬМОНОСЕЦ: Писать или дарить? Здравствуйте!Пишу двумя пальцами, ибо остальные: забинтованы…Эх, люди, вы наивно думаете, что судный день еще далеко, и всякие там толпы киборгов-убийц, бродящих по догорающим развалинам в поисках остатков человеческого рода, — сценарий для
Писать в периодические издания
Писать в периодические издания Хороший материал ищут многие газеты, журналы и бюллетени, освещающие вопросы безопасности. Если у вас есть что рассказать о технической поддержке, о продуктах, инструментах и т. п., то поделитесь своей информацией с другими. Это прекрасный
Форумы Modlabs.net: Программирование драйверов устройств для чайников — Форумы Modlabs.net
- Обсуждения
- Team MXS
- Пользователи
- Календарь
- Форумы Modlabs.net
- >Тематические форумы
- >Программное обеспечение
- >Программы
- Просмотр новых публикаций

Программирование драйверов устройств для чайников
#1
dE fENDER
- Member



- Группа: Пользователи
- Сообщений: 271
- Регистрация: 18 Декабрь 08
#2
dE fENDER
- Member



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







- Группа: Главный Администратор
- Сообщений: 12 008
- Регистрация: 16 Июль 05
#4
dE fENDER
- Member



- Группа: Пользователи
- Сообщений: 271
- Регистрация: 18 Декабрь 08
Урок 0. Установка и настройка программного обеспечения
Список используемого программного обеспечения:
- Windows XР или Windows 2003 Server х86 – операционная система, под которую будем писать драйверы;
- Visual Studio 2010 – среда разработки приложений на языках С/С++ и других. Будет использоваться для написания различных простых прикладных программ, в т.ч. и управляющей программы драйвера. Можно попробовать воспользоваться Visual Studio 11 beta, которая доступна для скачивания по ссылке — http://www.microsoft. studio/11/en-us;
- Windows Driver Kit – набор инструментов, необходимых для разработки драйвера. Включает в себя компилятор, заголовочные файлы и много другого. Требуется версия 7.1.0, ее можно взять по ссылке
http://www.microsoft. ang=en&id=11800; - VMWare – при отсутствии физической машины операционную систему можно запустить в виртуальной. Это также безопаснее при разработке — при ошибке в драйвере система уйдет в синий экран;
- Daemon Tools – средство для открытия образов CD и DVD дисков в формате iso. WDK распространяется в виде такого образа. В случае использования VMWare использовать не обязательно, т.к. VMWare умеет монтировать такие образы самостоятельно. Бесплатную lite-версию можно скачать здесь: http://www.disc-tool. download/daemon
- DebugView – утилита Марка Руссиновича, которая позволяет увидеть текстовые сообщения, выдаваемые драйверами, собранными в отладочном режиме. Скачивать здесь — http://technet.micro. ernals/bb896647
- Far Manager – файловый менеджер, с которым легче работать с консольными утилитами. Взять можно здесь — http://www.farmanage. wnload.php?l=ru.
Все используемое мною программное обеспечение – на английском языке. В большинстве случаев можно использовать и русские версии, но может возникнуть путаница в командах или другие проблемы с совместимостью. Для начала работы потребуется компьютер с установленной обычной 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.
Берем диск со студией в iso-формате, монтируем его в Daemon Tools, либо подключаем как компакт-дисковод в VMWare. Запускаем с диска setup.exe, выбираем «Install Microsoft Visual Studio 2010». На следующем шаге под Windows 2003 может появится сообщение, что требуется Windows Imaging Component. Щелкаем по этой строке, запускается браузер – скачиваем подходящий по языку операционной системы (у меня английский, значит wic_x86_enu.exe), устанавливаем. Заново запускаем setup.exe с диска. Нажимаем Next, соглашаемся с лицензионным соглашением, затем идет выбор типа установки. Выбираем Custom, изменяем путь установки – для избежания ошибок Visual Studio и все любые другие инструменты следует ставить по максимально короткому пути без русских букв и пробелов. Мой вариант – C:ProgramsVS10. Следующая страница – выбор пакетов. Устанавливаем в соответствии с рисунком:
Рис 0.1 – Выбор пакетов в установщике Visual Studio
Кроме показанных, можно также выбрать «Visual C#». C# бывает полезен для расширения возможностей самой Visual Studio и написания небольших утилит. Нажимаем Install. Кроме самой студии ставится куча бесполезной, ненужной и вредной дряни. Если на компьютере была старая версия Windows Installer’а – потребуется перезагрузка. После установки – Finish, Exit.
Установка WDK
Монтируем скачанный iso-образ аналогично тому, как это было сделано с Visual Studio. Запускаем KitSetup.exe, на этапе выбора пакетов устанавливаем галочки в соответствии с рисунком:
Рис 0.2 – Выбор пакетов в установщике WDK
Нажимаем Ок, укорачиваем путь установки до C:WinDDK, может появится окно установки .net 2.0. Устанавливаем .net. Появляется окно с лицензией WDK, соглашаемся. Ждем завершения установки. Нажимаем Finish. Установочный диск WDK не удаляем, а сохраняем до лучших времен, предыдущую версию 7.0 невозможно было корректно удалить без образа именно того диска, с которого ее устанавливали.
Устанавливать Visual Studio и WDK не обязательно на той же машине, на которой планируется проверять работу программ. Для проверки я поставил их на реальной машине с Windows 7 х64 и откомпилировал драйвер, который заработал под Windows 2003 Server х86.
Создаем на диске каталог для проектов, учитывая вышеописанные рекомендации (без пробелов и русских букв), например C:Projects. Запускаем Visual Studio, открываем настройки (меню Tools/Option), выбираем в дереве «Projects and Solutions/General», в параметре Projects location выбираем/указываем созданный каталог.
Также настоятельно рекомендую установить Far Manager 2 и работать через него. Его преимущество над Windows/Total Commander’ами и Explorer’ами в том, что через него нормально видно содержимое консоли (команда ctrl + O), куда выдается полезная информация при компиляции и запуске консольных программ. Путь установки желательно записать в переменную PATH.
В дальнейшем рассмотрим и драйверы для Windows 9x. Для этого потребуется другое программное обеспечение, но программирование драйверов для Windows 9x проще.
Драйвер — это просто
Многие считают что самому создать драйвер для 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 (синий экран смерти), поэтому экспериментируйте осторожно и обязательно всё сохраняйте перед запуском драйвера.
За возможную потерю данных, я ответственности не несу!

dE fENDER


