Перейти к содержимому

Создание драйверов. Часть 4. Обмен данными

Создание драйверов. Часть 4. Обмен данными. В данной статье рассмотрены способы обмена данными между приложением и драйвером, при помощи рабочих процедур драйвера с IRP кодом IRP_MJ_READ, IRP_MJ_WRITE и IRP_MJ_DEVICE_CONTROL. Из второй части цикла статей и примеров по созданию и работе с драйверами, мы узнали, как отправлять драйверу IOCTL запросы, в зависимости от которых драйвер будет предпринимать те или иные действия.
В обмене данных между приложением и драйвером активное участие принимают IRP(I/o Request Packet) пакеты, в которых содержится фиксированная часть (заголовок) и изменяющаяся часть (стек), IRP пакет представляет собой структуру, более наглядно представленной на рисунке
 Создание драйверов.Часть 4. Обмен данными.
Таблица: Заголовок IRP пакета
 Создание драйверов.Часть 4. Обмен данными.
Для получения указателя на стек IRP пакета используется функция:
PIO_STACK_LOCATION IoGetCurrentIrpStackLocation(IN PIRP Irp);
аргументом, которой является указатель на IRP пакет.

При работе с IRP необходимо указать диспетчеру ввода/вывода (IOManager), каким способом будет происходить управление буферами, существуют три вида:
— буферизованный ввод-вывод (buffered I/O);
— прямой ввод-вывод (direct I/O);
— ввод-вывод без управления (neither I/O).
Установка одного из видов управления осуществляется сразу после создания объекта устройства, то есть после вызова функции IoCreateDevice:

PDEVICE_OBJECT fdo;
status = IoCreateDevice(..,..,..,..,..,..,&fdo);
fdo->Flags |= DO_BUFFERED_IO;
или:
fdo->Flags |= DO_DIRECT_IO;
или:
fdo->Flags |= 0;

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

NTSTATUS TestControl (IN PDEVICE_OBJECT fdo, IN PIRP irp);
NTSTATUS TestWrite (IN PDEVICE_OBJECT fdo, IN PIRP irp);
NTSTATUS TestRead (IN PDEVICE_OBJECT fdo, IN PIRP irp);

и в функции DriverEntry добавить точки входа в драйвер:

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = TestControl;
DriverObject->MajorFunction[IRP_MJ_READ] = TestRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = TestWrite;

