Rambler's Top100

Архитектура захвата пакетов для Windows WinPCAP: бальзам на душу хакера или панацея для программиста?

 

Алексей Волков, Вячеслав Семенов

 

Среди хакеров и системных администраторов существует общепринятое мнение, что операционная система Windows для работы в сети никуда не годится: она  плохо защищена, да и серьезных приложений для работы в сети для нее не напишешь. Вот Юникс – это другое дело. Действительно, до недавних пор в Windows крайне сложно было сформировать пакет с поддельным IP-адресом и отправить его жертве; так же сложно было создать хороший пакетный фильтр и не бояться, что его «обойдут» злодеи на FreeBSD. Архитектура, о которой пойдет речь в данной статье, избавляет эти категории людей от всех бед, которые ранее существовали, и позволяет взглянуть на возможности Windows с другой точки зрения.

Архитектура BPF и UNIX

BPF, или Berkley Packet Filter (пакетный фильтр Беркли), описанный Стивеном МакКейном и Ван Якобсоном (McCane and Jacobson 1993) – это архитектура ядра для захвата пакетов, созданная для работы с ядрами ОС UNIX.

BPF представляет собой драйвер устройства, который может использоваться приложениями ОС UNIX для передачи и приема пакетов, передаваемых по сети, напрямую через сетевой интерфейс (сетевой адаптер, модем) высокоэффективным методом. С другой стороны, BPF – не совсем обычный драйвер, поскольку он не управляет сетевым адаптером, а взаимодействует с его драйвером.

BPF состоит из двух основных элементов: сетевая ловушка (network tap) и пакетный фильтр (packet filter). Сетевая ловушка – это петлевая функция, являющаяся частью кода BPF, однако не вызываемая самим BPF. Ее вызывает драйвер сетевого адаптера, когда принимает очередной входящий пакет. Драйвер адаптера должен вызывать сетевую ловушку для каждого поступившего пакета, в противном случае BPF не будет нормально работать с этим адаптером. Сетевая ловушка копирует поступившие пакеты и передает их копии приложению верхнего уровня, каковым для нее является пакетный фильтр.

Пакетный фильтр – это заданная пользователем функция с бинарным результатом, устанавливаемым относительно входящего пакета. Если значение функции TRUE, ядро передает копию входящего пакета приложению верхнего уровня , если FALSE – входящий пакет игнорируется. Практически все приложения, использующие BPF, подавляют большинство входящих пакетов, поэтому производительность фильтра определяет производительность приложения.

Пакетный фильтр BPF определяет не только необходимость приема пакета, но и количество байт входящего пакета, необходимых приложению (например, приложение может обрабатывать только заголовки пакета, если в данных нет необходимости). Это существенно ускоряет процесс фильтрации и значительно снижает потери, поскольку для сохранения меньшего объема данных меньше места задействуется в буфере драйвера.

На рис.1 показано взаимодействие BPF с операционной системой.

Рисунок 1: взаимодействие BPF с операционной системой

BPF назначает фильтр и два буфера на каждый процесс, запросивший его функции. Фильтр создается приложением и передается в BPF путем вызова функций IOCTL.  Оба буфера постоянно размещены в BPF, и их размер составляет обычно 4 кБ. Первый буфер (store buffer – буфер сохранения) используется для приема данных от адаптера, второй (hold buffer – буфер задержки) используется для копирования пакетов для приложений. Если буфер сохранения заполнен, а буфер задержки пуст, BPF меняет их местами.

Когда очередной пакет поступает на вход сетевого интерфейса, драйвер устройства канального уровня в обычном режиме  передает его системному стеку протоколов. Однако если BPF «прослушивает» сетевой интерфейс, драйвер сначала вызывает функцию сетевой ловушки.

Ловушка передает входящий пакет каждому из созданных фильтров. Заданный приложением фильтр определяет необходимость приема пакета и количество байт сохраняемого пакета. Заметим, что фильтр воздействует на пакеты, пока они находятся в памяти драйвера канального уровня до его копирования.

