Nadzorowanie ruchu sieciowego w systemach Windows z wykorzystaniem interfejsu WinSock 2 Miłosz Kubański Wydział Inżynierii Mechanicznej i Informatyki Kierunek Informatyka, Rok IV miloszes@o2.pl Streszczenie W poniższej pracy pokazano sposób przechwytywania oraz modyfikowania danych przesyłanych przy użyciu "gniazd" w systemie Windows. Jako przykład zastosowania omawianej technologii podczas prezentacji przedstawiony zostanie system filtrujący dane przesyłane za pośrednictwem protokołu HTTP (strony WWW). Program posiada również funkcję blokującą niepożądane połączenia sieciowe (firewall). 1 Wstęp W dzisiejszych czasach szybki i bezproblemowy przepływ informacji odgrywa kluczowe znaczenie. Codziennie komputery wymieniają między sobą ogromne porcje informacji poprzez różnego rodzaju media transmisyjne. W sieci krążą liczne wirusy obciążające łącza oraz umożliwiające wykradane tak cennych w dzisiejszych czasach informacji. Dlatego tak istotne staje się zagwarantowanie jakości usług oraz niezawodności połączeń. Aby chronić się przed utratą informacji, oraz licznymi wirusami komputerowymi instalowane są liczne programy filtrujące połączenia sieciowe. W poniższej pracy zostanie przedstawiona technologia pozwalająca na tworzenie aplikacji nadzorujących ruch sieciowy danego komputera. Do takich programów możemy zaliczyć zapory sieciowe, oraz różnego rodzaju filtry treści. 2 Wprowadzenie do sieci komputerowych 2.1 Model ISO-OSI Aby połączenie pomiędzy dwoma programami aplikacyjnymi znajdującymi się na różnych komputerach o odmiennej konfiguracji sprzętowej było możliwe zaistniała potrzeba stworzenia standardu opisującego przepływ informacji. Ogólnie przyjętym modelem sieci jest model warstwowy ISO-OSI. W modelu tym wyodrębnia się pewne niezależne zadania, które mogą być rozwiązywane przez wydzielone układy sprzętowe lub pakiety oprogramowania zwane obiektami. Klasą obiektów rozwiązujących dane zagadnienie nazywa się warstwą. Pojęcie warstwy nie jest jednoznaczne z pojęciem protokołu funkcje 1
Rys. 1: Siedmio warstwowy model sieci ISO OSI danej warstwy mogą być wykonywane przez kilka różnych protokołów. Każdy protokół komunikuje się ze swoim odpowiednikiem, będącym implementacją tego samego protokołu w równorzędnej warstwie komunikacyjnej systemu odległego. Warstwy (a dokładnie konkretne protokoły zawarte w tej warstwie) komunikują się bezpośrednio z odpowiadającymi im warstwami w odległym hoście. Należy więc też zapewnić reguły przekazywania informacji w dół do kolejnych warstw pracujących na danym komputerze. Dane przekazywane są od wierzchołka stosu, poprzez kolejne warstwy, aż do warstwy fizycznej, która przesyła je poprzez sieć do odległego hosta. Na szczycie stosu znajdują się usługi świadczone bezpośrednio użytkownikowi przez aplikacje sieciowe, na spodzie sprzęt realizujący transmisję sygnałów niosących informacje[8]. Rysunek 1 przedstawia ogólny schemat modelu ISO-OSI. 2.2 Model protokołu TCP/IP Protokół tworzący Internet TCP/IP również możemy opisać za pomocą siedmio warstwowego modelu ISO/OSI. Lepiej jednak oddaje funkcje i właściwości protokołu TCP/IP uproszczony model cztero warstwowy. Jak można zauważyć na rysunku 2, w modelu tym najważniejsze są warstwy sieciowa i transportowa, pozostałe są połączone i tworzą dwie warstwy zwane warstwą dostępu do sieci oraz warstwą aplikacji. Funkcje tych warstw pokrywają się z zadaniami odpowiadających im warstw w modelu ISO/OSI. [5] Podobnie jak w modelu OSI kolejne warstwy dołączają (bądź usuwają, w zależności w którą stronę przesuwają się dane na stosie protokołów) własne nagłówki. Taki proces nazywa się enkapsulacją danych (Rys. 3). 2
Rys. 2: Czterowarstwowy model przedstawiający rodzinę protokołów TCP/IP 3 "Gniazda" Rys. 3: Przykład enkapsulacji danych protokołów rodziny TCP/IP Oprogramowanie protokołów TCP/IP w większości implementacji jest wbudowane w system operacyjny danego komputera. Tak więc, aby skorzystać z usług tych protokołów, program aplikacyjny musi wywoływać funkcje systemowe. Protokoły TCP/IP zaprojektowano tak, aby mogły działać na różnych systemach komputerowych, pochodzących od różnych producentów. Tak więc, opis interfejsu pośredniczącego pomiędzy protokołami TCP/IP, a korzystającymi z nich programami aplikacyjnymi ma postać tzw. specyfikacji luźnej (ang. loosely specyfied interface). W praktyce powstało tylko kilka realizacji interfejsu do oprogramowania TCP/IP. W Uniwersytecie Kalifornijskim w Berkeley opracowano definicję interfejsu przeznaczonego do systemu Berkeley Unix. Jest on znany jako interfejs gniazd lub interfejs gniazdowy (ang. sockets interface lub sockets). Z kolei firma AT&T zdefiniowała interfejs dla Systemu V Unixa. Jest on określany skrótem TLI (ang. Transport Layer Interface - interfejs warstwy transportowej). Powstało jeszcze kilka innych definicji interfejsu, ale jak dotych- 3
czas żadna z nich nie uzyskała szerszej akceptacji. 3.1 Gniazda "berkleyowskie" Na początku lat osiemdziesiątych Advanced Research Project Agency (ARPA) sponsorowała pracę zespołu specjalistów na Uniwersytecie Kalifornijskim w Berkeley, który to zespół podjął się zadania wbudowania oprogramowania TCP/IP w środowisko systemu UNIX, z założeniem udostępnienia opracowanego oprogramowania innym ośrodkom. Częścią tej pracy było zaprojektowanie interfejsu komunikacyjnego dla programów użytkowych. Projektanci zdecydowali się użyć istniejących uniksowych funkcji wszędzie tam, gdzie było to możliwe. Nowe funkcje wprowadzono tylko dla tych operacji wykonywanych przez TCP/IP, których wywołania trudno byłoby zrealizować w ramach istniejącego repertuaru. W rezultacie powstał interfejs gniazd (ang. socket interface), natomiast system operacyjny stał się znany pod nazwą Berkeley Unix lub BSD UNIX. Berkeley UNIX przyjęto jako system operacyjny w wielu komputerach, zwłaszcza dla stacji roboczych produkowanych przez takie firmy jak Sun Microsystems Incorporated, Tektronix Incorporated czy Digital Equipment Corporation. Przyczyniło się to do rozpowszechnienia interfejsu gniazdkowego. Jest on tak szeroko stosowany, że stał się de facto standardem.[4]. 4 WinSock Opracowany w Berkeley interfejs gniazd został zaadaptowany do systemu Windows pod nazwą WinSock (Windows Socket). Obejmuje on zestaw funkcji ogólnego zastosowania, umożliwiających korzystanie z wielu różnych protokołów komunikacyjnych, przy czym interfejs, z którego korzystają aplikacje nie jest zależny od protokołu, którym chcą się porozumiewać. Protokoły dostarczane są na zasadzie usług, udostępniających ujednolicony interfejs. W wywołaniach funkcji programista podaje jedynie typ usługi, z której chce skorzystać (nie podaje dokładnej nazwy protokołu). Dzięki takiemu podejściu wszystkie mechanizmy warstw poniżej warstwy aplikacji (model OSI) są maskowane przed aplikacją, która korzysta z gniazd w podobny sposób jak ze strumieni danych. Ogólną hierarchię warstw biblioteki WinSock 2.0 przedstawia Rys. 4 4.1 Powstanie WinSock W 1993 roku powstała biblioteka WinSock (wersja 1.1). Bardzo szybko stała się ona standardem aplikacji działających w systemach operacyjnych firmy Microsoft, wypierając inne nieustandaryzowane rozwiązania. Niestety miała ona sporo ograniczeń. Jednym z nich była możliwość obsługi tylko jednej rodziny protokołów: "TCP/IP". W momencie pojawienia się wersji oznaczonej jako 2.0 sytuacja uległa znacznej poprawie. Umożliwiono bowiem korzystanie z protokołów innych rodzin niż TCP/IP, gniazd w trybie nie blokującym. Wprowadzono również kontrolę jakości połączenia (ang. Quality of Service), współdzielenia "socketów" i wiele innych możliwości. Interfejs dostarczony w tej wersji był już bardzo zbliżony do ideologii interfejsu gniazd z systemu BSD UNIX. 4
4.2 SPI (Service Provider Interface) Jedną z największych wad WinSock 1.1 było ograniczenie możliwości korzystania z protokołów tylko do rodziny TCP/IP. Twórcy tego interfejsu poszli o duży krok do przodu przygotowując WinSock 2.0. Udostępniono bowiem obsługę protokołów innych rodzin niż TCP/IP, dołączając nowy interfejs zarządzający usługami zainstalowanymi w systemie Windows nazwany SPI (Service Provider Interface). 4.3 LSP (Layered Service Provider) WinSock 2 pozwala tworzyć dwa typy usług: transportowe (ang. transport service providers), do której zaliczamy protokoły transportowe np. TCP/IP oraz przestrzeni nazw (ang. namespace resolution service providers) czyli usługi związane z tłumaczeniem adresów sieciowych na nazwy przyjazne dla człowieka np. DNS). W ramach usług transportowych można wyróżnić dwie grupy dostawców: podstawowe zaliczane do warstwy transportowej i sieciowej (ang. Base Service Provider) oraz rozszerzajace (ang. Layered Service Provider) znajdujący się w warstwie sesji. Usługi typu podstawowego zawierają szczegóły protokołu komunikacyjnego takie jak ustanawianie połączenia, transfer danych, czy też obsługa błędów. Drugi typ usługi transportowej zawiera tylko wybrane funkcje komunikacyjne i opiera się na istniejącej już w systemie usłudze komunikacyjnej typu podstawowego (odpowiedzialnej za obsługę połączenia) lub innej istniejącej usłudze typu rozszerzonego. Przykładem usługi typu rozszerzonego może być usługa jakości połączeń (QOS) w Windows 98 oraz 2000, która korzysta z protokołu TCP/IP (typ podstawowy).[1] Dostawcy usług typu przestrzeni nazw w chwili obecnej (WinSock 2.2.x) nie posiadają typu rozszerzalnego. 4.4 Stos dostawców usług Ponieważ usługi typu rozszerzonego korzystają z typów podstawowych ("leżą" na usługach transportowych), muszą udostępniać interfejs identyczny dla usług podstawowych i posiadać takie same parametry co warstwa, którą przykrywają. W systemie operacyjnym mogą powstać dwie lub więcej usługi o podobnych parametrach, co powoduje problem wybrania odpowiedniego dostawcy przez aplikację. Problem ten udało się rozwiązać dzięki wykorzystaniu stosu dostawców usług. W większości przypadków podczas wybierania odpowiedniego dostawcy, aplikacja zgłasza do interfejsu WinSock żądanie udostępnienia gniazda usługi charakteryzującej się pewnymi cechami (np. żądanie udostępnienia protokołów rodziny TCP/IP). Warstwa SPI biblioteki WinSock sprawdza dostępne usługi przeglądając stos dostawców począwszy od jego wierzchołka. Po napotkaniu usługi zgodnej z żądanymi parametrami zwraca do aplikacji deskryptor gniazda wykorzystującego daną usługę. Jak łatwo zauważyć w wypadku istnienia więcej niż jednej usługi o tych samych parametrach, o wyborze jednej z nich najczęściej decyduje ich położenie na stosie, przy czym pierwszeństwo wyboru mają usługi położone bliżej wierzchołka. 5
Rys. 4: Hierarchia warstw w bibliotece WinSock 2.0 5 Wykorzystanie LSP do nadzorowania ruchu sieciowego To właśnie dzięki usługom LSP możliwe jest pisanie programów nadzorujących ruch sieciowy na wszystkich systemach Windows, które zawierają bibliotekę WinSock w wersji 2.x. Jak wspomniano wcześniej usługi tego typu nie wymagają ponownej implementacji warstw sieciowych. Tak naprawdę usługi LSP udostępniają tylko interfejs zgodny ze standardowym interfejsem dostawcy usług, przekazując obsługę połączenia do dostawcy usługi którego przykrywają. [3]. Dzięki wykorzystaniu ujednoliconego interfejsu funkcji aplikacja korzysta ze wszystkich usług, wykorzystując ten sam interfejs. Usługi rozszerzające protokoły transportowe są "przezroczyste" dla aplikacji, które z nich korzystają, dzięki czemu można wymusić na aplikacji korzystanie z danego rozszerzenia usługi. W momencie wybrania przez SPI usługi rozszerzonej, wszystkie wywołania funkcji operujących na gniazdach przekazywane są do LSP. Rozszerzony dostawca interpretuje przekazane parametry i może je przekazać dalej do dostawcy znajdującego się niżej, lub zwrócić błąd. To właśnie rozwiązanie pozwala nam na pełną kontrolę nad danymi przesyłanymi z wykorzystaniem gniazd WinSock, gdyż wszystkie funkcje oraz parametry muszą być zaakceptowane przez warstwę usługi rozszerzonej. Funkcje udostępniane przez API Win- Sock są mapowane do funkcji interfejsu SPI. Np. funkcja API "socket ()" odpowiadająca za tworzenie gniazda w interfejsie SPI posiada nazwę WSPSocket(), funkcja accept() WSPAccept () itd. Jak łatwo się domyśleć wszystkie funkcje udostępnione przez API WinSock są w podobny sposób przeciążane przez funkcje SPI. 6
Znając interfejs API winsock możemy łatwo zaplanować implementację funkcji naszego dostawcy. Np. chcąc wyposażyć naszą usługę w funkcje zapory ogniowej (ang. firewall) interesować nas będą szczególnie funkcje: WSPStartup (), WSPSocket(), WSPAccept(), WSPConnect(), WSPBind () Dla programów filtrujących funkcjami którymi powinniśmy obdarzyć dodatkowymi możliwościami mogą być np.: WSPSend (), WSPSendTo (), WSPRecv (), WSPRecvFrom () W funkcjach tych zawieramy szczegółową implementację nowych właściwości usługi. W większości pozostałych zaś metod możemy ograniczyć się jedynie do przekazania funkcjonalności do dostawcy znajdującego się warstwę niżej. Jednakże ograniczenie się wyłącznie do takich modyfikacji naszego dostawcy nie jest wystarczające, ponieważ biblioteka WinSock w wersji 2.0 została znacznie bardziej rozszerzona w stosunku do swojego poprzednika. Aplikacje korzystające z funkcjonalności WinSock 1.x będą działały poprawnie, lecz inne korzystające w większym stopniu z nowych możliwości WinSock, mogą działać niepoprawnie, w najgorszym przypadku mogą się "zawiesić". Jest to szczególnie ważne, gdyż wiele warstw oraz procesów systemu operacyjnego również korzysta z komunikacji wykorzystując do tego gniazda. Jak łatwo się domyśleć w łatwy sposób można doprowadzić do awarii systemu operacyjnego. 5.1 Obsługa zdarzeń Dużym udogodnieniem dla programistów jest możliwość obsługi zdarzeń sieciowych, przez programy aplikacyjne. Podczas procesu komunikacji z wykorzystaniem gniazd aplikacja może sprawdzić czy pojawiły się jakieś nowe zdarzenia wywołując funkcję WSPEnumNetworkEvents() [3]. Obsługa zdarzeń w systemach Windows oparta jest na systemie komunikatów miedzy oknami. Aby nasza usługa mogła otrzymywać komunikaty od dostawcy warstwy niższej i przesyłać je do aplikacji, należy stworzyć nowe okno w kontekście wykonywanego procesu. Okno to różni się od standardowych formatek używanych przez aplikacje tym, że jest ono przez cały czas ukryte i służy naszej usłudze wyłącznie w celu przechwytywania pojawiających się zdarzeń sieciowych, które mogą zostać zinterpretowane i przesłane do aplikacji korzystającej z danego gniazda. 6 Podsumowanie Zaadaptowanie rozwiązania powstałego na Uniwersytecie w Berkeley do systemów rodziny Windows bardzo uprościło proces tworzenia aplikacji sieciowych. Istotnymi właściwościami tego interfejsu są: wykorzystanie tych samych funkcji obsługujących gniazda niezależnie od wykorzystywanego protokołu transportowego, 7
swoboda dodawania, oraz rozszerzania usług, "przezroczystość" (ang. transparency) usług rozszerzających podstawowe funkcje protokołów transportowych, obsługa zdarzeń sieciowych. Najistotniejszą właściwością z punktu widzenia naszej pracy jest możliwość dodawania nowych rozszerzeń na istniejące już usługi transportowe. Mechanizm ten daje nam prawie nieograniczone możliwości nadzorowania ruchu sieciowego. Dzięki temu rozwiązaniu można w łatwy sposób tworzyć "przezroczyste" zapory sieciowe, programy filtrujące, usługi QOS, kryptograficzne i wiele innych. Niestety jak wiele innych technologii, również ta może być wykorzystana do celów przestępczych. Bardzo często programy podsłuchujące (ang. sniffer), oraz konie trojańskie wykorzystują tą technikę do podsłuchiwania ruchu sieciowego komputera w celu przechwycenia poufnych danych. Do wad mechanizmu LSP można również dodać zwiększenie wykorzystania mocy obliczeniowej procesora, oraz pamięci operacyjnej przez system operacyjny. Niemniej jednak w czasach komputerów dysponujących tak dużymi mocami obliczeniowymi, oraz ogromnymi ilościami pamięci RAM, wady te w porównaniu z możliwościami jakie udostępnia ten mechanizm wydają się nie mieć większego znaczenia. Literatura [1] Wei Hua, Jim Ohlund, Barry Butterklee, Unraveling the Mysteries of Writing a Winsock 2 Layered Service Provider. http://www.microsoft.com/msj/0599/layeredservice/layeredservice.aspx [2] Microsoft Developer Network (MSDN). http://msdn.microsoft.com/http://msdn.microsoft.com/ [3] Windows* Sockets 2 Service Provider Interface. http://www.ictp.trieste.it/ radionet/nuc1996/ref/winsock/wsspi22.htm [4] Douglas E. Comer, David L. Stevens, Sieci komputerowe TCP/IP tom 3 Programowanie w trybie klient-serwer. Wersja BSD. [5] W. Richards Stevens, Gary R. Wright, Biblia TCP/IP tom 2 Implementacje. [6] W. Richards Stevens, UNIX programowanie usług sieciowych tom 1 API: gniazda i XTI. [7] Ian McLean, Windows 2000 TCP/IP. Czarna księga. [8] Karol Krysiak, Sieci komputerowe. Kompendium. 8