Определение кодов элементов управления вводом-выводом

При определении новых ioCTL важно помнить о следующих правилах:

  • Если новый IOCTL будет доступен для компонентов программного обеспечения в пользовательском режиме, IOCTL необходимо использовать с IRP_MJ_DEVICE_CONTROL запросов. Компоненты пользовательского режима отправляют запросы IRP_MJ_DEVICE_CONTROL путем вызова DeviceIoControl, который является функцией Win32.
  • Если новый IOCTL будет доступен только для компонентов драйвера в режиме ядра, IOCTL необходимо использовать с IRP_MJ_INTERNAL_DEVICE_CONTROL запросов. Компоненты режима ядра создают IRP_MJ_INTERNAL_DEVICE_CONTROL запросы путем вызова IoBuildDeviceIoControlRequest. Дополнительные сведения см. в разделе Создание запросов IOCTL в драйверах.

Код элемента управления вводом-выводом — это 32-разрядное значение, состоящее из нескольких полей. На следующем рисунке показан макет кодов элементов управления вводом-выводом.

схема, иллюстрирующая макет кода элемента управления вводом-выводом.

Используйте предоставленный системой CTL_CODE макрос, определенный в Wdm.h и Ntddk.h, для определения новых кодов управления вводом-выводом. Определение нового кода IOCTL, предназначенного для IRP_MJ_DEVICE_CONTROL илиIRP_MJ_INTERNAL_DEVICE_CONTROL запросов, использует следующий формат:

#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

Выберите описательное имя константы для IOCTL в форме IOCTL_Device_Function, где Device указывает тип устройства, а Function — операцию. Пример имени константы — IOCTL_VIDEO_ENABLE_CURSOR.

Укажите следующие параметры макроса CTL_CODE :

DeviceType
Определяет тип устройства. Это значение должно соответствовать значению, заданному в элементе DeviceTypeструктуры DEVICE_OBJECT драйвера. (См . раздел Указание типов устройств). Значения менее 0x8000 зарезервированы для корпорации Майкрософт. Поставщики могут использовать значения 0x8000 и выше. Обратите внимание, что значения, назначенные поставщиком, задают общий бит.

FunctionCode
Определяет функцию, выполняемую драйвером. Значения менее 0x800 зарезервированы для корпорации Майкрософт. Поставщики могут использовать значения 0x800 и выше. Обратите внимание, что значения, назначаемые поставщиком, задают Пользовательский бит.

TransferType
Указывает, как система будет передавать данные между вызывающим объектом DeviceIoControl (или IoBuildDeviceIoControlRequest) и драйвером, обрабатывающим IRP.

Используйте одну из следующих системных констант:

METHOD_BUFFERED
Указывает метод буферизованного ввода-вывода , который обычно используется для передачи небольших объемов данных на запрос. Большинство кодов элементов управления вводом-выводом для устройств и промежуточных драйверов используют это значение TransferType .

Сведения о том, как система задает буферы данных для METHOD_BUFFERED контрольных кодов ввода-вывода, см. в разделе Описания буфера для кодов управления вводом-выводом.

Дополнительные сведения о буферизованном вводе-выводе см. в разделе Использование буферизованного ввода-вывода.

METHOD_IN_DIRECT или METHOD_OUT_DIRECT
Указывает метод прямого ввода-вывода , который обычно используется для чтения или записи больших объемов данных с помощью DMA или PIO, которые необходимо быстро передать.

Укажите METHOD_IN_DIRECT, будет ли вызывающий объект DeviceIoControl или IoBuildDeviceIoControlRequest передавать данные драйверу.

Укажите METHOD_OUT_DIRECT, будет ли вызывающий объект DeviceIoControl или IoBuildDeviceIoControlRequest получать данные от драйвера.

Сведения о том, как система задает буферы данных для METHOD_IN_DIRECT и METHOD_OUT_DIRECT контрольных кодов ввода-вывода, см. в разделе Описания буфера для кодов управления вводом-выводом.

Дополнительные сведения о прямом вводе-выводе см. в разделе Использование прямого ввода-вывода.

METHOD_NEITHER
Не указывает ни буферный, ни прямой ввод-вывод. Диспетчер ввода-вывода не предоставляет системных буферов или многомерных выражений. IRP предоставляет виртуальные адреса входных и выходных буферов, указанные для DeviceIoControl или IoBuildDeviceIoControlRequest, без проверки или сопоставления.

Сведения о том, как система задает буферы данных для METHOD_NEITHER контрольных кодов ввода-вывода, см. в разделе Описания буфера для кодов управления вводом-выводом.

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

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

Дополнительные сведения см. в разделе Использование операций ввода-вывода без буферизации и прямого ввода-вывода.

RequiredAccess
Указывает тип доступа, который вызывающий объект должен запрашивать при открытии объекта файла, представляющего устройство (см . IRP_MJ_CREATE). Диспетчер ввода-вывода создаст irp и вызовет драйвер с определенным кодом управления вводом-выводом, только если вызывающий объект запросил указанные права доступа. RequiredAccess указывается с помощью следующих системных констант:

FILE_ANY_ACCESS
Диспетчер ввода-вывода отправляет IRP для любого вызывающего объекта, который имеет дескриптор, в файловый объект, представляющий объект целевого устройства.

FILE_READ_DATA
Диспетчер ввода-вывода отправляет IRP только вызывающему объекту с правами доступа на чтение, что позволяет базовому драйверу устройства передавать данные с устройства в системную память.

FILE_WRITE_DATA
Диспетчер ввода-вывода отправляет IRP только вызывающему объекту с правами доступа на запись, позволяя базовому драйверу устройства передавать данные из системной памяти на устройство.

FILE_READ_DATA и FILE_WRITE_DATA могут быть объединены, если вызывающий объект должен иметь права на чтение и запись.

Некоторые системные коды управления вводом-выводом имеют значение RequiredAccess FILE_ANY_ACCESS, что позволяет вызывающему объекту отправлять определенный IOCTL независимо от доступа, предоставленного устройству. Примеры включают коды управления вводом-выводом, которые отправляются драйверам монопольных устройств.

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

#define IOCTL_DISK_SET_PARTITION_INFO\
        CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
        FILE_READ_DATA | FILE_WRITE_DATA)

Примечание

Прежде чем указывать FILE_ANY_ACCESS для нового кода IOCTL, вы должны быть абсолютно уверены, что разрешение неограниченного доступа к устройству не создает возможный путь для злоумышленников для компрометации системы.

Драйверы могут использовать IoValidateDeviceIoControlAccess для более строгой проверки доступа, чем обеспечивается битами RequiredAccess IOCTL.

Другие полезные макросы

Следующие макросы полезны для извлечения 16-разрядных полей DeviceType и 2-bit TransferType из кода IOCTL:

#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode)   (((ULONG)(ctrlCode & 0xffff0000)) >> 16)
#define METHOD_FROM_CTL_CODE(ctrlCode)        ((ULONG)(ctrlCode & 3))

Эти макросы определены в Wdm.h и Ntddk.h.