Если фильтр принимает пакет, ловушка копирует заданное фильтром число байт из памяти драйвера в буфер сохранения, связанный с этим фильтром. После этого драйвер сетевого интерфейса вновь получает управление и происходит нормальная обработка пакета в стеке протоколов операционной системы.

Входящий пакет игнорируется фильтром также в том случае, если буфер сохранения заполнен, а буфер задержки недоступен (т.е. приложение считывает из него данные).

Приложение взаимодействует с драйвером BPF при помощи библиотеки захвата пакетов, называемой libpcap (packet capture library). Для получения принятых пакетов из буфера задержки приложение вызывает системную функцию считывания. Если буфер задержки заполнен (или истекло время ожидания), BPF копирует содержимое буфера в память приложения и передает ему управление. Приложение способно принять более одного пакета за один раз. Для выделения границ блоков принятых данных BPF снабжает их заголовком, содержащим время приема и размер блока, а также концевиком для синхронизации блоков данных.

Архитектура PCAP: там, где нас нет

Заметим, что не все версии UNIX имеют в своем составе BPF (т.е. возможности фильтрации и буферизации в ядре операционной системы), однако архитектура PCAP (Packet Capture – захват пакетов) может компенсировать этот недостаток. Данная архитектура полностью совместима с архитектурой BPF и позволяет фильтровать пакеты аналогично BPF, но на уровне пользователя.

Программная реализация архитектуры PCAP была выполнена в виде NDIS-драйвера. NDIS (Network Driver Interface Specification – спецификация интерфейса сетевого драйвера) – это набор спецификаций, описывающих организацию обмена данными сетевым адаптером (точнее, драйвером, управляющим им) и драйверами протоколов (IP, IPX и т.д.). Основное назначение NDIS состоит в том, чтобы предоставить возможность драйверу протоколов принимать и передавать пакеты по сети (локальной или глобальной) не зависимо от того, какая версия ОС используется.

NDIS-драйвер имеeт некоторые отличия от BPF. Во-первых, процесс фильтрации проходит на уровне пользователя, поэтому каждый входящий пакет должен быть скопирован из ядра в буфер приложения до его фильтрации. При этом происходит потеря процессорного времени и оперативной памяти, поскольку в память приложения копируются все пакеты, в том числе те, которые не нужны данному приложению.

Во-вторых, в ядре ОС отсутствует буферизация пакетов. В многозадачных операционных системах приложение вынуждено делить процессорное время с другими программами. Возможна ситуация, когда приложение не будет активно в момент прихода очередного пакета. Кроме того, приложение может быть занято выполнением других задач и не ждать входящий пакет. При отсутствии буфера в ядре ОС возникновение этих ситуаций приведет к потере пакета.

Существование этих недостатков привело к тому, что фильтрацию и буферизацию задает и выполняет драйвер захвата пакетов (packet capture driver), а не приложение. Заметим, что управление драйвером осуществляется при помощи все той же библиотеки libpcap, а это обеспечивает программную совместимость двух архитектур.

PCAP и WinPCAP: сходства и отличия

Архитектура PCAP изначально разрабатывалась для операционных систем с открытым исходным кодом. Закрытые коммерческие операционные системы остались без внимания разработчиков. Этот факт явился одной из причин, из-за которых практически все более-менее серьезные сетевые приложения написаны для UNIX, Linux, BSD и т.д. Windows в этом плане заметно отставала.

Однако программирование приложений на языках высокого уровня под Windows – процесс куда более приятный, чем под той же Linux. Удобство отладки, наличие удобной системы помощи, быстрая разработка приложений и визуальных интерфейсов и масса других положительных качеств средств разработки приложений (Delphi, C++ Builder, Visual Studio) и отсутствие возможности низкоуровневого управления сетевым интерфейсом привели к возникновению необходимости создания архитектуры, аналогичной PCAP, для семейства ОС Windows. Естественно, разработчики архитектуры WinPCAP не стали изобретать велосипед, а адаптировали существующую архитектуру PCAP для ОС Windows.

