Пишем драйвер под windows

Пишем драйвер под windows

Ну вот мы и добрались до самого интересного — сейчас будем писать драйвер. Итак, приступим. Для удобства выполнения, всю последовательность действий по написанию драйвера я распишу по пунктам.

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

Установив DDK, на Вашем компьютере в директории C:\WINDDK\2600.1106\ должны появиться файлы DDK. В этой директории создадим папку, в которой будут храниться наши проекты. Назовем ее, например, MyDrivers.

В папке MyDrivers создадим папку FirstDriver — тут будет находится наш первый проект драйвера.

2. Подготовка файлов проекта

В папке FirstDriver создайте пустой текстовый файл и переименуйте его под именем FirstDriver.c

При попытке переименовки со сменой расширения файла, появляется следующее предупреждение:

Не обращаем внимания на это предупреждение, и нажимаем Да. При этом наш файл примет вид:

Если же никакого предупреждения не было и переименованный файл так и остался текстовым с именем FirstDriver.c и расширением .txt, то в настройках своийств папки, которые можно найти в Пуск-> Настройка-> Панель управления-> Свойства паки уберите галочку напротив пункта «Скрывать расширения для зарегестрированных типов файлов». Попробуйте еще раз и все должно быть в порядке.

Теперь нам надо добавить еще два очень важных файла в наш проект, без которых драйвер нам не сделать. Они называются makefile и sources (обратите внимание, у них нет расширения). Их можно создать самим, но мы сделаем проще: скопируем готовые из какого либо примера проекта драйвера из DDK. Например, возьмем их из C:\WINDDK\2600.1106\src\general\cancel\sys\. Итак, копируем из указанной директории эти два файла и вставляем их в нашу папку проекта FirstDriver. Эти файлы управляют процессрм компиляции драйвера. Файл makefile оставляем без изменений, а вот sources надо подредактировать.

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

Первым параметром идет TARGETNAME, которому мы присвоили Port. Это значит, что когда DDK откомпилирует наш код и создаст драйвер, имя этого файла будет Port.sys Следующем параметром идет TARGETPATH, которому мы указали путь к папке нашего проекта. Если Вы устанавливали DDK в другое место, или создали пупку проекта в другой директории, здесь Вам надо это поправить на тот путь, который у Вас. Параметр TARGETTYPE пока оставлю без комментариев. В параметре SOURCES указываем, из каких файлов будет компилироваться драйвер. У нас это файл FirstDriver.c, вот мы его и указали.

Всю подготовительную работу мы сделали. Можно приступать к самой содержательной части — коду драйвера. Писать мы будем его на Си.

Еще раз напомню решаемую нами задачу: надо написать драйвер под Windows 2000, XP с помощью которого можно будет работать с портами компьютера (читать и писать данные) .

Рассмотрим код, представленный ниже. Серым цветом обозначены те участки кода, которые являются как бы шаблонными для большинства драйверов, а зеленым — код, который относится именно к нашему текущему драйверу.

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

Пишем драйвер под windows

В один прекрасный день меня попросили написать Драйвер. На тот день мои познания в C/C++ ограничивались программой Hellow Word, и поэтому на вопрос: «Напишешь?» я самоуверенно ответил: «Конечно». На следующий день я узнал, что на свете существуют MSDN и DDK. Вскоре я понял, что не все Windows одинаковые, оказалось, что мой драйвер должен работать под Win2000/NT. У меня ушло больше месяца на то, чтобы скомпилировать и запустить свой первый Драйвер. По правде сказать, это был не совсем мой Драйвер, а точнее — это был genport из NTDDK. Но радовался я так, как будто минимум написал свою ОС.

Недавно мне пришлось опять вернуться к Драйверу. И вот вчера я, наконец-то, сдал работающий драйвер и решил написать эту небольшую статью, для того, чтобы как-то систематизировать то, что я узнал, и, чтобы когда мне снова придется взяться за драйвер, было от чего отталкиваться.