Более наглядно взаимодействие приложения и драйвера показано на рисунке:
Создание драйверов.Часть 4. Обмен данными.
Рассмотрим обмен данными между приложение и драйвером при помощи функции DeviceIoControl, которая позволяет одновременно передавать в драйвер данные и принимать, для этого служат 3, 4, 5 и 6 аргументы, то есть 3 аргумент, это указатель на данные которые буду передаваться в драйвер, 4 аргумент размер передаваемых данных, соответственно 5 аргумент функции, буфер куда будет происходить прием данных и 6 аргумент размер принятых данных. Более подробное описание функции можно найти в MSDN`е. В прилагаемом примере мною был создан собственный IOCTL запрос при помощи макроса CTL_CODE:
#define IOCTL_FROM_CORE CTL_CODE(0x00008000,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)
при помощи которого указаны тип устройства, управляющий код, способ получения доступа к буферу и тип доступа, более наглядно вы можете это видеть на следующем рисунке:
 Создание драйверов

char szoutBuff[] = "DeviceIoControl+IOCTL_FROM_CORE";
char szinBuff[80]; ZeroMemory(&szinBuff,sizeof(szinBuff));
if(DeviceIoControl(hHandl,ioctl,
&szinBuff,sizeof(szinBuff),//in
&szoutBuff,sizeof(szoutBuff),//out
&dwReturnBytes,NULL)){/*ok*/}else{/*error*/ }

Рабочая процедура драйвера по обработке IOCTL запросов выглядит следующим образом:

NTSTATUS TestControl (IN PDEVICE_OBJECT fdo, IN PIRP irp)
{
        PVOID pBuffer = NULL;
        UCHAR usString[] = "www.code.hut1.ru";
        PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(irp);
        ULONG ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
 
        ULONG InputLength  = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
        ULONG OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
 
        pBuffer = irp->UserBuffer;
        if(ControlCode == IOCTL_FROM_CORE)
        {
                #if DBG
                        DbgPrint("IoControlCode IOCTL_FROM_CORE: %d",ControlCode);
                        DbgPrint("TestControl(InputLength): %d",InputLength);
                        DbgPrint("TestControl(OutputLength): %d",OutputLength);
                        DbgPrint("TestControl(pBuffer): %s",pBuffer);
                #endif
 
                RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,&usString,InputLength);
        }
 
        irp->IoStatus.Status      = STATUS_SUCCESS;
        irp->IoStatus.Information = InputLength;
        IoCompleteRequest(irp, IO_NO_INCREMENT);
 
        return STATUS_SUCCESS;
}

Теперь подробнее о коде:
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(irp); — получение указателя на стек пакета;
ULONG ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode; — получение управляющего IOCTL кода;
ULONG InputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; — получение размера передаваемого из приложения буфера;
ULONG OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; — получение размера буфера которое ожидает приложение;
pBuffer = irp->UserBuffer; — получение переданной строки, в данном случае «DeviceIoControl+IOCTL_FROM_CORE»;
RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,&usString,InputLength); — передача строки приложению, в данном случае «www.code.hut1.ru»;
irp->IoStatus.Information = InputLength; — количество переданных байт.

Следующим шагом будет, запись в драйвер при помощи функции WriteFile:

char outBuffer[] = "This is a test string";
DWORD outCount = sizeof(outBuffer);
DWORD bW;
if(WriteFile(hHandl,outBuffer,outCount,&bW,NULL)){/*ok*/}else{/*error*/}

Код рабочей процедуры драйвера по обработке IRP с кодом IRP_MJ_WRITE:

NTSTATUS TestWrite(IN PDEVICE_OBJECT fdo, IN PIRP irp)
{
        PVOID pBuffer = NULL;
        ULONG ulSize = 0;
 
        PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(irp);
 
        ulSize  = IrpStack->Parameters.Write.Length;
        pBuffer = irp->AssociatedIrp.SystemBuffer;
        
        
#if DBG
        DbgPrint("Run TestWrite");
        DbgPrint("ulSize:  %d", ulSize);
        DbgPrint("pBuffer: %s",pBuffer);
#endif
 
        irp->IoStatus.Status      = STATUS_SUCCESS;
        irp->IoStatus.Information = ulSize;
        IoCompleteRequest(irp, IO_NO_INCREMENT);
                
        return STATUS_SUCCESS;
}

Здесь драйвер просто выводит полученный буфер при помощи функции DbgPrint().
ulSize = IrpStack->Parameters.Write.Length; — получение количества прочитанных байт;
pBuffer = irp->AssociatedIrp.SystemBuffer; — получение переданного буфера.

Чтение данных из драйвера при помощи функции ReadFile:

char inBuffer[80];DWORD inCount = sizeof(inBuffer);DWORD bR;
if(ReadFile(hHandl,inBuffer, inCount, &bR, NULL)){/*ok*/}else{/*error*/ }

Код рабочей процедуры драйвера по обработке IRP с кодом IRP_MJ_READ:

NTSTATUS TestRead(IN PDEVICE_OBJECT fdo, IN PIRP irp)
{
        PVOID pBuffer = NULL;
        UCHAR usString[] = "Code by Lazy_elf";
        ULONG ulSize = 0;
 
        PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(irp);
 
        ulSize  = IrpStack->Parameters.Read.Length;
        pBuffer = irp->AssociatedIrp.SystemBuffer;
 
        RtlCopyMemory(pBuffer,&usString,ulSize);
 
 
 
#if DBG
        DbgPrint("Run TestRead");
        DbgPrint("ulSize:  %d", ulSize);
        DbgPrint("pBuffer: %s",pBuffer);
#endif
        irp->IoStatus.Status      = STATUS_SUCCESS;
        irp->IoStatus.Information = ulSize;
        IoCompleteRequest(irp, IO_NO_INCREMENT);
                
        return STATUS_SUCCESS;
}

Самый важный момент в это участке кода: RtlCopyMemory(pBuffer,&usString,ulSize); — где происходит копирование внутреннего буфера содержавшего строку «Code by Lazy_elf» в буфер который будет передан приложению.
Приложение и драйвер тестировались при помощи программы Dbgview(Mark Russinovich), результат которой представлен на рисунке:
Создание драйверов.Часть 4. Обмен данными.
Создание драйверов.Часть 4. Обмен данными.

Метки:

4 комментария для “Создание драйверов. Часть 4. Обмен данными”

  1. Отличная статья. Большое спасибо автору за труды.

  2. Хлуденьков Владимир

    Очень познавательно. Спасибо большое. Требуем продолжения банкета!

    1. Благодарю за положительный комментарий. К сожалению, продолжений не будет, так как в данный момент занимаюсь программированием 1С.

Обсуждение закрыто.