Тушим огненные стены
Некоторые этюды борьбы с файрволами в ring 0Комплексные системы безопасности и файрволы душат тебя? Ограничивают
твою свободу и не дают программному обеспечению спокойно выходить в
интернет? Тебе плохо, тебя посещают мысли о самоубийстве? Поговорим об
этом? Да нет, не о самоубийстве. Лучше побеседуем о кодерских методах,
которые помогут нам потягаться с Outpost и NOD32. Методы перехвата сетевого трафикаНа практике в среде Windows используются пять методов перехвата сетевого трафика: 1-2) Фильтрация сетевого трафика в User-mode без
написания каких-либо драйверов. Известны и документированы такие
способы, как Winsock Layered Service Provider (LSP) и Windows 2000
Packet Filtering Interface. К преимуществам первого стоит отнести то,
что он позволяет отследить вызовы к библиотеке WinSock и может
использоваться, скажем, для шифрования сетевого трафика. Второй способ в Windows 2000 представляет собой механизм, позволяющий
приложению установить набор "filter descriptors”, на основе которого
TCP/IP выполняет фильтрацию пакетов. Оба метода, на мой взгляд,
малоэффективны, накладывают определенные ограничения и реальной защиты
не предоставляют. Поэтому лезем глубже... 3)Промежуточный драйверNDIS - NDIS IM. Он устанавливается между NDIS-драйвером и драйвером сетевой карты.
Microsoft предусмотрела этот класс драйверов как раз для нужд, подобных
нашим (ну, не совсем нашим ;)), однако их функциональность в
операционных системах Windows 98/ME/NT оставляет желать лучшего, а в
Windows 95 отсутствует вовсе. Он неудобен в установке, тем не менее,
встречаются файрволы, которые реализуют свою функциональность при помощи
NDIS IM. Желающие могут изучить NDIS IM FAQ и документацию в DDK. 4) Драйвер-фильтр, фильтрующий сетевые пакеты. По утверждению той же самой Microsoft, использовать его в качестве
фильтра не стоит. Всего лишь одна ловушка может быть установлена в
системе, причем устанавливается она на слишком большой высоте в сетевом
стеке. 5) NDIS-Hooking Filter драйвер, перехватывающий основные функции NDIS для отслеживания регистрации протоколов и открытия сетевых интерфейсов. Это наиболее надежный способ, поэтому именно его использует
подавляющее большинство файров. Конкретные методики перехвата достаточно
разнообразны, но сводятся, так или иначе, к патчу "родного" NDIS
драйвера в памяти, что несравненно проще реализации промежуточного NDIS
драйвера с нуля (под хуками здесь подразумевается как патчинг таблицы
экспорта, так и непосредственный патч кода ndis.sys). Реализация NDIS-Hooking позволяет создать подобие оболочки над самой
библиотекой NDIS и аналогична методу перехвата системных сервисов, о
котором не раз писал "][акер". Адреса необходимых функций библиотеки
NDIS заменяются «подставными» обработчиками, в результате чего можно
получить контроль над всеми сетевыми операциями в системе. Вывод
напрашивается сам собой: перехватив NdisRegisterProtocol()/
NdisDeregisterProtocol(), NdisOpenAdapter()/NdisCloseAdapter(), можно
отследить загрузку сетевого драйвера, затем установить адреса своих
обработчиков для точек входа отдельных процедур и контролировать все
сетевые операции ввода/вывода, проходящие через драйвер. Вот так и
действуют самые распространенные и популярные в народе файрволы! Ключевые структурыДля борьбы с перехватом на уровне ядра необходимо уметь обращаться с
несколькими ключевыми структурами библиотеки NDIS - NDIS_OPEN_BLOCK,
NDIS_PROTOCOL_BLOCK, переменной ndisProtocolList, ну и еще со структурой
NDIS_MINIPORT_BLOCK (о ней мы поговорим чуть позже).
Структура NDIS_PROTOCOL_BLOCK typedef struct _NDIS_PROTOCOL_BLOCK { PNDIS_OPEN_BLOCK OpenBlock; REFERENCE Reference; UINT Length; NDIS50_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics; struct _NDIS_PROTOCOL_BLOCK *Next; ULONG MaxPatternSize; } NDIS_PROTOCOL_BLOCK, *PNDIS_PROTOCOL_BLOCK; Эта структура не документирована и отличается в разных версиях
Windows. Поле Length содержит длину структуры NDIS_PROTOCOL_BLOCK.
ProtocolCharacteristics – это структура адресов обработчиков. Reference
содержит спинлоки для работы с OpenBlock, ну а сам OpenBlock – адреса
открытых обработчиков. Для того чтобы иметь возможность перехвата всех зарегистрированных
NDIS протоколов, нужно найти первый элемент в NdisProtocolList. Этот
список возвращается после вызова функции NdisRegisterProtocol() в виде
хендла NDIS_HANDLE. Он являет собой связный реестр NDIS-протоколов,
представляемых структурой NDIS_PROTOCOL_BLOCK. Они экспортируют свои
функции-обработчики, вызываемые при каких-то событиях, например, при
связывании адаптера и протокола, при принятии и удалении пакета и т.д. В случае регистрации/удаления протокола используется указатель на
первый протокол в списке (если не запущен снифер или другие программы,
работающие на уровне NDIS, то это будет протокол TCPIP_WANARP). В ядре существует переменная модуля NDIS.SYS – ndisProtocolList,
которая указывает на последний зарегистрированный протокол
(соответственно, первый в списке). Сейчас я тебя обрадую – эта
переменная не экспортируется. Указатель на последний зарегистрированный
протокол можно получить так: регистрируем пустой протокол, при
регистрации получаем указатель на следующий протокол в цепочке и сразу
его удаляем. Полученный после регистрации протокола NDIS_HANDLE будет
указателем на созданную нами структуру NDIS_PROTOCOL_BLOCK, которая, к
сожалению, отличается от одной версии NDIS к другой и не всегда
встречается в ndis.h. Структура, помимо прочего, содержит в себе
NDIS_PROTOCOL_CHARACTERISTICS с адресами всех ProtocolXXX функций и
список NDIS_OPEN_BLOCK, который, в свою очередь, содержит обработчики
Send/SendPackets/Request() всех сетевых интерфейсов, открытых данным
протоколом, и указатель на следующую структуру NDIS_PROTOCOL_BLOCK.
Двигаясь по списку зарегистрированных протоколов, переписываем
интересующие нас обработчики. Файры при установке хуков проверяют зарегистрированные в системе
протоколы, после чего путем патчинга NDIS_PROTOCOL_CHARACTERISTICS
подменяют адреса родных ndis’овских зарегистрированных функций на свои
собственные. При этом необходимо иметь в виду, что некоторые NDIS
макросы вызывают функции по указателям не из
NDIS_PROTOCOL_CHARACTERISTICS, а из NDIS_OPEN_BLOCK. Последний способ
применяется так – при вызове NdisOpenAdapter() устанавливается хук
указателя на код (PNDIS_OPEN_BLOCK)*NdisBindingHandle, что в дальнейшем
позволит перехватывать обработчики самых важных функций – SendHandler,
SendPaсketsHandler и др. Как уже было сказано, в структуре NDIS_OPEN_BLOCK (которая, повторю,
создается при вызове NdisOpenAdapter()) содержатся указатели на
обработчики конкретного сетевого адаптера, связанного с протоколом. С
каждым протоколом может быть связано несколько адаптеров, открытые блоки
которых объединяются в связный список. А указатель на первую структуру
NDIS_OPEN_BLOCK содержится в NDIS_PROTOCOL_BLOCK.OpenBlock. В ядре выживает тот, кто умнееТак как бороться с файрволами? Бороться с этим явлением сложно, хотя и
возможно ;). В основе обхода файрволов лежит тот факт, что в ядре
Windows все равны. А для реализации реального превосходства одной
программы над другой нужны повышенные привилегии – файр должен обладать
такими полномочиями, которые не может получить малварь. Впрочем, не стоит тешить себя напрасными надеждами. Разработчики
файрволов пряники едят не даром и немаленькую зарплату в зеленых
американских рублях получают заслуженно. Ну, или почти заслуженно. Такие
избитые вещи, как инжект кода в доверенный процесс или манипуляции с
выгрузкой драйвера файра (или еще хлеще – убийство процесса файра)
рассматривать не будем. Себя надо уважать. И первое, и второе есть всего
лишь обман файра и свидетельство некомпетентности разработчика (тем
более, что все современные софтины это дело палят – прим. ред.). Мы
будем сражаться честно, лицом к лицу и с открытым забралом. Приступим-с...На данный момент мне известны следующие способы обхода файрволов в ядре (претендовать на исключительность не буду): 1) Первое, что должно прийти на ум – восстановить
таблицу экспорта ndis.sys, тем самым убрав установленные хуки с главных
функций NdisRegisterProtocol()/ NdisDeregisterProtocol(),
NdisOpenAdapter()/NdisCloseAdapter() и вызвав эти функции для
регистрации нового "тайного" протокола. Протокол, конечно же, будет
существовать в переменной ndisProtocolList и NDIS_PROTOCOL_BLOCK, но
файр пропустит его регистрацию и привязку сетевого адаптера. Данная
техника сильна, но ненадежна, поскольку хорошо реализованные файры
следят за целостностью установленных хуков и восстанавливают их через
определенную единицу времени. Далее при помощи техники DKOM – Direct
kernel object manipulation («][акер» уже писал об этом) – следует убрать
из NDIS_PROTOCOL_BLOCK зарегистрированный нами протокол, ведь по сути
NDIS_PROTOCOL_BLOCK представляет собой простой линкованный список. Как
его найти – описано выше. 2) Как некий подвид вышеприведенного метода можно
нафантазировать и такое: путем патчинга функций NdisRegisterProtocol() и
NdisOpenAdapter() осуществить замену всех ссылок и указателей на
переменную ndisProtocolList референсами – на некую поддельную
переменную, которую мы сами и создадим. Трудность в том, что затем
придется ручками перехватывать вызов функций ndis.sys, использующих эту
переменную: NdisRegisterProtocol() и NdisDeregisterProtocol() NdisReferneceProtocolByName() NdisDereferenceProtocol() NdisPnPDispatch() NdisCheckAdapterBindings() 3) При вызове NdisOpenAdapter() происходит
обновление базы данных фильтров минипорта, которые определяются
соответствующими структурами в W2K\WinXp – ETH_FILTER\X_FILTER.
Дальнейший вызов неэкспортируемых функций XNoteFilterOpenAdapter() и
EthNoteFilterAdapter() присоединяет к базе данных фильтров минипорта
структуры ETH_BINDING_INFO/X_BINDING_INFO, а уже в них (ты не поверишь!)
хранится указатель на список NDIS_OPEN_BLOCK, который хранит
обработчики функций для текущего минипорта (смотри описание ниже). Недокументированная структура ETH_FILTER (она же X_FILTER)typedef struct _ETH_FILTER { PNDIS_SPIN_LOCK Lock; CHAR (*MulticastAddresses)[ETH_LENGTH_OF_ADDRESS]; ETH_MASK *BindingsUsingAddress; UINT CombinedPacketFilter; ETH_BINDING_INFO *OpenList; (<----!!!) ETH_ADDRESS_CHANGE AddressChangeAction; ETH_FILTER_CHANGE FilterChangeAction; ETH_DEFERRED_CLOSE CloseAction; UINT MaximumMulticastAddresses; UINT NumberOfAddresses; ULONG FreeBindingMask; UCHAR AdapterAddress[ETH_LENGTH_OF_ADDRESS]; CHAR (*OldMulticastAddresses)[ETH_LENGTH_OF_ADDRESS]; ETH_MASK *OldBindingsUsingAddress; UINT OldNumberOfAddresses; UINT OldCombinedPacketFilter; PETH_BINDING_INFO DirectedList; PETH_BINDING_INFO BMList; struct _NDIS_MINIPORT_BLOCK *Miniport; } ETH_FILTER,*PETH_FILTER; Недокументированная структура ETH_BINDING_INFO typedef struct _ETH_BINDING_INFO { NDIS_HANDLE MacBindingHandle; NDIS_HANDLE NdisBindingContext; UINT PacketFilters; ULONG References; BOOLEAN ReceivedAPacket; UCHAR FilterIndex; struct _ETH_BINDING_INFO *NextOpen; UINT OldPacketFilters; struct _ETH_BINDING_INFO *NextDirected; struct _ETH_BINDING_INFO *NextBM; } ETH_BINDING_INFO,*PETH_BINDING_INFO; Структуры, которые ты можешь увидеть на соответствующих блок-врезках,
в Windows изменяются от билда к билду, поэтому стопроцентно полагаться
на мое описание я бы не советовал (оно, кстати, подходит к win2k). 4) Как известно, при обработке сетевого пакета
менеджеры пакетов используют указатель ETH_FILTER.OpenList для получения
всех открытых обработчиков, привязанных к данному сетевому
адаптеру/минипорту. А что нам мешает перехватить ETH_FILTER.OpenList
путем создания собственной структуры ETH_BINDING_INFO, где и подменить
указателя структуры OpenList на свой собственный? Подумай над этим на
досуге... Метод супер! Его сложно выявить, поскольку все, что нужно
сделать – это перезаписать один указатель на подставную структуру
ETH_BINDING_INFO – и дело почти в шляпе. Сложность реализации в том, что
функции, которые регистрируют и ассоциируют структуры ETH_BINDING_INFO с
конкретным минипортом XNoteFilterOpenAdapter() и
EthNoteFilterAdapter(), не документированы и не экспортируются. Выход,
который можно использовать – поиск этих функций по сигнатуре в памяти и
патч чем-нибудь в духе JMP/CALL на свой собственный обработчик. 5) Наконец, способ для любителей решать проблемы
через анальное отверстие: загрузить ndis.sys при помощи своего
собственного PE-лоадера, промаппить секции загруженного имиджа ndis.sys в
соответствующие виртуальные адреса в неподкачиваемой памяти (при этом
все абсолютные адреса должны быть переадресованы на уже существующий
имидж ndis.sys в памяти), после чего перехватывать и обрабатывать все
обращения к NDIS-библиотеке. При явных выгодах у метода есть и очевидные
недостатки – простейший сканер памяти может легко обнаружить копию
ndis.sys, да и потом дополнительно потребуется писать свой собственный
PE-лоадер. Сначала удостоверься, что Disk I/O не хучится антивирусами и
файрами. С особой осторожностью нужно использовать такие функции, как
ZwCreateSection и MmCreateSection. Преимущества этого кодерского хинта
видны невооруженным глазом: переносимость, 100% работающий код и
скорость исполнения, но уж больно это метод шумный и палевный. На мой
взгляд, в природе практически не встречается. В принципе, для обеспечения надежного контроля за сетевой активностью
можно перехватывать все подмножество функций NDIS библиотеки, другой
вопрос – а оно вам надо? Для достижения своих грязных целей достаточно
перехватить четыре функции: NdisRegisterProtocol(),
NdisDeregisterProtocol(), NdisOpenAdapter() и NdisCloseAdapter() – это
может реально отразить картину сетевой активности в ядре. А что же с кодингом?Сразу оговорюсь, что сорца драйвера, который будет ставить все
файрволы мира в недвусмысленную позицию (я имею в виду позу гордого
льва) здесь ты не найдешь. Должен же быть какой-то простор для фантазии
;). Просто рассмотрим, как в ядре устанавливаются и снимаются хуки на
основные функции ndis.sys. Для этого заранее подготовим обработчик
функции, которую собираемся похучить, найдем ndis.sys в ядре и пропарсим
таблицу экспорта на предмет адреса искомой функции, после чего
перезапишем их. И все! Весьма основательно откомментированный исходник
данного действа ожидает тебя на диске, в статью он не попал, так как
журнальное пространство не предполагает публикации больших сорцов. ЗаключениеЧестно говоря, в природе, кроме хуков функций, из вышенакуренного
мало что встречается. Разработчики файров не стоят на месте и
вышеописанными способами дело далеко не ограничивается. Но об этом
как-нибудь в другой раз... Да пребудет с тобой Сила! DVDНа диске ты можешь найти исходники и откопмилированный вариант
драйвера, который ставит хуки на некоторые NDIS функции путем патча
таблицы экспорта. Для его компиляции тебе понадобится Driver Development
Kit. WWWЕсли хочешь разобраться с темой статьи подробнее, обязательны к посещению wasm.ru, rootkit.com, pcausa.com и winpcap.org. Наш спонсор:
|