Процесс переноса PCAP на Windows заключался в адаптации pcap-драйвера и библиотеки libpcap для работы под Win32. Оригинальная версия libpcap написана на языке C с учетом возможности переноса библиотеки на различные версии UNIX. ОС Windows не поддерживает всех вызовов POSIX-систем, однако предоставляет некоторые аналогичные им функции через API. Модели памяти UNIX и Windows одинаковы (Windows и большинство UNIX являются 32-х битными ОС) и имеют аналогичный размер целых чисел.

Версия PCAP для Win32 основана на драйвере захвата пакетов, структура и принцип действия которого аналогичен его предшественнику для UNIX. Это значительно облегчает процесс переноса приложений с одной ОС на другую. Библиотеки и функции ОС UNIX, отсутствующие в Windows (например, getnetbyname) и необходимые для компиляции libpcap на Windows-машине, разработчикам пришлось включить в исходный код драйвера (директория Win32-Include дистрибутива).

Часть исходного кода, отвечающая за взаимодействие с сетевым адаптером, была изменена для поддержки его NDIS-драйвера. В соответствии с обозначениями, принятыми в оригинальной версии libpcap, исходный код для взаимодействия с драйвером находится в наборе файлов pcap-XXX.c (и соответствующий ему pcap-XXX.h), где XXX – указывает на операционную систему (например, pcap-linux.c). К уже существующим файлам были добавлены pcap-win32.c и pcap-win32.h.

Основному изменению подвергся принцип взаимодействия приложения пользователя с драйвером захвата пакетов. Libpcap для Win32 взаимодействует с аппаратным обеспечением через интерфейс, предоставляемый динамической библиотекой packet.dll (в отличие от Windows, в ОС UNIX сетевой адаптер или модем «виден» как стандартный файл, поэтому нет необходимости в использовании промежуточных библиотек – достаточно просто создать пакет необходимой структуры и записать его в этот файл). Это не влияет на нормальную работу libpcap, однако может создать определенные проблемы программисту, желающему получить доступ непосредственно к драйверу захвата пакетов. Например, в ОС UNIX возможно использовать системный вызов SELECT для того, чтобы узнать, поступил ли пакет на вход адаптера. В ОС Windows такая возможность отсутствует.

Программист может использовать функции libpcap для  обеспечения работоспособности исходного кода приложения на различных операционных системах, но при этом возможности приложения будут ограничены (например, libpcap не позволяет отправлять пакеты через сетевой интерфейс). Если программист решит воспользоваться функциями packet.dll, то его приложение будет работать только под управлением ОС семейства Win32, однако при этом возможности приложения будут практически неограниченными.

Для обеспечения максимальной совместимости исходного кода libpcap различных ОС часть кода, предназначенная для ОС Windows, отделена от остального кода директивами #ifdef и #ifndef. Например:

#ifdef WIN32

/* исходный код для Windows */

#endif

Это позволяет компилировать исходный код libpcap как на ОС Windows, так и на UNIX.

Основная концепция архитектуры WinPCAP

Архитектура WinPCAP дополняет стандартные функции операционных систем семейства Win32 возможностью принимать и передавать данные по сети, минуя стек протоколов операционной системы и взаимодействуя непосредственно с сетевым адаптером компьютера. Более того, она предоставляет приложениям API высокого уровня для управления низкоуровневыми процессами. WinPCAP состоит из трех компонентов: драйвер устройства захвата пакетов (paсket.vxd), низкоуровневая динамическая библиотека (packet.dll) и статическая библиотека высокого уровня (libpcap).

Данная архитектура может быть использована как для создания программ обработки пакетов для ОС Windows, так и для переноса аналогичных программ, написанных для ОС UNIX, и полностью совместима с архитектурой BPF – libpcap.

Структура стека захвата пакетов

Для перехвата пакетов, передаваемых по сети, приложению необходимо взаимодействовать непосредственно с сетевым оборудованием. По этой причине операционная система должна предоставлять несколько примитивных функций для приема и передачи данных непосредственно через сетевой адаптер. Назначение этих функций состоит в том, чтобы принять входящий пакет и передать его в стек протоколов операционной системы для дальнейшей обработки. Приложение получает пакет без заголовков канального, сетевого и транспортного уровней, интерпретирует и обрабатывает его и предоставляет в удобном для пользователя виде.