Люди, знающие что такое IOCTL, DEVICE_EXTENSION, MajorFunction и DriverEntry не найдут здесь ничего нового. Эта статья для тех, кто, возможно, никогда не слышал слово ДДК, и кто до сего дня никогда не заглядывал в исходники драйверов. И еще, я буду довольно-таки подробно описывать многие, даже очевидные вещи, поэтому напомню о том, что данная статья рассчитана на людей с очень малым опытом программирования, какой был у меня, когда я занялся написанием драйверов.

Первое, с чем сталкивается программист, решивший заняться драйверами, это почти полное отсутствие русскоязычной литературы по данной теме. За год поисков мне удалось найти всего три книги на русском, в которых хотя бы немного говорится о драйверах:

Читайте также:  Удаляем получить windows 10

————————
1. Свен Шрайбер «Недокументированные возможности Windows 2000». Издательство «Питер» 2002 год.
Здесь очень хорошо описан механизм динамической загрузки драйвера.
——————
2. П. И. Рудаков, К. Г. Финогенов «Язык ассемблера: уроки программирования» Диалог МИФИ 2001 год.
Очень полезная книга для того, что бы понять, как писать драйвера без всякий Wizard-ов.
——————————
3. Светлана Сорокина, Андрей Тихонов, Андрей Щербаков «Программирование драйверов и систем безопасности». Издательство «БХВ-Петербург» 2002 год.
Здесь хорошо описывается многоуровневая модель драйверов.

Я ни в коей мере не претендую как на полноту освещения темы написания драйверов, так и на 100% правильность и достоверность того, что здесь написано (но все приведенные здесь исходники проверены и являются работоспособными). Буду благодарен всем, приславшим мне (01_artem@mail.ru) или высказавшим в форуме свои замечания.

Итак, я обращаюсь к человеку, решившему написать Драйвер уровня ядра под Win2000/NT. Надеюсь, эти заметки помогут сэкономить кучу времени и сил.

Прежде всего, я бы не рекомендовал (исходя из собственного опыта) пользоваться различными библиотеками (типа NuMega и всякими другими визардами). В основном из-за того, что даже для написания простейшего драйвера необходимо хотя бы поверхностное представление о том, как он функционирует. И самый простой способ получить представление об этом — написать драйвер самому. Мне, например, не хватило терпения разобраться с NuMega, и даже оболочки функций динамической загрузки/выгрузки драйвера, предложенные Свен Шрайбером в своей книге, я предпочел переписать.

Итак, начнем. Для начала надо установить на компьютер Visul C++ 6.0, MSDN и NTDDK установку проводить желательно именно в этом порядке. Лично я пользуюсь редактором UltraEdit для работы с текстами драйверов, но, в принципе, исходный код драйвера можно набирать в любом текстовом редакторе, хоть в NotePad.

Создадим папку, в которой мы будем работать с драйвером (пусть это будет C:myDriver). В этой папке создадим 5 файлов:
1. myDrv.c
2. myDrv.h
3. myDrv.rc
4. MAKEFILE
5. SOURCES

Начнем с последнего файла. В SOURCES скопируйте следующее:

#define FIRST_IOCTL_INDEX 0x800
#define FILE_DEVICE_myDRV 0x00008000

#define TEST_SMTH CTL_CODE(FILE_DEVICE_myDRV, \
FIRST_IOCTL_INDEX + 101, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)

#include «ntddk.h»
#include «myDrv.h»
#include «parallel.h»

#define NT_DEVICE_NAME L»\\Device\\myDrv»
#define DOS_DEVICE_NAME L»\\DosDevices\\myDrv»

//структура расширения устройства
typedef struct _DEVICE_EXTENSION
<
PDRIVER_OBJECT DriverObject;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
HANDLE Handle;

//прототипы функций
NTSTATUS
DriverDeviceControl(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp);

VOID
DriverUnload(IN PDRIVER_OBJECT DriverObject);

NTSTATUS
DriverOpen(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp);

NTSTATUS
DriverClose(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp);

NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
<
PDEVICE_OBJECT deviceObject;
UNICODE_STRING deviceNameUnicodeString;
UNICODE_STRING deviceLinkUnicodeString;
PDEVICE_EXTENSION extension;
NTSTATUS ntStatus;

if (!NT_SUCCESS(ntStatus)) return ntStatus;

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl;
DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverOpen;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;

