Программирование драйверов Windows

       

Рабочая процедура обработки IOCTL запросов


Процедура DeviceControlRoutine предназначена для обработки запросов Диспетчера ввода/вывода, которые он формирует в виде IRP пакетов с кодом IRP_MJ_DEVICE_CONTROL по результатам обращения к драйверу из пользовательских приложений с вызовами DeviceIoControl.

В нашем примере это самая важная функция. Она реализует обработку пяти IOCTL запросов:

  • IOCTL_PRINT_DEBUG_MESS — выводим отладочное сообщение в окно DebugView.
  • IOCTL_CHANGE_IRQL — проводим эксперимент, насколько высоко можно искусственно поднять уровень IRQL в коде драйвера.
  • IOCTL_MAKE_SYSTEM_CRASH — проводим эксперимент по "обрушению" операционной системы и пытаемся его предотвратить.
  • IOCTL_TOUCH_PORT_378H — проводим эксперимент по обращению к аппаратным ресурсам системы.
  • IOCTL_SEND_BYTE_TO_USER — отправляем байт данных в пользовательское приложение.
  • Эти IOCTL коды являются пользовательскими — они определены с помощью макроса CTL_CODE в файле Driver.h, который является частью данного проекта, и речь о котором пойдет ниже.



    Определения используемых ниже непривычных для программистов Win32 типов данных (например, UCHAR или PUCHAR) можно найти в DDK в файле Windef.h .

    // (Файл init.cpp) // DeviceControlRoutine: обработчик IRP_MJ_DEVICE_CONTROL запросов // Аргументы: // Указатель на объект нашего FDO // Указатель на структуру IRP, поступившего от Диспетчера ВВ // Возвращает: STATUS_XXX // #define SMALL_VERSION // В том случае, если не закомментировать верхнюю строчку v будет // выполнена компиляция версии, в которой будет обрабатываться только // один тип IOCTL запросов -- IOCTL_MAKE_SYSTEM_CRASH

    NTSTATUS DeviceControlRoutine( IN PDEVICE_OBJECT fdo, IN PIRP Irp ) { NTSTATUS status = STATUS_SUCCESS; ULONG BytesTxd =0; // Число переданных/полученных байт (пока 0) PIO_STACK_LOCATION IrpStack=IoGetCurrentIrpStackLocation(Irp);

    // Получаем указатель на расширение устройства PEXAMPLE_DEVICE_EXTENSION dx = (PEXAMPLE_DEVICE_EXTENSION)fdo->DeviceExtension; //------------------------------- // Выделяем из IRP собственно значение IOCTL кода, по поводу // которого случился вызов: ULONG ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode; ULONG method = ControlCode & 0x03;


    errDetected=1; }; #if DBG DbgPrint("-Example- Value of x is %X.",x); if(errDetected) DbgPrint("-Example- Except detected in Example driver."); #endif break; }

    #ifndef SMALL_VERSION case IOCTL_TOUCH_PORT_378H: { unsigned short ECRegister = 0x378+0x402; #if DBG DbgPrint("-Example- IOCTL_TOUCH_PORT_378H."); #endif // Пробуем программно перевести параллельный порт 378, // сконфигурированный средствами BIOS как ECP+EPP, в // режим EPP. _asm { mov dx,ECRegister ; xor al,al ; out dx,al ; Установить EPP mode 000 mov al,095h ; Биты 7:5 = 100 out dx,al ; Установить EPP mode 100 } // Подобные действия в приложении пользовательского // режима под NT обязательно привело бы к аварийной // выгрузке приложения с сообщением об ошибке! // Практически эти пять строк демонстрируют, что можно // работать с LPT портом под Windows NT ! break; }

    case IOCTL_SEND_BYTE_TO_USER: { // Размер данных, поступивших от пользователя: ULONG InputLength = //только лишь для примера IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Размер буфера для данных, ожидаемых пользователем ULONG OutputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; #if DBG DbgPrint("-Example- Buffer outlength %d",OutputLength); #endif

    if( OutputLengthAssociatedIrp.SystemBuffer; #if DBG DbgPrint("-Example- Method : BUFFERED."); #endif } else if (method==METHOD_NEITHER) { buff=(unsigned char*)Irp->UserBuffer; #if DBG DbgPrint("-Example- Method : NEITHER."); #endif } else { #if DBG DbgPrint("-Example- Method : unsupported."); #endif status = STATUS_INVALID_DEVICE_REQUEST; break; } #if DBG DbgPrint("-Example- Buffer address is %08X",buff); #endif *buff=33; // Любимое число Штирлица BytesTxd = 1; // Передали 1 байт break; } #endif // SMALL_VERSION // Ошибочный запрос (код IOCTL, который не обрабатывается): default: status = STATUS_INVALID_DEVICE_REQUEST; } // Освобождение спин-блокировки KeReleaseSpinLock(&MySpinLock,irql);



    #if DBG DbgPrint("-Example- DeviceIoControl: %d bytes written.", (int)BytesTxd); #endif

    return CompleteIrp(Irp,status,BytesTxd); // Завершение IRP }

    Корректность фрагмента кода, посвященного обработке IOCTL запроса IOCTL_TOUCH_PORT_378H, может вызвать споры, поскольку действует "напролом", не обращая внимания на то, что в системе могут быть устройства и драйвера, работающие с этим портом, существование которых следует учитывать. Однако цель данного примера &#8212 показать, что само по себе обращение к аппаратным ресурсам в режиме ядра является делом тривиальным, не имеющим ограничений со стороны операционной системы.


    Содержание раздела