На рис. 2 приведена структура стека захвата пакетов от сетевого адаптера до приложения верхнего уровня.

Рисунок 2:структура стека захвата пакетов

На нижнем уровне находится сетевой адаптер, принимающий все пакеты, передаваемые по сети. Драйвер захвата пакетов (pcap-драйвер) packet.vxd является программным модулем низкого уровня. Он работает на уровне ядра ОС и взаимодействует непосредственно с драйвером сетевого адаптера. Pcap-драйвер предоставляет набор функций низкого уровня, обеспечивающих прием и передачу данных на канальном уровне через NDIS, который является частью сетевой подсистемы Win32. NDIS отвечает за управление различными типами адаптеров и обеспечивает связь адаптера с программным обеспечением, отвечающим за формирование пакетов различной структуры.

Динамическая библиотека packet.dll «изолирует» программу пользователя от драйвера и предоставляет приложению независимый от вида ОС (семейства Win32) интерфейс. Это позволяет приложению работать на различных Windows-платформах без перекомпиляции. Библиотека packet.dll работает на уровне пользователя, но отдельно от приложения.

Статическая библиотека libpcap используется частью программы пользователя, обеспечивающей перехват и фильтрацию пакетов. Она задействует функции, предоставляемые библиотекой packet.dll, и обеспечивает программе пользователя управление процессами приема и фильтрации данных на высоком уровне. Библиотека libpcap статически связана с программой пользователя и является ее частью.

Программа пользователя – высший уровень структуры стека захвата пакетов. Она обеспечивает обработку принятых пакетов и отображение результатов в удобном для пользователя виде.

Взаимодействие с NDIS

NDIS поддерживает три типа сетевых драйверов:

1.     Драйверы сетевых карт (NIC – Network Interface Card) – низший уровень сетевой подсистемы.

2.     Промежуточные драйверы – обеспечивают взаимодействие драйверов верхнего уровня (например, драйверов протоколов) с драйверами NIC.

3.     Драйверы транспортного уровня (драйверы протоколов) – обеспечивают прием и передачу данных приложения верхнего уровня, используя стек стандартных сетевых протоколов (например, TCP/IP).

Общий вид структуры NDIS с двумя стеками захвата пакетов, привязанных к одному сетевому адаптеру, представлен на рис. 3. Один из них состоит из драйвера NIC и драйвера протокола, другой – из драйвера NIC, промежуточного драйвера и драйвера протокола.

Рисунок 3: структура NDIS

Для нормальной работы драйверу захвата пакетов необходимо взаимодействовать как с драйвером сетевого устройства (для передачи и приема данных), так и с приложением пользователя (для получения от него данных или передачи ему принятых от сетевого устройства пакетов). Поэтому драйвер захвата пакетов разработан как драйвер протокола в структуре NDIS (рис. 4).

Рисунок 4: положение драйвера захвата пакетов в структуре NDIS

Это позволяет ему работать со всеми сетевыми устройствами, поддерживаемыми ОС Windows (адаптерами Ethernet и т. д.). Тем не менее, на данный момент драйвер работает только  с адаптерами Ethernet, loopback-адаптерами и поддерживает подключение к глобальной сети через Ethernet-адаптер. Пакет протоколов PPP NCP-LCP «прозрачен» для драйверов протоколов, поскольку PPP-соединения устанавливаются виртуально. Поэтому драйвер захвата пакетов не может работать с такого рода соединениями.

Вместо заключения

В этой статье мы рассказали лишь о построении и принципах работы архитектуры WinPCAP. В следующих статьях мы более подробно расскажем о функциях библиотеки packet.dll и libpcap, о том, как использовать эти библиотеки при написании сетевых приложений и приведем конкретные примеры исходного кода программ, использующих библиотеки архитектуры WinPCAP.

Сам драйвер WinPCAP, библиотеки и их исходные коды вы можете получить на сайте:

http://netgroup-serv.polito.it/winpcap

Rambler's Top100 ???????@Mail.ru