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

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

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

Ссылки:


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

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


Предупреждение:

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

Итак, я обращаюсь к человеку, решившему написать Драйвер уровня ядра под 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 скопируйте следующее:
Код:
TARGETNAME=myDrv
TARGETPATH=obj
TARGETTYPE=DRIVER

SOURCES=myDrv.c MyDrv.rc
В MAKEFILE скопируйте следующее:
!INCLUDE $(NTMAKEENV)makefile.def

В myDrv.rc скопируйте следующее:
#include <windows.h>
#include <ntverp.h>
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
#define VER_FILEDESCRIPTION_STR "Generic Port I/O"
#define VER_INTERNALNAME_STR "myDrv.sys"
#include "common.ver"

А вот так должен выглядеть myDrv.h:
Код:
#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)

Теперь обратимся к тексту самого драйвера myDrv.c:
Код:
#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;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

//прототипы функций
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;

RtlInitUnicodeString(&deviceNameUnicodeString, NT_DEVICE_NAME);

ntStatus = IoCreateDevice(DriverObject,
sizeof (DEVICE_EXTENSION),
&deviceNameUnicodeString,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&deviceObject);

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;
}

Немного поясню содержимое файла myDrv.c. Итак, по порядку:
Код:
#define NT_DEVICE_NAME L"\Device\myDrv"
#define DOS_DEVICE_NAME L"\DosDevices\myDrv"- Эти строки служат для задания символических имен текстовыми строками с именами объекта устройства, который будет создан нашим драйвером.
Далее приведены прототипы используемых функций. Этим функциям можно дать произвольные имена, НО их сигнатура (состав параметров с типом возвращаемого значения) жестко заданы системой. [2]:Программная часть драйвера начинается с обязательной функции с именем DriverEntry(), которая автоматически вызывается системой на этапе загрузки драйвера. Эта функция должна содержать все действия по его инициализации. В качестве первого параметра наша функция получает указатель на объект драйвера типа PDRIVER_OBJECT.

Небольшое лирическое отступление:

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

Далее, в первых строках 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/~gorinov/texts/irp99.html.

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 и со следующим содержанием:
Код:

%SystemRoot%system32cmd.exe /c "cd C:NTDDKbin&&setenv.bat C:NTDDK&&cd c:myDriver &&build -ceZ"
pause

Замечание:

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 <conio.h>
#include "LoadDRV.h"
#include <string.h>
#include <stdio.h>


void main()
{
LPTSTR m_name = new char[20];
strcpy(m_name, "myDrv.sys");

if (drvLoad(m_name)) TestSmth();

drvUnLoad(m_name);
delete m_name;
}

//-----------------------------------------------------------------------------
//создаем и посылаем драйверу IRP-запрос
int TestSmth(void)//0x800 + 101
{
int test = 0;
DWORD ReturetLength = 0;

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;

status=FALSE;


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);

return status;
}

//---------------------------------------------------------------------

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;

return status;
}

//---------------------------------------------------------------------
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 drvOpenService(PCHAR name)
{

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;
}

//-------------------------------------------------------------------
void CloseService()
{
CloseServiceHandle (hService);
hService=NULL;
}

//-------------------------------------------------------------------
int drvUnLoad(PCHAR name)
{
int status;
status=FALSE;

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;

}
}

И содержимое h-файла этого приложения:
Код:
#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-порта.

Артем.
Information
  • Posted on 01.02.2010 00:53
  • Просмотры: 4453