Re: Как грамотно организовать выборку из KM ?
От: Alexey Frolov Беларусь  
Дата: 05.11.07 11:41
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте!

А>Помогите, пожалуйста, найти грамотное решение вот такой задачки.

А>Есть файловый фильтр, осуществляющий фильтрацию IRP_MJ_CREATE.

А>Этот фильтр на основе тех или иных критериев должен сделать одно из следующего:

А>
  • Разрешить операцию, т.е. просто переслать запрос ниже.
    А>
  • Запретить операцию, т.е. завершить запрос с соответствующим статусом.
    А>
  • Спросить у пользователя "что делать с этим запросом?".

    А>Вот сейчас думаю как грамотно реализовать третий пункт?

    А>Придумалось пока вот что:

    [skipped]

    Примерно такой механизм что вы описали выше работает давно, называется "Inverted call", поищите по этим ключевым словам и найдете подробное описание.
    Искать можете на рсдн, найдете много чего, а также в оригинале (если я конечно не ошибаюсь насчет источника) эта статья есть на osronline.com

    Механизм примерно следующий: user-mode приложение шлет запрос драйверу, драйвер помечает его как pending, приложение дожидается завершения запроса, получая из него необходимые данные, как то "Спросить у пользователя что делать с этим запросом?", в котором и содержится необходимая информация о самом запросе. А в ответ приложение шлет новый запрос, содержащий ответ, ну и соответственно информацию позволяющую идентифицировать то самое действие о котором драйвер хотел спросить у приложения. Драйвер получив данный запрос выполняет соответствующие действия, чаще всего заверщает опреацию с соответствующим статусом. А также приложение посылает новый запрос, на тот случай если драйверу опять понадобится что то спросить у приложения.

    Так как был упомянут файловый фильтр, думаю будет нелишним напомнить, что если используется minifilter модель для фильтрации, то есть такой неплохой механизм, как FilterCommunicationPort, вполне себе эффективный механизм
  • Как грамотно организовать выборку из KM ?
    От: Аноним  
    Дата: 30.10.07 15:24
    Оценка:
    Здравствуйте!
    Помогите, пожалуйста, найти грамотное решение вот такой задачки.

    Есть файловый фильтр, осуществляющий фильтрацию IRP_MJ_CREATE.
    Этот фильтр на основе тех или иных критериев должен сделать одно из следующего:

  • Разрешить операцию, т.е. просто переслать запрос ниже.
  • Запретить операцию, т.е. завершить запрос с соответствующим статусом.
  • Спросить у пользователя "что делать с этим запросом?".

    Вот сейчас думаю как грамотно реализовать третий пункт?
    Придумалось пока вот что:

    1. Драйвер создаёт именованное событие через IoCreateNotificationEvent() с именем что-то типа \??\EventName. Юзермодное приложение открывает это событие через OpenEvent().
    2. Драйвер создаёт внутренний список (очередь FIFO) pending-запросов.
    3. Для всех поступающих IRP, соответствующих определённым критериям, драйвер делает следующее:
    3.1. Создаёт примерно такую структуру:

    typedef struct _REQUEST_ENTRY
    {
      KEVENT Event;
      PIRP Irp;
      ULONG UserAction;
    }
    REQUEST_ENTRY, *PREQUEST_ENTRY;


    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 вот с такой примерно структурой:

    typedef struct _RESOLVE_ENTRY
    {
      PIRP Irp;
      ULONG UserAction;
    }
    RESOLVE_ENTRY, *PRESOLVE_ENTRY;


    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. Выполняется выбранное пользователем действие.

    Вот так, что-то вроде того .

    Просьба к гуру: прокомментируйте, пожалуйста, мои мысли.
    Насколько приведённый алгоритм грамотен?
    Какие могут быть подводные камни?
    Как можно сделать безопаснее / красивее / эффективнее?

    Спасибо!
  • Re[2]: Как грамотно организовать выборку из KM ?
    От: Аноним  
    Дата: 06.11.07 13:56
    Оценка:
    AF>Примерно такой механизм что вы описали выше работает давно, называется "Inverted call"

    Ok, я понял. Значит двигаюсь я в правильном направлении.
    Вот ещё вопросы:

    1. А существует ли способ обменяться вопросом/ответом с приложением с помощью одного IOCTL, посланного приложением ? Как такое можно реализовать ?
    2. Что вы можете сказать конкретно по моей реализации ?
    3. Зачем помечать IRP как pending, когда можно банально войти в ожидание по KeWaitForSingleObject(), благо IRQL позволяет ? Или в этом есть какой-то смысл, которого я не замечаю ?
    Re[3]: Как грамотно организовать выборку из KM ?
    От: Sergey Storozhevykh Россия  
    Дата: 06.11.07 14:58
    Оценка:
    Здравствуйте, Аноним, Вы писали:

    А>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 раз подряд!! Хелп!!!".
    Т.е. проблема сводиться к группировке запросов от одного и того же приложения для одного и того же файла, чтобы лишний раз не тревожить пользователя.
    Кто как борол сие безобразие? Какие есть способы?
    Спасибо!
    Re[5]: Группировка запросов!
    От: Sergey Storozhevykh Россия  
    Дата: 12.11.07 07:53
    Оценка:
    Здравствуйте, Аноним, Вы писали:

    А>На основании серии одинаковых сообщений от юзермодной части приложения пользователь сделает вывод что-то вроде "Мой Блокнот сошёл с ума! Он открывает один и тот же файл 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. Разрешить все запросы для этого приложения/экземпляра приложения.

    Спасибо вам, Сергей!
    Очень интересные мысли!
    Я чего я сам не догадался...
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.