Программирование систем защиты

         

Драйверы - фильтры

Система ввода/вывода Windows NT является расширяемой. Один из методов расширения возможностей системы ввода/вывода - разработка и применение драйверов-фильтров.
Драйвер-фильтр - это промежуточный драйвер, перехватывающий запросы, предназначенные некоторым программным модулям (например, драйверу файловой системы или драйверу диска). Перехватив запрос, драйвер-фильтр имеет возможность либо расширения, либо замещения функциональности, обеспечиваемой первоначальным получателем запроса. Драйвер-фильтр может, либо использовать сервисы драйвера, которому изначально предназначался запрос, либо использовать сервисы других программных модулей уровня ядра для обеспечения дополнительной функциональности. Драйверы-фильтры могут встраиваться в любое место в стеке драйверов, их может быть несколько, в том числе расположенных подряд.
Примером использования драйвера-фильтра может послужить реализация прозрачного зашифрования/расшифрования данных, хранимых на диске или в сети. Ни одна из поставляемых с ОС Windows NT файловых систем (FASTFAT, NTFS, CDFS, LAN Manager Redirector) не обеспечивает поддержки прозрачного зашифрования данных перед записью их на диск (или отправкой в сеть) и расшифрования




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

  • Присоединение к нужному объекту-устройству. Диспетчер ввода/вывода включает возможность присоединения к объекту-устройству (1), созданному некоторым драйвером (1), объекта-устройства (2), созданного другим драйвером(2). В результате такого присоединения пакеты IRP, направленные драйверу (1), ассоциированному с объектом-устройством (1), будут перенаправляться драйверу (2), ассоциированному с присоединенным объектом-устройством (2). Этот «присоединенный» драйвер (2) и является драйвером-фильтром.

Каждый объект-устройство имеет поле, называемое AttachedDevice и содержащее указатель на объект-устройство, созданное драйвером-фильтром, который первым присоединил свое устройство. Если это поле содержит NULL, значит нет присоединенных устройств. В процессе присоединения нового объекта-устройства, процедура присоединения пройдет по связанному списку присоединенных устройств до конца и выполнит присоединение нового объекта-устройства к последнему объекту-устройству в этом списке. В результате, любой запрос на создание/открытие, предназначающийся некоторому объекту-устройству, будет перенаправляться последнему (в его списке присоединенных устройств) объекту-устройству. То есть последнему драйверу-фильтру, ассоциированному с последним объектом-устройством. Аналогично, при вызове функции, которая по имени объекта-устройства возвращает его указатель, будет возвращен указатель на объект-устройство, являющийся последним в списке присоединенных устройств.
Но важно заметить, что диспетчер ввода/вывода проходит до конца списка присоединенных устройств не во всех случаях. Например, никакого перенаправления не будет, если будет вызвана функция IoCallDriver(). Следовательно, чтобы обращение к драйверу назначения не миновало драйвер-фильтр, этот драйвер-фильтр должен присоединить свой объект-устройство к объекту-устройству драйвера-назначения, прежде, чем вышележащий драйвер вызовет процедуру определения указателя нижележащего объекта-устройства по его имени (это делается во время запроса на открытие), а затем будет использовать этот указатель (в частности при вызове функции IoCallDriver()).

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

Ниже рассмотрены основные принципы, которых необходимо придерживаться при разработке драйвера-фильтра, а также недостатки, из-за которых применение драйверов-фильтров становится нежелательным:

  1. 1. Разработчик драйвера-фильтра должен четко знать, как работает драйвер, создавший устройство, к которому будет присоединяться его драйвер. Драйвер-фильтр должен уметь обрабатывать все получаемые им запросы, адресованные на самом деле первоначальному драйверу, к которому он присоединился.

Несмотря на то, что все драйверы должны отвечать на стандартное множество запросов, выдаваемых менеджером ввода/вывода, в реальности, в случае разработки собственного драйвера-фильтра, нужно знать очень тонкие взаимосвязи, которые проявляются в зависимости от того, на каком уровне иерархии находится драйвер-фильтр. Возможны случаи, когда драйвер одной файловой системы запрашивает сервисы у драйвера другой файловой системы, или когда промежуточный драйвер запрашивает сервисы вышележащего драйвера файловой системы.
Например, в случае фильтрации всех запросов, предназначенных некоторому логическому тому, управляемому драйвером файловой системы (NTFS), необходимо понимать всевозможные пути вовлечения драйвера NTFS в обработку запроса, так как во всех этих случаях будут вовлекаться процедуры распределения драйвера-фильтра. Процедуры обработки запроса на чтения/запись драйвера файловой системы могут вовлекаться несколькими путями, например: из потока режима ядра, вызвавшего системные сервисы ZwReadFile(), ZwWriteFile(); из потока пользовательского режима, вызвавшего системные сервисы NtReadFile(), NtWriteFile(); из диспетчера памяти из-за обращения к отсутствующей странице спроецированного файла; из диспетчера кэша в результате асинхронного сброса буферов диспетчера кэша на диск; и так далее.
В некоторых ситуациях может быть приемлемым то, что драйвер - фильтр отправляет запрос ввода/вывода на асинхронное выполнение, но в других ситуациях (например, при обслуживании обращения к отсутствующей странице) драйвер-фильтр не должен этого делать, иначе это приведет к взаимной блокировке или зависанию.
В случае, когда драйвер-фильтр привязывается к объекту-устройству нижнего уровня (например, к устройству, представляющему физический диск), то возникают другие проблемы, с которыми сталкивается подобный драйвер-фильтр. Например, пакеты IRP, посланные драйверу диска, могут быть чаще всего ассоциированными пакетами, созданными драйвером файловой системы, и поэтому драйвер-фильтр не должен пытаться в свою очередь создавать ассоциированные пакеты. Обычно от драйверов дисков ожидают, что они будут выполнять операции асинхронно, поэтому драйвер-фильтр должен удовлетворять этим ожиданиям.

  1. 2. Драйвер-фильтр должен корректно работать с другими возможными драйверами-фильтрами, присоединенными до или после него. Никогда нельзя полагаться на то, что запрос, выпущенный драйвером-фильтром, пойдет напрямую нужному драйверу, так как он может быть перехвачен другим драйвером-фильтром.
  2. 3. Драйвер-фильтр должен знать, к чему он привязывается. Например, может получиться так, что когда драйвер-фильтр попытается привязаться к объекту-устройству, представляющему логический том файловой системы, он в действительности привяжется к объекту-устройству, представляющему физический диск, если том еще не был вмонтирован.
  3. 4. Драйвер-фильтр должен избегать поддержки ненужных ссылок. Если по небрежности драйвер-фильтр поддерживает лишнюю ссылку на объект-устройство назначения, то, возможно, что это будет препятствовать всем дальнейшим запросам на открытие и выполнение операций над этим устройством.
  4. 5. Драйвер-фильтр должен следить за контекстом потока, в котором выполняется его процедура распределения. В первую очередь это касается запросов ввода/ вывода к драйверам файловых систем, так как эти запросы должны передаваться в контексте потока, инициировавшего запрос, поэтому если драйвер-фильтр вызвал переключение контекста, например, послав запрос на исполнение рабочему потоку, то дальнейшая обработка этого запроса драйвером файловой системы приведет к непредсказуемым последствиям.

На всех уровнях, которые будут обсуждаться в дальнейшем, возможны реализация и применение драйверов-фильтров.


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