Windows Filtering Platform в защите и нападенииНачиная с Server 2008 и Vista в винду был встроен механизм WFP, представляющий собой набор API и системных сервисов. С помощью него стало можно запрещать и разрешать соединения, управлять отдельными пакетами. Эти нововведения были предназначены для упрощения жизни разработчиков различных защит. Внесенные в сетевую архитектуру изменения затронули как kernel-mode, так и user-mode части системы. В первом случае необходимые функции экспортируются fwpkclnt.sys, во втором — fwpuclnt.dll (буквы "k" и "u" в названиях библиотек означают kernel и user соответственно). В этой статье мы расскажем о применении WFP для перехвата и фильтрации трафика, а после ознакомления с основными определениями и возможностями WFP мы напишем свой простой фильтр. Основные понятияПеред началом кодинга нам совершенно необходимо ознакомиться с терминологией Microsoft — и для понимания статьи будет полезно, и дополнительную литературу читать будет проще :). Итак, поехали. Классификация — процесс определения того, что нужно делать с пакетом. Из возможных действий: разрешить, блокировать или вызвать callout. Callouts — это набор функций в драйвере, которые проводят инспекцию пакетов. Они имеют специальную функцию, выполняющую классификацию пакетов. Эта функция может принять следующее решение:
Фильтры (Filters) — правила, указывающие, в каких случаях вызывается тот или иной callout. Один драйвер может иметь несколько callout’ов, а разработкой драйвера с callout’ом мы и займемся в этой статье. Кстати, колауты есть и встроенные, например, NAT-callout. Layer — это признак, по которому объединяются различные фильтры (или, как говорят в MSDN, "контейнер"). По правде говоря, документация от Microsoft, выглядит достаточно мутно, пока не заглянешь в примеры в WDK. Поэтому, если вдруг надумаешь разрабатывать что-то серьезное, нужно непременно с ними ознакомиться. Ну что ж, теперь плавно перейдем к практике. Для успешной компиляции и тестов тебе потребуется WDK (Windows Driver Kit), VmWare, виртуальная машина с установленной Вистой и отладчик WinDbg. Что касается WDK, то у меня лично установлена версия 7600.16385.0 — там есть все необходимые либы (поскольку мы будем разрабатывать драйвер, нам нужны только fwpkclnt.lib и ntoskrnl.lib) и примеры использования WFP. Ссылки на весь инструментарий уже неоднократно приводились, поэтому повторяться не будем. CodingДля инициализации callout’а я написал функцию BlInitialize. Общий алгоритм создания callout и добавления фильтра таков:
Обрати внимание, что функции оканчиваются на 0. В Windows 7 некоторые из этих функций были изменены, например, появилась FwpsCalloutRegister1 (при сохраненной FwpsCalloutRegister0). Отличаются они аргументами и, как следствие, прототипами классифицирующих функций, но для нас это сейчас неважно — 0-функции универсальны. FwpmEngineOpen0 и FwpmTransactionBegin0 не особо нам интересны — это подготовительный этап. Самое интересное начинается с функции FwpsCalloutRegister0: Прототип FwpsCalloutRegister0
Я уже говорил, что callout — это набор функций, теперь пришло время рассказать об этом подробнее. Структура FWPS_CALLOUT0 содержит указатели на три функции — классифицирующую (classifyFn) и две уведомляющие (о добавлении/удалении фильтра (notifyFn) и закрытии обрабатываемого потока (flowDeleteFn)). Первые две функции являются обязательными, последняя нужна только в случае, если ты хочешь мониторить сами пакеты, а не только соединения. Также в структуре содержится уникальный идентификатор, GUID колаута (calloutKey). Код регистрации callout
Далее нужно добавить объект-callout в систему и присоединить его к определенному уровню (layer) с помощью функции FwpmCalloutAdd0:
В структуре FWPM_CALLOUT0 нам интересно поле applicableLayer — уникальный идентификатор уровня, на который добавляется callout. В нашем случае это FWPM_LAYER_ALE_AUTH_CONNECT_V4. "v4" в названии идентификатора означает версию протокола Ipv4, есть также FWPM_LAYER_ALE_AUTH_CONNECT_V6 для Ipv6. Учитывая малую распространенность Ipv6 на настоящий момент, работать мы будем только с Ipv4. CONNECT в названии означает, что мы контролируем только установку соединения, о входящих и исходящих на этот адрес пакетах речи не идет! Вообще уровней, помимо использованного нами, много — они объявлены в заголовочном файле fwpmk.h из WDK. Добавление объекта-callout в систему
Итак, после того, как callout успешно добавлен в систему, нужно создать фильтр, то есть указать, в каких случаях будет вызываться наш callout, а именно — его классифицирующая функция. Новый фильтр создается функцией FwpmFilterAdd0, которой в качестве аргумента передается структура FWPM_FILTER0. В FWPM_FILTER0 есть одна или несколько структур FWPM_FILTER_CONDITION0 (их число определяется полем numFilterConditions). Поле layerKey заполняется GUID’ом уровня (layer), к которому мы хотим присоединиться. В данном случае указываем FWPM_LAYER_ALE_AUTH_CONNECT_V4. Теперь подробнее рассмотрим заполнение FWPM_FILTER_CONDITION0. Во-первых, в поле fieldKey нужно явно указать, что мы хотим контролировать — порт, адрес, приложение или что-то еще. В данном случае WPM_CONDITION_IP_REMOTE_ADDRESS указывает системе, что нас интересует IP-адрес. Значение fieldKey определяет, значения какого типа будут в структуре FWP_CONDITION_VALUE, входящей в FWPM_FILTER_CONDITION0. В данном случае в ней содержится ipv4-адрес. Идем дальше. Поле matchType определяет, каким образом будет производиться сравнение значения в FWP_CONDITION_VALUE с тем, что пришло по сети. Тут вариантов много: можно указать FWP_MATCH_EQUAL, что будет означать полное соответствие условию, а можно — FWP_MATCH_NOT_EQUAL, то есть, фактически, мы можем добавить таким образом исключение фильтрации (адрес, соединение с которым не отслеживается). Еще есть варианты FWP_MATCH_GREATER, FWP_MATCH_LESS и другие (см. энум FWP_MATCH_TYPE). В данном случае у нас стоит FWP_MATCH_EQUAL. Я не стал сильно заморачиваться и просто написал условие на блокирование одного выбранного IP-адреса. В случае, когда какое-то приложение попытается установить соединение с выбранным адресом, будет вызвана классифицирующая функция нашего callout’а. Код, обобщающий сказанное, ты можешь посмотреть на врезке "Добавление фильтра в систему".
Вообще, конечно, фильтрующих условий может быть много. Например, можно указать блокирование соединений с определенным удаленным или локальным портом (FWPM_CONDITION_IP_REMOTE_PORT и FWPM_CONDITION_IP_LOCAL_PORT соответственно). Можно вылавливать все пакеты определенного протокола или определенного приложения. И это еще не все! Можно, например, заблокировать трафик определенного пользователя. В общем, есть где разгуляться. Впрочем, вернемся к фильтру. Классифицирующая функция в нашем случае просто блокирует соединение с указанным адресом (BLOCKED_IP_ADDRESS), возвращая FWP_ACTION_BLOCK: Код нашей classify-функции
На практике функция классификации также может установить FWP_ACTION_PERMIT, FWP_ACTION_CONTINUE и др. И в заключение при выгрузке драйвера нужно удалить все установленные callout’ы (угадай, что будет, если система попытается вызвать callout выгруженного драйвера? Правильно, BSOD). Для этого существует функция FwpsCalloutUnregisterById. В качестве параметра ей передается 32-битный идентификатор callout’а, возвращенный функцией FwpsCalloutRegister. Завершение работы callout’а
Как видишь, программирование WFP-фильтра — не такая сложная задача, поскольку MS предоставили нам весьма удобный API. Кстати, в нашем случае мы устанавливали фильтр в драйвере, но это можно делать и из юзермода! Например, семпл из wdk msnmntr (монитор трафика MSN Messenger-а) так и поступает — это позволяет не перегружать kernel-mode часть фильтра. Свой GUIDДля регистрации callout ему нужен уникальный идентификатор. Для того, чтобы получить свой GUID (Globally Unique Identifier), используй guidgen.exe, входящий в Visual Studio. Лежит тулза в (VS_Path)\Common7\Tools. Вероятность коллизии очень мала, поскольку длина GUID составляет 128 бит, и всего доступно 2^128 идентификаторов. Отладка фильтраДля отладки дров удобно использовать связку Windbg+VmWare. Для этого нужно настроить как гостевую систему (в виде которой выступает Vista), так и отладчик WinDbg. Если у WinXP для удаленной отладки нужно было редактировать boot.ini, то для Vista+ есть консольная утилита bcdedit. Как обычно, нужно включить отладку:
Далее нужно настроить последовательный порт удаленной системы (см. соответствующую иллюстрацию).
Теперь все готово! Запускаем батник с нижеприведенным текстом:
и лицезреем отладочный вывод в окне windbg (см. картинку).
ЗаключениеКак видишь, область применения WFP довольно широка. Тебе решать, как применить эти знания — во зло или во благо :) |