Здравствуйте!
Помогите, пожалуйста, найти грамотное решение вот такой задачки.
Есть файловый фильтр, осуществляющий фильтрацию IRP_MJ_CREATE.
Этот фильтр на основе тех или иных критериев должен сделать одно из следующего:
Разрешить операцию, т.е. просто переслать запрос ниже.
Запретить операцию, т.е. завершить запрос с соответствующим статусом.
Спросить у пользователя "что делать с этим запросом?".
Вот сейчас думаю как грамотно реализовать третий пункт?
Придумалось пока вот что:
1. Драйвер создаёт именованное событие через IoCreateNotificationEvent() с именем что-то типа \??\EventName. Юзермодное приложение открывает это событие через OpenEvent().
2. Драйвер создаёт внутренний список (очередь FIFO) pending-запросов.
3. Для всех поступающих IRP, соответствующих определённым критериям, драйвер делает следующее:
3.1. Создаёт примерно такую структуру:
3.2. Ставит её во внутреннюю очередь.
3.3. Ставит глобальное событие \??\EventName в состояние signaled.
3.4. Входит в ожидание события REQUEST_ENTRY.Event через KeWaitForSingleObject().
4. Приложение получает уведомление о событии \??\EventName через WaitForSingleObject().
5. По получении уведомления приложение шлёт что-то типа IOCTL_QUERY_REQUEST_DATA драйверу.
6. Драйвер заполняет буфер приложения данными по первому запросу в очереди (в том числе передаёт и указатель на IRP) и завершает запрос IOCTL_QUERY_REQUEST_DATA.
7. Приложение отображает пользователю окно с данными запроса (например, имя файла) и пользователь жмёт, например, либо "Разрешить" либо "Запретить".
8. Приложение шлёт драйверу что-то типа IOCTL_RESOLVE_REQUEST вот с такой примерно структурой:
9. Драйвер по получении IOCTL_RESOLVE_REQUEST делает следующее:
9.1. Прочёсывает всю свою внутреннюю очередь и ищет в ней указанный IRP по указателю RESOLVE_ENTRY.Irp.
9.2. Если нашёл — ставит поле REQUEST_ENTRY.UserAction в значение RESOLVE_ENTRY.UserAction.
9.3. Ставит событие REQUEST_ENTRY.Event в состояние signaled.
10. Далее запрос IRP_MJ_CREATE, который заснул в ожидании события REQUEST_ENTRY.Event, просыпается и происходит следующее:
10.1. Считывается значения поля REQUEST_ENTRY.UserAction и запоминается в локальной переменной.
10.2. REQUEST_ENTRY удаляется из внутреней очереди и уничтожается.
10.3. Выполняется выбранное пользователем действие.
Вот так, что-то вроде того .
Просьба к гуру: прокомментируйте, пожалуйста, мои мысли.
Насколько приведённый алгоритм грамотен?
Какие могут быть подводные камни?
Как можно сделать безопаснее / красивее / эффективнее?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте! А>Помогите, пожалуйста, найти грамотное решение вот такой задачки.
А>Есть файловый фильтр, осуществляющий фильтрацию IRP_MJ_CREATE. А>Этот фильтр на основе тех или иных критериев должен сделать одно из следующего:
А>Разрешить операцию, т.е. просто переслать запрос ниже. А>Запретить операцию, т.е. завершить запрос с соответствующим статусом. А>Спросить у пользователя "что делать с этим запросом?".
А>Вот сейчас думаю как грамотно реализовать третий пункт? А>Придумалось пока вот что:
[skipped]
Примерно такой механизм что вы описали выше работает давно, называется "Inverted call", поищите по этим ключевым словам и найдете подробное описание.
Искать можете на рсдн, найдете много чего, а также в оригинале (если я конечно не ошибаюсь насчет источника) эта статья есть на osronline.com
Механизм примерно следующий: user-mode приложение шлет запрос драйверу, драйвер помечает его как pending, приложение дожидается завершения запроса, получая из него необходимые данные, как то "Спросить у пользователя что делать с этим запросом?", в котором и содержится необходимая информация о самом запросе. А в ответ приложение шлет новый запрос, содержащий ответ, ну и соответственно информацию позволяющую идентифицировать то самое действие о котором драйвер хотел спросить у приложения. Драйвер получив данный запрос выполняет соответствующие действия, чаще всего заверщает опреацию с соответствующим статусом. А также приложение посылает новый запрос, на тот случай если драйверу опять понадобится что то спросить у приложения.
Так как был упомянут файловый фильтр, думаю будет нелишним напомнить, что если используется minifilter модель для фильтрации, то есть такой неплохой механизм, как FilterCommunicationPort, вполне себе эффективный механизм
Re[2]: Как грамотно организовать выборку из KM ?
От:
Аноним
Дата:
06.11.07 13:56
Оценка:
AF>Примерно такой механизм что вы описали выше работает давно, называется "Inverted call"
Ok, я понял. Значит двигаюсь я в правильном направлении.
Вот ещё вопросы:
1. А существует ли способ обменяться вопросом/ответом с приложением с помощью одного IOCTL, посланного приложением ? Как такое можно реализовать ?
2. Что вы можете сказать конкретно по моей реализации ?
3. Зачем помечать IRP как pending, когда можно банально войти в ожидание по KeWaitForSingleObject(), благо IRQL позволяет ? Или в этом есть какой-то смысл, которого я не замечаю ?
Здравствуйте, Аноним, Вы писали:
А>1. А существует ли способ обменяться вопросом/ответом с приложением с помощью одного IOCTL, посланного приложением ? Как такое можно реализовать ?
Так и делается обычно. IRP, посылаемый драйверу, может содержать ответ пользователя на текущий запрос, а при завершении драйвер может передать с помощью того же IRP новый запрос. Ну и так по кругу
А>2. Что вы можете сказать конкретно по моей реализации ?
Избыточна, по-моему. Зачем событие дополнительное, если и так собираетесь обмениваться IRP, факт получения/завершения которых и так уже сами по себе события, которые и нужно мониторить. Плюс еще ваша реализация не учитывает возможность отмены (IoCancelIrp) оригинального запроса ввода-вывода в процессе ожидания реакции пользователя. Наконец, заставлять поток ждать не нужно, если операция асинхронная (хотя у вас в примере всегда синхронный CREATE рассматривается, но вдруг планируется на другие события подобную схему распространить).
А>3. Зачем помечать IRP как pending, когда можно банально войти в ожидание по KeWaitForSingleObject(), благо IRQL позволяет ? Или в этом есть какой-то смысл, которого я не замечаю ?
А если вы обрабатываете асинхронную операцию? Ждать нельзя долго. Плюс cancellation.. В висте уже create — опреация в принципе отменяемая, так что если планируются задержки (а они неизбежны, если пользователь участвует в принятии решения по запросу , то и IRP_MJ_CREATE из вашего примера нужно откладывать и завершать из рабочего потока, установив cancel-процедуру перед этим.
В общем читайте пример от OSR. Брать "как есть" не рекомендую. Советую ознакомиться с Cancel-Safe IRP Queues и реализовать у себя для организации очередей IRP.
Re[4]: Группировка запросов!
От:
Аноним
Дата:
09.11.07 17:29
Оценка:
Здравствуйте, Sergey Storozhevykh, Вы писали:
Сергей, большое спасибо вам за ответы !
Всё получилось за исключением одного.
Проблема вот в чём.
Берём Блокнот, пытаемся открыть файл.
Если на первый же запрос IRP_MJ_CREATE от Блокнота на этот файл ответить STATUS_ACCESS_DENIED, то Блокнот скажет "Отказано в доступе" и, естественно, ничего не откроет.
Но вот если на первый запрос ответить STATUS_SUCCESS, то Блокнот, цука такая, пошлёт ещё около 3х запросов IRP_MJ_CREATE для этого же файла.
На основании серии одинаковых сообщений от юзермодной части приложения пользователь сделает вывод что-то вроде "Мой Блокнот сошёл с ума! Он открывает один и тот же файл 5 раз подряд!! Хелп!!!".
Т.е. проблема сводиться к группировке запросов от одного и того же приложения для одного и того же файла, чтобы лишний раз не тревожить пользователя.
Кто как борол сие безобразие? Какие есть способы?
Спасибо!
Здравствуйте, Аноним, Вы писали:
А>На основании серии одинаковых сообщений от юзермодной части приложения пользователь сделает вывод что-то вроде "Мой Блокнот сошёл с ума! Он открывает один и тот же файл 5 раз подряд!! Хелп!!!". А>Т.е. проблема сводиться к группировке запросов от одного и того же приложения для одного и того же файла, чтобы лишний раз не тревожить пользователя.
По-моему, ничего группировать не нужно. Обычно для разрешения таких ситуаций используются разные типы правил, которые генерируются при ответе пользователя. Причем пользователь сам определяет как ваша программа себя должна вести в дальнейшем.
Можно в диалоге пользователю предоставить выбор:
1. Разрешить однократно. Запрос разрешается, дополнительные правила не генерируются. Каждый последующий подобный запрос будет приводить к задействованию режима обучения и требовать реакции пользователя.
2. Разрешить такой запрос для данного экземпляра приложения. Тогда запрос разрешается, в драйвер уходит правило, разрешающее данному процессу (идентифицируемому по PID, например), выполнять подобные запроосы. В правило можно добавить другие параметры: пользователя, например. Соответственно пользователя больше не побеспокоят одинаковые лерны от этого процесса. При завершении процесса правило автоматически удаляется.
3. Разрешить данный тип запросов для конкретного приложения (например, read/write file.dat для notepad.exe от имени user1). Причем приложение желательно идентифицировать не только по имени, но и по хешу.
4. Разрешить все запросы для этого приложения/экземпляра приложения.
Можно еще придумать варианты, в зависимости от того, что хотите получить. Режим обучения для того и нужен, чтоб программа могла на основе ответа пользователя сгенерировать правила, описывающие конкретную ситуцию, с тем чтоб в дальнейшем не беспокоить пользователя. И важно пользователю дать возможность самому решать как себя в дальнейшем ваша программа должна вести при обнаружении похожих ситуаций.
Re[6]: Группировка запросов!
От:
Аноним
Дата:
12.11.07 12:56
Оценка:
SS>Можно в диалоге пользователю предоставить выбор: SS>1. Разрешить однократно. Запрос разрешается, дополнительные правила не генерируются. Каждый последующий подобный запрос будет приводить к задействованию режима обучения и требовать реакции пользователя. SS>2. Разрешить такой запрос для данного экземпляра приложения. Тогда запрос разрешается, в драйвер уходит правило, разрешающее данному процессу (идентифицируемому по PID, например), выполнять подобные запроосы. В правило можно добавить другие параметры: пользователя, например. Соответственно пользователя больше не побеспокоят одинаковые лерны от этого процесса. При завершении процесса правило автоматически удаляется. SS>3. Разрешить данный тип запросов для конкретного приложения (например, read/write file.dat для notepad.exe от имени user1). Причем приложение желательно идентифицировать не только по имени, но и по хешу. SS>4. Разрешить все запросы для этого приложения/экземпляра приложения.
Спасибо вам, Сергей!
Очень интересные мысли!
Я чего я сам не догадался...