extension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
extension->DeviceObject = deviceObject;
extension->DriverObject = DriverObject;

// Create counted string version of our Win32 device name.
RtlInitUnicodeString(&deviceLinkUnicodeString, DOS_DEVICE_NAME);

// Create a link from our device name to a name in the Win32 namespace.
ntStatus = IoCreateSymbolicLink(&deviceLinkUnicodeString, &deviceNameUnicodeString);
if (!NT_SUCCESS(ntStatus))
<
IoDeleteDevice(deviceObject);
return ntStatus;
>

return STATUS_SUCCESS;
>

//———————————————————————————————-
VOID
DriverUnload(IN PDRIVER_OBJECT DriverObject)
<
UNICODE_STRING deviceLinkUnicodeString;
PDEVICE_EXTENSION extension;
PIRP pNewIrp = NULL;
ULONG m_size;
NTSTATUS ntStatus;
extension = DriverObject->DeviceObject->DeviceExtension;

// Create counted string version of our Win32 device name.
RtlInitUnicodeString(&deviceLinkUnicodeString, DOS_DEVICE_NAME);

// Delete the link from our device name to a name in the Win32 namespace.
IoDeleteSymbolicLink(&deviceLinkUnicodeString);

// Finally delete our device object
IoDeleteDevice(DriverObject->DeviceObject);
>

