Снифферы, NDIS, RAW Sockets и проч.
Прочитал тут вашу статью о снифферах (СР.10 декабрь 2000). Цитирую:
"......
За бортом остались по крайней мере две большие темы: внутреннее устройство снифферов, включая методы и подходы к написанию подобного софта....
....."
:P
Оч-ч-чень интересно!!!
Не первый месяц бьюсь над вопросом как в win9x можно контролировать TCP/IP трафик.
Имеется в виду какими средствами с точки зрения програмиста. Сокеты, сами понимаете, совсем не то.
Я вас очень прошу, скажите КАК? В какую сторону хотя-бы копать? Где в Сети можно найти какую-нибудь документацию, исходы и т.д. А статья в СР была-бы просто манной небесной...
Кстати, мне кажется, что многих интересует применение всего того что вы пишите с точки зрения Dual-Up. Как вообще идут пакеты в данном случае?
Всего доброго!
Доброго времени суток, не-пожелавший-себя-назвать!
Спасибо за трогательные слова в адрес моей статьи. К сожалению в течение прошедшего месяца я так и не сподвигнулась засесть за написание обещанной второй части "Снифферов...", в которой должны были проясняться моменты, указанные вами в письме. Я решила поступить другим образом: попросить ответить на ваш вопрос моих коллег по группе Necrosoft.
Первый ответ принадлежит перу нашего СЕО и ведущего разработчика, лидера проекта NScan — Averk aka HellMaster. Не пугайтесь, что начало его рассказа немного отдает "оффтопиком" и экскурсом в историю — во-первых это тоже небезынтересно, однако затем последует, на мой взгляд, практически исчерпывающий ответ на ваш вопрос.
Окей, listen, hon.:)
Я как-то сам разбирался с архитектурой сетевых драйверов в Windows 95/98/NT. Интересовала меня возможность сборки IP-пакетов со своими заголовками. Первое, на что я тогда обратил внимание, был тип сокетов SOCK_RAW. Любопытно, подумал я — это же то же самое, что есть и в *nix, только там такой сокет можно было открыть только будучи superuser-ом. Первое, что я решил тогда сделать — портировать какой-то эксплойт из *nix, который использовал такой сокет (это было еще когда только-только начали распространять патч к winsock против winnuke). Интересно было, почему это не было сделано под windows. Откопал я тогда спецификацию Winsock 2 и посмотрел что к чему. Все выглядело вполне радужно, документация ободряла и текст будущей программы уже огненными буквами горел перед моим мысленным взором. Требовалось всего-навсего использовать тип сокета SOCK_RAW для адресного пространства AF_INET и протокола IPPROTO_IP и установить для этого сокета опцию IP_HDRINCL (чтобы включать свой заголовок в пакет). Вызвал socket (AF_INET, SOCK_RAW, IPPROTO_IP). Сокет открылся. "Вау!": подумал я — "Работает!". Хорошо, осталось использовать setsockopt. IP_HDRINCL в стандартный набор заголовков не входил, но разве этим кого-то испугаешь? Найдя в ws2tcpip.h, что он равен 2, подставляю. Не работает, несмотря на все увещевания документации. Что характерно, не работает оно и с другими протоколами. Ну, думаю, черт с ним, попробую так. И вызвал sendto. Success! Что-то здесь нечисто, подумал я и полюбовался на свой трафик. Мамочки, да где ж это видано, чтобы протокол был равен 0? Способа это изменить я не нашел и поковырявшись в винсоке еще немного и найдя в сети комментарии по поводу этого парадокса, дело это безнадежное я забросил.
Итак, вот что сказано в спецификации про SOCK_RAW:
1. Если винсок поддерживает SOCK_RAW, то он обязан поддерживать IP_HDRINCL и при этом должен брать заголовок IP из буфера данных. Черта с два.
2. Пакет конструируется из данных буфера. По идее должно означать, что UDP, например, пакет должен без изменений накладываться поверх IP. Щяс, как же. Работает точно так же, как и просто открытый сокет — если умеет, пытается считать контрольную сумму, вставляет адреса портов источника и назначения, а буфер пересылает как данные. Справедливо для UDP. С TCP sendto возвращает код успешного завершения, но ничего не делает. ICMP работает (там портов нет, поэтому добавить от себя винсоку нечего). Бардак, в общем. Кстати, есть в SDK сэмпл с пингом, сделанным при помощи SOCK_RAW.
3. Приложение всегда получает заголовок IP вне зависимости от IP_HDRINCL. Угу. Хоть тут не обманули.
Еще меня позабавило то, что в документации написано, что такой сокет может получить много лишних пакетов. Например для ICMP могут прийти сообщения типа "host unreachable". Еще там написано, что если много приложений открывают одинаковые сокеты, им могут прийти те же данные, что и "соседям". Как я убедился на практике, ключевое слово здесь "могут". Могут прийти, а могут и не прийти — каково? Особенно четко это чувствуется при включенном фильтре AtGuard — в таком случае иногда ICMP пакеты приходят, но чаще они достаются только внутренним механизмам winsock. Тем не менее, это глюк редкий и, как, правило, проявляющийся со специализированным софтом, так что такие сокеты запросто можно использовать для мониторинга, например, ICMP трафика.
В общем, смотрится грустно, но такое у Микрософта иногда случается — сначала пишут документацию к софту, а потом сам софт. Ситуация эта характерна для NT4, 95 и 98, в Win2000 работа винсока уже значительно ближе к спецификации.
Итак, поставив крест на windows sockets, полез я в документацию поглубже, и, убедившись в том, что фильтрация сетевого трафика возможна только при помощи виртуального драйвера (VXD), скачал WIN95 DDK (device development kit). DDK с ходу поразил своей крайней лаконичностью (а сами они такой документацией пользоваться пытались?) — все, в сущности, сводилось к тому, что есть, дескать, такая штука сетевая — NDIS, с которой можно горы свернуть. Пара рисуночков, пара слов о том, как это все круто работает и... все. Только ссылка на NTDDK и мутный сэмпл пакетного драйвера. Придя в экстаз от изобилия данных, скачал я 45 мегабайт DDK под NT (по тем временам — или героизм или сумасшествие). С трепетом поставив DDK, я обнаружил полный список функций NDIS, еще один, практически идентичный, сэмпл и пару слов о том, как оно все работает. Кстати, потом вышел MSVC 6.0 и с ним MSDN, содержащий полный help для обеих версий DDK ().
Копаться пришлось очень долго, документация выглядела так, как будто ее авторы считали там почти все либо само собой разумеющимся, либо сами не имели представления о том, что они пишут и боялись опозориться. В конце концов, многое из этого удалось уяснить, покопавшись, в частности, в тех самых исходниках драйверов.
Итак. Архитектура взаимодействия сетевых драйверов windows, как она была мною понята (см. рисунок 1):
В самом низу иерархии стоит NIC (Network Interface Card) — драйвер, отвечающий за работу с устройством. Выше находится NDIS Intermediate (промежуточный) драйвер, а над ним уже транспортный драйвер, представляющий интерфейс TDI. С точки зрения фильтрации трафика самая интересная вещь тут — NDIS IM. Эта часть выглядит для NIC как драйвер протокола и наоборот. Такие драйверы могут собираться в цепочки и работать с трафиком, например, перепаковывать его в другой формат, сжимать, шифровать и т.п.. Подобный драйвер, например, стоит в небезызвестном NetXRay.
Обычно этого хватает, чтобы построить драйвер для общего контроля трафика, однако NDIS IM не контролирует работу сети на уровне процессов, то есть, вкратце, узнать, что за сволочь время от времени сливает куда-то в сеть пароли из registry, не удастся:).
Насколько это понял я, есть, как минимум, два способа это узнать: первый, не самый изящный — подменить winsock. Так иногда делают, но это может иногда приводить к недобрым последствиям. Еще один, похоже, работа с TDI на уровне ядра в виде клиента (логическая структура TDI представлена на рисунке 2)
Во втором случае я могу быть и не прав, так как сам я это не проверял, хотя почитать документацию стоит, хотя бы для общего развития. Есть также еще одна интересная штука, которую можно как-нибудь применить — библиотеки расширения winsock — структура таких надстроек в NT показана на рисунке 3.
Стоит еще обратить внимание на winsock 2 SPI — набор функций WSPxxx, WSCxxx, NSPxxx и WPUxxx из platform SDK. Это набор функций для построения "слоистого" набора транспортных провайдеров (см. рисунок 4).
Теперь перейдем к практическим советам. Построение драйвера можно начать с сэмплов DDK. В NT и 95/98 они практически идентичны. Различия, в основном, в процедуре инициализации, так что большая часть кода будет переносимой (в NT, кстати, все немного проще, чем в 95/98). В 98 DDK есть очень неплохой набор, позволяющий хоть немного понять как это работает — например, с небольшими модификациями сэмпл может перехватывать весь трафик сетевого устройства. Дальше уже можно наворачивать все, что угодно. Если же сделать что-то очень хочется, но самому все писать лень, можно воспользоваться драйверами WinPCap. Это аналог libcap для windows, полностью функционально идентичный оному из Linux (оттуда и взят). Работает при помощи фильтров BPF, которые представляют собой нечто вроде набора байт-кодов для программы просмотра содержимого пакета, основанных на командах RISC-процессоров. Вещь достаточно простая, гибкая и быстрая. Основной принцип — интерпретация такой функции и возврат 0 или 1 — пропустить или захватить пакет. Сам драйвер, в общем, неплохой, хоть и немного кривенький, работает с ограничениями. Кстати, по адресу http://netgroup-serv.polito.it/netgroup/tools.html есть его исходники, а также еще пара интересных вещей.
Серьезный SDK также предлагает компания PCAUSA — http://www.pcausa. com. Там есть впечатляющая документация по NDIS, NDIS IM и TDI, сэмплы драйверов и сами полнофункциональные драйверы, правда, по не менее впечатляющим ценам, хотя есть там и кое-что задаром:)
А вот более отвлеченное от вопроса, содержавшегося в письме, но не менее практически значимое дополнение от другого коллеги по Necrosoft — Ghost, который предпочитает работать и творить исключительно под *nix-системами. Впрочем, его ответ также предлагает и один из вариантов решения проблемы под Windows.
Если вы желаете отслеживать сетевой трафик в различных *nix-системах, то в данном случае вам поможет libcap. Libcap — это системно-независимый интерфейс прикладного уровня для захвата пакетов, который обеспечивает мобильную базу для низкоуровневого мониторинга сети. Данный интерфейс включает в себя: сбор сетевой статистики, фильтрацию пакетов, сетевую отладку, и т. д. Так как каждый производитель ОС предоставляет возможность захвата пакетов, были написаны различные утилиты, использующие данные возможности. И для этого было разработано системно-независимое API, что бы в последствии можно было легко портировать приложения и не задумываться о использовании каких либо системно-зависимых функций в приложении.
Это довольно новый интерфейс, и со временем может обновляться и дополняться.
Libcap поддерживает механизм фильтрации, базирующийся на архитектуре фильтра пакетов, реализованного в BSD — BSD Packet Filter (BPF). Впервые BPF был описан в 1993 году в газете "Winter Usenix" — "The BSD Packet Filter: A New Architecture for User-level Packet Capture", саму статью в postscript формате вы можете взять по адресу: ftp://ftp.ee.lbl.gov/ papers/bpf-usenix93.ps.Z.
Хотя некоторые интерфейсы захвата пакетов поддерживают фильтрацию пакетов внутри ядра, libcap использует фильтрацию только для BPF интерфейса. На системах, не имеющих BPF, все пакеты перемещаются в специальный буфер и обрабатываются BPF-фильтром.
BPF — стандарт для 4.4BSD, BSD/386, NetBSD и FreeBSD. В DEC OSF/1 используется интерфейс фильтрации пакетов, который реализует расширенный доступ к BPF-фильтру (который использует libcap). Так же вы можете встроить в свою Ultrix-систему BPF-фильтр при помощи патчей, доступных по адресу:ftp://gatekeeper.dec.com/pub/DEC/net/bpfext42.tar.Z.
Что же касается Linux, то в Linux используется Berkley sockets — ни что иное, как стек TCP/IP от BSD. Так что проблем с использованием libcap не возникает.
Для Win 9x/WinNT/Win2000 специально был написан драйвер захвата пакетов. Данный драйвер добавляется в систему и позволяет захватывать и посылать raw-пакеты, по своим функциональным возможностям очень похож на BPF для *nix систем. Packet.dll — API, который используется для прямого доступа к функциям BPF-драйвера. WinPcap экспортирует набор примитивов, которые совместимы с libpcap, знаменитой библиотекой для *NIX систем. На самом деле WinPcap — ни что иное как порт libcap, который предлагает набор функций высокого уровня для захвата пакетов. Данный порт вместе с драйверами можно приобрести по адресу:
Исходники WinPcap —http://netgroup-serv.polito.it/winpcap/install/bin/WPcapSrc.zip
Developers pack —http://netgroup-serv.polito.it/winpcap/install/bin/WPdpack.zip
В исходниках WinPcap так же присутствуют исходники драйверов для Win 9x/WinNT/Win2000.
Сетевые решения. Статья была опубликована в номере 02 за 2001 год в рубрике технологии