Как передать массив переменного размера из драйвера ядра Windows в процесс пользовательского режима?

Я изучаю программирование ядра Windows, и мне интересно, как передать массив байтов из драйвера ядра в приложение пользовательского режима, где драйвер ядра инициирует вызов?

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

Вот мой реальный пример: у меня есть функция обратного вызова ядра, которую можно вызывать в любое время с помощью STRING. Затем мне нужно передать строку из нее моему текущему процессу в пользовательском режиме и предупредить его.


person MikeF    schedule 19.06.2018    source источник
comment
Я не уверен, что понимаю. Вы имеете в виду, что вы предоставляете ядру функцию обратного вызова для его вызова? Потому что, если бы все было наоборот, у вас уже были бы данные внутри вашей программы.   -  person John Bollinger    schedule 19.06.2018
comment
@JohnBollinger: Мой драйвер ядра устанавливает функцию обратного вызова, которая вызывается из пространства ядра. После этого мне нужно передать массив байтов, предоставленный функцией обратного вызова, моему запущенному процессу пользовательского режима.   -  person MikeF    schedule 19.06.2018
comment
В порядке. Тогда как процесс пользовательского режима вообще связан с драйвером? То есть как драйвер узнает, куда доставить данные?   -  person John Bollinger    schedule 19.06.2018
comment
Подобные вещи обычно выполняются через DeviceIoControl, вы передаете буфер, который позже будет заполнен, ваш драйвер сначала возвращает STATUS_IO_PENDING, а затем завершает операцию, когда данные готовы), заполнив буфер предоставленным кодом пользовательского режима). Для кода пользовательского режима совсем не типично передавать функцию обратного вызова в режим ядра.   -  person SoronelHaetir    schedule 19.06.2018
comment
@SoronelHaetir: Спасибо. Пока я ждал, я провел дополнительные исследования, и это был один из предложенных методов. Хотя, как это будет работать, если ввод-вывод IOCTL, определенный драйвером, может оставаться незавершенным в течение неопределенного времени? Я также видел ссылки на использование PnP для такого рода целей, а именно IoReportTargetDeviceChangeAsynchronous() на стороне драйвера и RegisterDeviceNotification() и WM_DEVICECHANGE на стороне пользователя (без использования реального аппаратного устройства). Кроме того, был предложен объект раздела через ZwCreateSection(). Я просто не уверен, какой здесь подход рекомендуется?   -  person MikeF    schedule 19.06.2018
comment
самый мощный способ - использовать FltRegisterFilter + FltCreateCommunicationPort из ядра. FltStartFiltering можно даже не звонить, если не нужно прикреплять к томам. из пользовательского режима FilterConnectCommunicationPort, FilterGetMessage и т. д.   -  person RbMm    schedule 23.06.2018
comment
@RbMm: Спасибо, попробую. Под самым эффективным способом вы подразумеваете самую быструю коммуникацию или наибольшее количество возможностей, которые она предоставляет? Вы можете уточнить?   -  person MikeF    schedule 23.06.2018
comment
Я имею в виду под функциональным - обе стороны могут отправлять и получать сообщения (в режиме ядра через обратный вызов, в пользовательском режиме - асинхронный вызов), использовать буферы любого размера и т.д., конечно, существует много другого. скажем, драйвер может вставить apc в зарегистрированный пользовательский поток (скажем, этот дескриптор открытия потока на устройстве), но буферы здесь должны быть выделены отдельно. Пользователь может установить запрос к драйверу и драйверу, ожидающим этого запроса, и завершить его по событию, но пользователю необходимо заранее отправить этот запрос.   -  person RbMm    schedule 23.06.2018


Ответы (1)


Существует множество способов межпроцессного взаимодействия режима ядра и режима пользователя, и разные требования могут соответствовать разным методам.

Во-первых, у вас есть возможность использовать именованные каналы (даже в режиме ядра). Однако есть кое-что, что вы должны знать ... Это официально не задокументировано для обычного драйвера устройства режима ядра (хотя существует документированный интерфейс для драйверов устройств Mini-Filter файловой системы).

Если вы хотите использовать именованный канал из обычного драйвера устройства, работающего в режиме ядра, вам нужно будет найти адрес в NtCreateNamedPipeFile или положиться на IoCreateFile (на который NtCreateNamedPipeFile полагается внутри, используя недокументированную структуру).

Для использования именованного канала из драйвера устройства Mini-Filter файловой системы у вас есть FltCreateNamedPipeFile.

Переходя от идеи именованных каналов, у вас есть возможность вызова локальных процедур! Однако опять же очередной тупик с точки зрения документации. Однако относительно просто сделать это в качестве клиента в режиме ядра. Однако существует документированный интерфейс для портов с драйвером устройства Mini-Filter файловой системы: FltCreateCommunicationPort.

Двигаясь дальше, вы можете подключиться к клиенту пользовательского режима и писать прямо в его память.

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

person ImmortaleVBR    schedule 20.06.2018
comment
Спасибо за ваши предложения. Придется их исследовать. Тем не менее, я продолжаю видеть много ссылок на недокументированные вещи в пространстве ядра. Например, даже MSDN может предоставить функцию, но не объяснять элементы возвращаемой структуры. Пример: IoGetCurrentProcess(). Это нормально? В пользовательском режиме использование недокументированных API-интерфейсов обычно не одобряется. - person MikeF; 21.06.2018
comment
@MikeF Я согласен с вами насчет недокументированных API, я не думаю, что это хорошая идея. Иногда у вас может не быть выбора в зависимости от того, что вы пытаетесь сделать, и это очень досадно. Что касается IoGetCurrentProcess, который должен возвращать указатель на структуру _EPROCESS, но мы не должны возиться с полями (или обращаться к ним), поскольку это непрозрачная структура. У Microsoft будут причины, по которым они это сделали, но вы все равно можете читать / писать в поля через доступ к памяти, если у вас есть смещения (плохая идея, смещения могут измениться в любое время). - person ImmortaleVBR; 21.06.2018
comment
Тоже побочный вопрос. Я разместил комментарий выше. В ходе собственного исследования я обнаружил, что IoReportTargetDeviceChangeAsynchronous() может использоваться для маршалинга структуры произвольного размера в процесс пользовательского режима, который может использовать RegisterDeviceNotification() для извлечения ее через сообщение WM_DEVICECHANGE. Есть причина, по которой может не работать? (т.е. быть слишком медленным, ненадежным? поскольку мы имеем дело с оконными сообщениями. Или плохо использовать концепцию устройства PnP без реального устройства.) - person MikeF; 21.06.2018
comment
@MikeF Я никогда не делал этого раньше, поэтому не думаю, что было бы неплохо дать мне совет по этому поводу, но вы можете попробовать это в качестве эксперимента и посмотреть, подходит ли он для ваших требований. Мне жаль, что я не могу дать реального понимания этой идеи. Я предполагаю, что другой активный участник может наткнуться на эту тему, увидеть комментарии и знать достаточно об этой теме, чтобы присоединиться к ней и ответить на нее ... надеюсь. - person ImmortaleVBR; 21.06.2018