//————————————————————————————————
NTSTATUS
DriverOpen(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
<
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
>

//————————————————————————————————
NTSTATUS
DriverClose(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
<
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
>

//————————————————————————————————
NTSTATUS
DriverDeviceControl(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
<
NTSTATUS ntStatus;
PIO_STACK_LOCATION irpStack;
PDEVICE_EXTENSION extension;
PULONG ioBuffer;
ULONG ioControlCode;
ULONG port = 0;

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
extension = DeviceObject->DeviceExtension;
ioBuffer = Irp->AssociatedIrp.SystemBuffer;
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode)
<
case TEST_SMTH:
ioBuffer[0] =(ULONG)DriverEntry;//В буфер обмена адрес функции DriverEntry
Irp->IoStatus.Information = 4;
break;

default:
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
>

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

Далее приведены прототипы используемых функций. Этим функциям можно дать произвольные имена, НО их сигнатура (состав параметров с типом возвращаемого значения) жестко заданы системой. [2]:Программная часть драйвера начинается с обязательной функции с именем DriverEntry(), которая автоматически вызывается системой на этапе загрузки драйвера. Эта функция должна содержать все действия по его инициализации. В качестве первого параметра наша функция получает указатель на объект драйвера типа PDRIVER_OBJECT.

При загрузке драйвера системы создается объект драйвера (driver object), олицетворяющий образ драйвера в памяти. С другой стороны, объект драйвера представляет собой структуру, содержащую необходимые для функционирования драйвера данные и адреса функций. В процессе инициализации драйвера (в DriverEntry()) создаются один или несколько объектов устройств (device object), олицетворяющие те устройства, с которыми будет работать драйвер. Этот самый device object необходим для правильного функционирования драйвера и создается (как и в нашем случае) даже тогда, когда драйвер не имеет отношения к какому-либо реальному устройству.

Читайте также:  How to create users windows 10

Далее, в первых строках DriverEntry мы определяем используемые в ней данные, в т.ч. указатель на device object, и две символьные строки UNICODE_STRING с именами устройств. Системные программы взаимодействуют с объектом устройства, созданным драйвером, посредством указателя на него. Однако для прикладных программ объект устройства представляется одним из файловых объектов, и обращение к нему осуществляется по имени (в приложении мы будем использовать функцию CreateFile()).

Надо иметь в виду, что объект устройства должен иметь два имени, одно — в пространстве имен NT, другое — в пространстве имен Win32. Эти имена должны представлять собой структуры UNICODE_STRING. Имена объектов устройств составляются по определенным правилам. NT-имя предваряется префиксом Device, а Win32-имя — префиксом ?? (или DosDevice). При указании имен в Си-программе знак обратной косой черты удваивается. Для того, чтобы указанное в программе драйвера имя можно было использовать в приложении для открытия устройства, следует создать символическую связь между обоими заданными именами устройств. Для этого используем функцию IoCreateSymbolicLink(). Следующая обязательная операция — создание объекта устройства — осуществляется вызовом функции IoCreateDevice(). Первый параметр этой функции — это указатель на объект драйвера, поступающий в DriverEntry(). Второй параметр определяет размер расширения устройства — структуры, которая служит для передачи данных между функциями драйвера (состав этой структуры произволен и полностью определяется разработчиком). Третий параметр — созданное ранее NT-имя устройства. Далее идут: тип устройства (FILE_DEVICE_UNKNOWN), специальные характеристики (0), FALSE означает, что у нас однопоточное устройство. Наконец, последний параметр является выходным — через него функция возвращает указатель на созданный объект устройства.

Далее необходимо занести в объект драйвера адреса основных функций, включенных программистом в текст драйвера. Массив MajorFunction является одним из элементов структурной переменной. В этот массив мы и заносим адреса основных функций (т.е. функций, которые вызываются системой автоматически в ответ на определенные действия приложения или устройства). Завершается функция оператором return с указанием кода успешного завершения.

Функция DriverUnload() вызывается при выгрузке драйвера. Здесь мы должны выполнить действия по удалению объекта устройства, созданного в DriverEntry().

Функции DriverOpen и DriverClose в нашем случае ничего не делают и возвращают просто STATUSS_SUCCESS. Кстати, все эти функции тоже могут иметь произвольные имена, но передаваемые им параметры строго фиксированы.

[2]:Вот мы и добрались до самой содержательной, с точки зрения прикладного программирования, функции DriverDeviceControl(). Эта функция вызывается каждый раз, когда драйверу приходит IRP-пакет с каким либо IOCTL_ кодом. Грубо говоря, IRP-пакет — это структура, передавая указатель на которую, приложение может общаться с драйвером (как, впрочем, и драйвер может общаться с другим драйвером). Более подробное описание того, что такое IRP-пакет, можно найти здесь http://www.lcard.ru/

IRP-пакет содержит так называемый системный буфер, служащий для обмена информацией (переменная SystemBuffer). Таким образом, нам надо получить доступ к IRP-пакету, а через него к SystemBuffer. Для этого объявляем переменную irpStack типа указателя на стековую область ввода-вывода PIO_STACK_LOCATION, и, кроме того, переменная ioBuffer, в которую будет помещен адрес системного буфера обмена. В нашем случае тип этой переменной — PULONG, в действительности тип передаваемых данных может быть каким угодно. С помощью функции IoGetCurrentIrpStackLocation() в переменную irpStack помещается адрес стековой области ввода-вывода, а в переменную ioBuffer заносится адрес системного буфера из структуры IRP. Системный буфер входит в объединение (union) с именем AssociatedIrp, поэтому мы используем конструкцию Irp->AssociatedIrp.SystemBuffer. Конструкция switch-case анализирует содержимое ячейки IoControlCode и в зависимости от значения кода выполняет те или иные действия. В нашей программе только один код действия TEST_SMTH. Засылка в буфер обмена адреса функции DriverEntry() осуществляется через указатель на этот буфер. В переменную Irp->IoStatus.Information заносим количество (4) пересылаемых байт. Для завершения IRP-пакета вызываем IoCompleteRequest().

Итак, драйвер мы написали. Теперь надо его скомпилировать. Т.к. процесс компиляции идет из командной строки, то для этой цели гораздо удобнее пользоваться bat-файлом. Создадим небольшой bat-файл с именем, допустим, Crt.bat и со следующим содержанием:

NTDDK у меня установлено в корень С:, если у Вас по-другому, то вместо C:NTDDKbin и C:NTDDK пропишите полные пути к соответствующим папкам.

Итак, теперь запустим наш Crt.bat. После окончания компиляции в папке C:myDriverobjfrei386 находим готовый драйвер myDrv.sys. Наш драйвер пока умеет только лишь загружаться/выгружаться и по специальному запросу посылает приложению адрес одной из своих процедур.

Теперь займемся написанием приложения, работающего с нашим драйвером. Еще раз напоминаю, что мы работаем под Win2000. Эта ОС позволяет реализовать динамическую загрузку/выгрузку драйвера.

Точнее, динамическую загрузку/выгрузку служб (service), но т.к. в Win2000 в качестве службы можно рассматривать и драйвер, я буду использовать оба эти термина, подразумевая, в данной статье, наш драйвер.

Для загрузки и выгрузки драйверов используется диспетчер управления службами SC Manager (Service Control Manager). Прежде чем вы сможете работать с интерфейсом SC, вы должны получить дескриптор диспетчера служб. Для этого необходимо обратиться к функции OpenSCManager(). Дескриптор диспетчера служб необходимо использовать при обращении к функциям CreateServise() и OpenService(). Дескрипторы, возвращаемые этими функциями, необходимо использовать при обращении к вызовам, имеющим отношение к конкретной службе. К подобным вызовам относятся функции ControlService(), DeleteService() и StartService(). Для освобождения дескрипторов обоих типов используется вызов CloseServiceHandle().

Читайте также:  Не могу расшарить папку линукс минт

Загрузка и запуск службы подразумевает выполнение следующих действий:

  • Обращение к функции OpenSCManager() для получения дескриптора диспетчера
  • Обращение к CreateServise() для того, чтобы добавить службу в систему. Если такой сервис уже существует, то CreateServise() выдаст ошибку с кодом 1073 (код ошибки можно прочитать GetLastError()) Данная ошибка означает, что сервис уже существует и надо вместо CreateServise() использовать OpenService().
  • Обращение к StartService() для того, чтобы перевести службу в состояние функционирования.
  • Если служба запустилась успешно, то можно вызвать CreateFile() для получения дескриптора, который мы будем использовать уже непосредственно при обращении к драйверу.
  • И по окончании работы не забудьте дважды обратиться к CloseServiceHandle() для того, чтобы освободить дескрипторы диспетчера и службы.

Если на каком-то шаге этой последовательности возникла ошибка, нужно выполнить действия, обратные тем, которые вы успели выполнить до ошибки. Надо помнить о том, что при обращении к функциям, подобным CreateServise(), необходимо указывать полное имя исполняемого файла службы (в нашем случае полный путь и имя myDrv.sys).

Посмотрим на исходный текст простого консольного приложения, написанного на Visual C++ 6.0:

#include
#include «LoadDRV.h»
#include
#include

void main()
<
LPTSTR m_name = new char[20];
strcpy(m_name, «myDrv.sys»);

if (drvLoad(m_name)) TestSmth();

drvUnLoad(m_name);
delete m_name;
>

DeviceIoControl(hDevice, IOCTL_TEST_SMTH, NULL, 0,
&test, 4, &ReturetLength, NULL);

printf(«TestSmth= %in»,test);
return test;
>

///**************Функции динамической загрузки************************
bool drvLoad(char* name)
<
printf (name);
hSCManager=NULL;
hService=NULL;
bool status;

if(OpenManager())
<
if(drvCreateService(name))
<
if(drvStartService(name))
<
status=TRUE;
printf(«n Driver is now load. n»);
>
>
>

hDevice = CreateFile («//./myDrv», GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);

bool OpenManager()
<
bool status;
status=FALSE;

if(hSCManager!=NULL)
<
CloseServiceHandle (hSCManager);
hSCManager=NULL;
>

hSCManager=OpenSCManager (NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (hSCManager == NULL)
<
error(_OpenSCManager);
> else status=TRUE;

//———————————————————————
bool drvCreateService(PCHAR pDrvName)
<
LPTSTR lpBuffer;
lpBuffer = new char[256];
bool status = FALSE;
LPTSTR awPath; // путь к драйверу с именем pDrvName

// формируем путь к pDrvName, драйвер должен лежать рядом с exe-шником

GetCurrentDirectory(256, lpBuffer);
strcat(lpBuffer,»\»);
strcat(lpBuffer,pDrvName);
awPath = lpBuffer;

hService = CreateService(hSCManager,pDrvName,pDrvName,SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,awPath,NULL,NULL,NULL,NULL,NULL);

if(!hService)
<
error(_CreateService);
status = drvOpenService(pDrvName);//Пытаемся открыть службу
>
else status=TRUE;

delete lpBuffer;
return status;
>

bool status;
status=FALSE;
if(hService!=NULL) CloseService();

hService=OpenService(hSCManager,name,SERVICE_ALL_ACCESS);
if (!hService) error(_OpenService);
else status=TRUE;
return status;
>

//———————————————————————
bool drvStartService(PCHAR name)
<
bool status;
status=FALSE;
if(!StartService(hService,0,NULL))
<
error(_StartService);
printf(«Deleting service. «);
drvDeleteService(name)
>
else status=TRUE;
return status;
>

//———————————————————————
bool drvDeleteService(PCHAR name)
<
bool status;
status=FALSE;
CloseService();
if(!DeleteService(hService)) error(_DeleteService);
else status=TRUE;
return status;
>

if (hDevice!=INVALID_HANDLE_VALUE)
<
if(!CloseHandle(hDevice)) error(_CloseHandle);
hDevice=INVALID_HANDLE_VALUE;
>

if (hService)
<
status = ControlService(hService,SERVICE_CONTROL_STOP,&ServiceStatus);
if(!status) error(_SERVICE_CONTROL_STOP);

status = DeleteService(hService);
if(!status) error(_DeleteService);

status = CloseServiceHandle(hService);
if(!status) error(_CloseServiceHandle);
>

if(!CloseServiceHandle(hSCManager)) error(_CloseServiceHandle);
if (status) printf(«Driver Unload. SUCCESSn»);
return status;
>

//———————————————————————
void error(error_index erIndex)
< DWORD err;
err=GetLastError();
switch(erIndex)
<
case _OpenSCManager:
printf(«OpenSCManager failed with Error=%in»,err);
break;

case _GetFullPathName:
printf(«GetFullPathName failed with Error=%in»,err);
break;

case _CreateService:
switch (err)
<
case 1073:
printf(«The specified service already exists.n»);
printf(«opening this service. «);
break;
default:
printf(«CreateService failed with Error=%in»,err);
>
break;

case _OpenService:
printf(«OpenService failed with Error=%in»,err);
break;

case _StartService:
printf(«StartService failed with Error=%in»,err);
break;

case _DeleteService:
printf(«DeleteService failed with Error=%in»,err);
break;

case _SERVICE_CONTROL_STOP:
printf(«SERVICE_CONTROL_STOP failed with Error=%in»,err);
break;
case _CreateFile:
printf(«CreateFile failed with Error=%in»,err);
break;
case _CloseHandle:
printf(«CloseHandle failed with Error=%in»,err);
break;
case _CloseServiceHandle:
printf(«CloseServiceHandle failed with Error=%in»,err);
break;

#define FIRST_IOCTL_INDEX 0x800
#define FILE_DEVICE_myDrv 0x00008000

#define TEST_SMTH CTL_CODE(FILE_DEVICE_myDrv, \
0x800 + 101, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)

Это приложение загружает драйвер, файл которого лежит в одной папке, что и exe-файл данного приложения.

Посмотрим на исходный текст:

В main() мы создаем переменную с именем драйвера (myDrv.sys) и передаем это имя в функцию динамической загрузки драйвера drvLoad(), которая выполняет все необходимые действия по работе с менеджером служб, и в конце вызывает CreateFile(), которая возвращает дескриптор, нужный для работы с драйвером как файловым объектом. Этот дескриптор, в частности, используется при вызове функции DeviceIoControl.

Если драйвер загружен успешно, то вызываем функцию TestSmth(), внутри которой мы создаем и посылаем драйверу IRP-пакет (с помощью вызова DeviceIoControl()). Приняв этот пакет, наш драйвер возвращает адрес своей процедуры DriverEntry. После этого выгружаем драйвер. Все.

Итак, мы написали простейший (дальше некуда) драйвер и приложение, работающее с ним. В этой статье я активно цитировал материалы из книги П. И. Рудаков, К. Г. Финогенов, по возможности делая ссылки на нее в виде: [2].

Через некоторое время напишу о том, как на базе этого драйвера написать драйвер-фильтр для LPT-порта.

Оцените статью