Лабы по драйверам / lr3
.docЛабораторная работа №3
Цель работы
Реализация в драйвере обработки запросов чтения, записи и кодов управления в/в для различных режимов передачи буфера. Модификация прикладной программы для проверки работоспособности драйвера.
Создание драйвера будет заключаться в модификации драйвера №3 (simple3) из л/р №1.
Создание прикладной программы будет заключаться в модификации диалоговой прикладной программы управления драйверами из л/р №2.
Драйвер №4
Имя проекта rw
Реализация обработки драйвером запросов чтения и записи при различных режимах передачи буфера с данными.
Для выполнения работы изучить:
-
Лекция lect7_8.doc п.[7.2] Структура IRP, в особенности п.[7.2.2.] Поля в Стеке размещения Ввода - вывода IRP.
-
[7.3] Описание Буфера Данных
-
[7.4] Коды Функции Ввода - вывода
-
[8.1] Передача данных посредством IRP и диспетчерские точки входа драйвера, в особенности п. [8.1.1] Запросы чтения и записи IRP_MJ_READ и IRP_MJ_WRITE
Принцип реализации:
Драйвер должен создать 3 устройства – по одному устройству на каждый из способов передачи буфера: buffered i/o, direct i/o и neither i/o. Соответственно, после создания каждого устройства функцией IoCreateDevice() в поле Flags объекта-устройство устанавливается требуемый способ передачи буфера (DO_BUFFERED_IO, DO_DIRECT_IO или ничего не устанавливается для метода neither i/o). Устройства должны иметь расширения устройств (в IoCreateDevice() указывается размер структуры DeviceExtension). Структура DeviceExtension определяется разработчиком драйвера. В нашем случае она должна содержать буфер из 16 байт.
#define THE_BUFFER_LENGTH 16
typedef struct _DEVICE_EXTENSION
{
unsigned char Image[THE_BUFFER_LENGTH];
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
Диспетчерская функция драйвера должна обрабатывать все запросы (IRP_MJ_CREATE, IRP_MJ_CLOSE, IRP_MJ_READ, IRP_MJ_WRITE). Это делается с помощью присваивания адреса этой функции соответствующим элементам массива DriverObject->MajorFunction.
При получении запроса на чтение или запись диспетчерская функция должна проверить поле Flags объекта-устройство, полученного в качестве первого параметра диспетчерской функции. В соответствии со значением этого поля запрос чтения/записи следует обрабатывать как buffered, direct или neither (см. лекцию №7-8 для получения информации о местонахождении буфера данных, его размере и о способе указания числа реально прочитанных/записанных байт).
Пример получения буфера и его размера для запроса записи с прямым в/в:
PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
switch (stack->MajorFunction)
{
...
case IRP_MJ_WRITE:
buf_size = stack->Parameters.Write.Length;
if (pDeviceObject->Flags & DO_BUFFERED_IO)
{
...
}
else
if (pDeviceObject->Flags & DO_DIRECT_IO)
{
buf = MmGetSystemAddressForMdl( Irp->MdlAddress );
}
else
{/*NEITHER I/O*/
...
}
break;
...
}
Запрос записи должен записывать полученный в пакете IRP блок данных в буфер внутри DeviceExtension соответствующего устройства (устройства, для которого послан запрос).
Запрос чтения должен записывать блок данных из буфера внутри DeviceExtension в буфер, переданный в пакете IRP.
При выполнении запросов чтения и записи необходимо проверять размеры буферов на переполнение (не записывать в DeviceExtension больше 16 байт и не записывать в буфер в IRP больше размера буфера).
switch (stack->MajorFunction)
{
case IRP_MJ_READ:
RtlMoveMemory(
buf,
DeviceExtension->Image,
bufsize>THE_BUFFER_LENGTH?THE_BUFFER_LENGTH:bufsize);
Irp->IoStatus.Information = bufsize>THE_BUFFER_LENGTH?THE_BUFFER_LENGTH:bufsize;
break;
case IRP_MJ_WRITE:
RtlMoveMemory(
DeviceExtension->Image,
buf,
bufsize>THE_BUFFER_LENGTH?THE_BUFFER_LENGTH:bufsize);
Irp->IoStatus.Information = bufsize>THE_BUFFER_LENGTH?THE_BUFFER_LENGTH:bufsize;
break;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
Модификация диалоговой прикладной программы управления драйверами для тестирования драйвера №4
В класс TScMgr добавить 2 функции: чтение из файла и запись в файл.
BOOL TScMgr::Read(HANDLE hDevice, void* buf, DWORD bsize);
BOOL TScMgr::Write(HANDLE hDevice, void* buf, DWORD bsize);
hDevice – описатель устройства, открытого с помощью функции TScMgr::OpenDevice() (Это та функция, где вызывается CreateFile())
buf, bsize – буфер и размер буфера для чтения и записи.
Реализация этих функций должна использовать WIN32-функции ReadFile() и WriteFile() (см. MSDN Library).
В случае неуспешного завершения этих функций вывести информацию об ошибке с помощью TScMgr::PrintLastError().
При успешном завершении вывести число реально прочитанных/записанных байт и первые 4 байта прочитанного буфера.
В класс диалогового окна добавить 2 кнопки и функции обработки события нажатия. В обработчике записи в файл передавать буфер размером 16 байт, в обработчике чтения из файла – получать буфер размером 8 байт.
Драйвер №5
Имя проекта devioctl
Реализация обработки драйвером запросов управления в/в при различных режимах передачи буфера с данными.
Для выполнения работы изучить:
-
Лекция lect7_8.doc п.[8.1.2] IRP_MJ_DEVICE_CONTROL и IRP_MJ_INTERNAL_DEVICE_CONTROL
-
Особое внимание обратить на таблицы в п. [7.3], [8.1.2.2], [8.1.2.3].
Принцип реализации:
Драйвер должен создавать одно устройство.
С помощью макроса CTL_CODE должны быть созданы 4 кода управления в/в для каждого из 4 методов передачи буфера. Например:
#define FILE_DEVICE_IOCTL 0x00008301
#define IOCTL_MY_NEITHER CTL_CODE(FILE_DEVICE_IOCTL, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_MY_BUFFERED CTL_CODE(FILE_DEVICE_IOCTL, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_MY_INDIRECT CTL_CODE(FILE_DEVICE_IOCTL, 0x802, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_MY_OUTDIRECT CTL_CODE(FILE_DEVICE_IOCTL, 0x803, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
В диспетчерской функции необходимо проверить код управления в/в для данного пакета IRP, и в зависимости от него обработать запрос. Обработка запроса будет состоять в получении адреса буферов InBuffer и OutBuffer и размеров буферов и вывода этой информации с помощью DebugPrint(). О получении адресов буферов и их размеров читай в лекциях №7 и 8.
Пример обработки IOCTL_MY_INDIRECT:
case IOCTL_MY_INDIRECT:
SimplPrint( ("IOCTL.SYS: IOCTL_INDIRECT:\n"));
InBuffer = Irp->AssociatedIrp.SystemBuffer;
InLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
OutBuffer = MmGetSystemAddressForMdl( Irp->MdlAddress);
OutLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
SimplPrint( ("IOCTL.SYS: InB=%08lx len=%lx, OutB=%08lx, len=%lx\n",
(ULONG)InBuffer, InLength, (ULONG)OutBuffer, OutLength));
break;
При получении неизвестного контрольного кода необходимо получить используемый для него метод передачи буферов
method = irpStack->Parameters.DeviceIoControl.IoControlCode & 0x03L;
и вставить обработку для каждого метода
switch(method)
{
case METHOD_BUFFERED:
…
case METHOD_IN_DIRECT:
…
case METHOD_OUT_DIRECT:
…
case METHOD_NEITHER:
…
}
Обработка будет совпадать с обработкой соответствующих контрольных кодов IOCTL_MY_NEITHER … IOCTL_MY_OUTDIRECT
Модификация диалоговой прикладной программы управления драйверами для тестирования драйвера №5
Модифицировать будем программу, полученную после реализации драйвера №4 (добавлены кнопки чтения и записи).
В класс TScMgr добавить функцию: отправки кода управления в/в.
BOOL TScMgr::SendCtlCode(HANDLE hDevice, DWORD CtlCode,
void* InBuf, DWORD InBufSize,
void* OutBuf, DWORD OutBufSize);
Реализация функции должна использовать WIN32-функцию DeviceIoControl() (См. MSDN Library).
В случае неуспешного завершения этой функции вывести информацию об ошибке с помощью TScMgr::PrintLastError().
При успешном завершении вывести число реально прочитанных/записанных байт и первые 4 байта буфера OutBuf.
В класс диалогового окна добавить кнопку отправки контрольного кода и обработчик ее нажатия. Функция – обработчик должна вызывать TScMgr::SendCtlCode() 8 раз и каждый раз передавать буфер InBuffer размером 8 байт и OutBuffer размером 16 байт. Каждый раз передавать разное значение кода в/в.
С помощью макроса CTL_CODE определить 8 разных кодов управления в/в: 4 таких же, как были определены в драйвере, и 4 новых, неизвестных драйверу:
#define FILE_DEVICE_IOCTL 0x00008301
#define IOCTL_MY_NEITHER CTL_CODE(FILE_DEVICE_IOCTL, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_MY_BUFFERED CTL_CODE(FILE_DEVICE_IOCTL, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_MY_INDIRECT CTL_CODE(FILE_DEVICE_IOCTL, 0x802, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_MY_OUTDIRECT CTL_CODE(FILE_DEVICE_IOCTL, 0x803, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_UNK_NEITHER CTL_CODE(FILE_DEVICE_IOCTL, 0x804, METHOD_NEITHER, FILE_ANY_ACCESS)
#define IOCTL_UNK_BUFFERED CTL_CODE(FILE_DEVICE_IOCTL, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_UNK_INDIRECT CTL_CODE(FILE_DEVICE_IOCTL, 0x806, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_UNK_OUTDIRECT CTL_CODE(FILE_DEVICE_IOCTL, 0x807, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
Для того, чтобы иметь возможность пользоваться макросом, необходимо подключить стандартный заголовочный файл winioctl.h.