Пользователь
0,0
рейтинг
6 января 2013 в 18:27

Разработка → Пишем простой сниффер под Windows из песочницы tutorial

В этой статье мы рассмотрим создание простого сниффера под ОС Windows.
Кому интересно, добро пожаловать под кат.

Введение


Цель: написать программу, которая будет захватывать сетевой трафик (Ethernet, WiFi), передающийся по протоколу IP.
Средства: Visual Studio 2005 или выше.
Подход, который здесь описан, не принадлежит лично автору и успешно применяется во многих коммерческих, а также категорически бесплатных программах (привет, GPL).
Сей труд предназначен прежде всего для новичков в сетевом программровании, которые, однако, имеют хотя бы базовые знания в области сокетов вообще, и windows-сокетов в частности. Здесь я часто буду писать общеизвестные вещи, потому что предметная область специфическая, если что-то пропустить — в голове будет каша.

Надеюсь, Вам будет интересно.

Теория (читать не обязательно, но желательно)


В данный момент подавляющее большинство современных информационных сетей базируются на фундаменте стека протоколов TCP/IP. Стек протоколов TCP/IP (англ. Transmission Control Protocol/Internet Protocol) — собирательное название для сетевых протоколов разных уровней, используемых в сетях. В настоящей статье нас будет интересовать в основном протокол IP — маршрутизируемый сетевой протокол, используемый для негарантированной доставки данных, разделяемых на так называемые пакеты (более верный термин – дейтаграмма) от одного узла сети к другому.
Особый интерес для нас представляют IP-пакеты, предназначенные для передачи информации. Это достаточно высокий уровень сетевой OSI-модели данных, когда можно обстрагироваться от устройства и среды передачи данных, оперируя лишь логическим представлением.
Совершенно логичным является то обстоятельство, что рано или поздно должны были появится инструменты для перехвата, контроля, учета и анализа сетевого трафика. Такие средства обычно называется анализаторами трафика, пакетными анализаторыми или снифферами (от англ. to sniff — нюхать). Это — сетевой анализатор трафика, программа или программно-аппаратное устройство, предназначенное для перехвата и последующего анализа, либо только анализа сетевого трафика, предназначенного для других узлов. [1]

Практика (разговор по существу)


На данный момент создано достаточно много программного обеспечения для прослушивания трафика. Наиболее известный из них: Wireshark. Естественно, пожинать его лавры цель не стоит — нас интересует задача перехвата трафика методом обычного «прослушивания» сетевого интерфейса. Важно понимать, что мы не собираемся заниматься взломом и перехватывать чужой трафик. Нужно всего лишь просматривать и анализировать трафик, который проходит через наш хост.

Для чего это может понадобиться:
  1. Смотреть текущий поток трафика через сетевое соеднинение (входящий/исходящий/всего).
  2. Перенаправлять трафик для последующего анализа на другой хост.
  3. Теоретически, можно попытаться применить его для взлома WiFi-сети (мы ведь не собираемся этим заниматься?).

В отличие от Wireshark, который базируется на библиотеке libpcap/WinPcap, наш анализатор не будет использовать этот драйвер. Чего уж там, у нас вообще не будет драйвера, и свой NDIS(о ужас!) мы писать не собираемся. Про это можно прочитать в этом топике. Он будет просто пассивным наблюдателем, использующим только библиотеку WinSock. Использование драйвера в данном случае избыточно.

Как так? Очень просто.
Ключевым шагом в превращении простого сетевого приложения в сетевой анализатор является переключение сетевого интерфейса в режим прослушивания (promiscuous mode), что и позволит ему получать пакеты, адресованные другим интерфейсам в сети. Этот режим заставляют сетевую плату принимать все кадры, вне зависимости от того, кому они адресованы в сети. [2]

Начиная с Windows 2000 (NT 5.0) создать программу для прослушивания сегмента сети стало очень просто, т.к. ее сетевой драйвер позволяет перевести сокет в режим приёма всех пакетов.

Включение неразборчивого режима

long flag = 1;
SOCKET socket;
#define SIO_RCVALL 0x98000001

ioctlsocket(socket, SIO_RCVALL, &RS_Flag);

Наша программа оперирует IP-пакетами, и использует библиотеку Windows Sockets версии 2.2 и «сырые» сокеты (raw sockets). Для того чтобы получить прямой доступ к IP-пакету, сокет нужно создавать следующим образом:

Создание сырого сокета

s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);

Здесь вместо константы SOCK_STREAM (протокол TCP) или SOCK_DGRAM (протокол UDP), мы используем значение SOCK_RAW. Вообще говоря, работа с raw sockets интересна не только с точки зрения захвата трафика. Фактически, мы получаем полный контроль за формированием пакета. Вернее, формируем его вручную, что позволяет, например, послать специфический ICMP-пакет…

Идем дальше. Известно, что IP-пакет состоит из заголовка, служебной информации и, собственно, данных. Советую заглянуть сюда, чтобы освежит знания. Опишем в виде структуры IP-заголовок (спасибо отличной статье на RSDN [3]):

Описание структуры IP-пакета


typedef struct _IPHeader
{
  unsigned char  ver_len;		// версия и длина заголовка
  unsigned char  tos;			// тип сервиса 
  unsigned short length;		// длина всего пакета 
  unsigned short id;			// Id 
  unsigned short flgs_offset;		// флаги и смещение
  unsigned char  ttl;			// время жизни 
  unsigned char  protocol;		// протокол 
  unsigned short xsum;			// контрольная сумма 
  unsigned long  src;			// IP-адрес отправителя 
  unsigned long  dest;			// IP-адрес назначения 
  unsigned short *params;		// параметры (до 320 бит)
  unsigned char  *data;			// данные (до 65535 октетов)
}IPHeader;


Главная функция алгоритма прослушивания будет выглядеть следующим образом:

Функция захвата одного пакета

IPHeader* RS_Sniff()
{
	IPHeader *hdr;
	int count = 0;
	count = recv(RS_SSocket, (char*)&RS_Buffer[0], sizeof(RS_Buffer), 0);
	if (count >= sizeof(IPHeader))
	{
		hdr = (LPIPHeader)malloc(MAX_PACKET_SIZE);
		memcpy(hdr, RS_Buffer, MAX_PACKET_SIZE);
		RS_UpdateNetStat(count, hdr);
		return hdr;
	}
	else
		return 0;
}

Здесь все просто: получаем порцию данных с помощью стандартной функции socket-функции recv, а затем копируем их в структуру типа IPHeader.
И, наконец, запускаем бесконечный цикл захвата пакетов:

Захватым все пакеты, которые попадут на наш сетевой интерфейс

while (true)
{
	IPHeader* hdr = RS_Sniff();
	// обработка IP-пакета
	if (hdr)
	{ 
		// печатаем заголовок в консоли
	}
}

Немного оффтопика

Здесь и далее у некоторых важных функций и переменных автор сделал префкис RS_ (от Raw Sockets). Проект делал 3-4 года назад, и была шальная мысль написать полноценную библиотеку для работы с сырыми сокетами. Как это часто бывает, после получения сколь-нибудь значимых(для автора) результатов, энтузиазм угас, и дальше учебного примера дело не полшло.

В принципе, можно пойти дальше, и описать заголовки всех последующих протоколов, находящихся выше. Для этого необходимо анализировать поле protocol в структуре IPHeader. Посмотрите на пример кода (да, там должен быть switch, чёрт возьми!), где происходит раскрашивание заголовка в зависимости от того, какой протокол имеет пакет, инкапсулированный в IP:

/*
*  Выделение пакета цветом
*/
void ColorPacket(const IPHeader *h, const u_long haddr, const u_long whost = 0)
{
	if (h->xsum)
		SetConsoleTextColor(0x17); // если пакет не пустой
	else
		SetConsoleTextColor(0x07); // пустой пакет

	if (haddr == h->src)		 
	{
		SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ 
			FOREGROUND_RED | FOREGROUND_INTENSITY); // "родной" пакет на отдачу
	}
	else if (haddr == h->dest)		 
	{
		SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ 
			FOREGROUND_GREEN | FOREGROUND_INTENSITY); // "родной" пакет на прием
	}

	if (h->protocol == PROT_ICMP || h->protocol == PROT_IGMP)		 
	{
		SetConsoleTextColor(0x70); // ICMP-пакет 
	}
	else if(h->protocol == PROT_IP || h->protocol == 115)
	{
		SetConsoleTextColor(0x4F); // IP-in-IP-пакет, L2TP
	}
	else if(h->protocol == 53 || h->protocol == 56)
	{
		SetConsoleTextColor(0x4C); // TLS, IP with Encryption
	}

	if(whost == h->dest || whost == h->src)
	{
		SetConsoleTextColor(0x0A);
	}
}


Однако это существенно выходит за рамки этой статьи. Для нашего учебного примера вполне достаточно будет посмотреть ip-адреса хостов, с которых и на которые идет трафик, и посчитать его количество в единицу времени(готовая программа в архиве в конце статьи).

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

Преобразование IP-заголовка в строку

inline char* iph2str(IPHeader *iph)
{
	const int BUF_SIZE = 1024;
	char *r = (char*)malloc(BUF_SIZE);
	memset((void*)r, 0, BUF_SIZE);

	sprintf(r, "ver=%d hlen=%d tos=%d len=%d id=%d flags=0x%X offset=%d ttl=%dms prot=%d crc=0x%X src=%s dest=%s",
	
           BYTE_H(iph->ver_len),
           BYTE_L(iph->ver_len)*4, 
           iph->tos, 
           ntohs(iph->length),
           ntohs(iph->id), 
           IP_FLAGS(ntohs(iph->flgs_offset)), 
           IP_OFFSET(ntohs(iph->flgs_offset)), 
           iph->ttl, iph->protocol, 
           ntohs(iph->xsum), nethost2str(iph->src),
           nethost2str(iph->dest) 
       );
	return r;
}

На основании приведенных выше базовых сведений, получается вот такая небольшая программа (жуткое название ss, сокр. от англ. simple sniffer), реализующая локальное прослушивание IP-трафика. Интерфейс ее приведен ниже на рисунке.



Исходный и бинарный код предоставляю как есть, таким как он был несколько лет назад. Сейчас мне на него страшно смотреть, и все же, он вполне читабельный (конечно же, нельзя быть таким самоуверенным). Для компиляции будет достаточно даже Visual Studio Express 2005.

Что у нас получилось в итоге:
  • Сниффер работает в режиме пользователя, однако требует привилегии администратора.
  • Пакеты не фильтруются, отображаясь как есть (можно добавить настраиваемые фильтры — предлагаю подробно рассмотреть эту тему в следующей статье, если интересно).
  • WiFi-трафик тоже захватывается(все зависит от конкретной модели чипа, у Вас может и не работать, как у меня несколько лет назад), хотя есть AirPcap, которая чудесно это умеет делать, но стоит денег.
  • Весь поток дейтаграмм логируется в файл (см. архив, приложенный в конце статьи).
  • Программа работает в качестве сервера на порту 2000. Можно подключиться с помощью утилиты telnet к хосту и произвести мониторинг потоков трафика. Количество подключений ограничено двадцатью (код не мой, нашел на просторах сети и применял для экспериментов; удалять не стал — жалко)

Спасибо за внимание, проздравляю хабровчан и хабровчанок и всех-всех-всех с наступающим Рождеством!

Скачать проект Пароль на открытие: habr

upd: По совету Weageoo, выложил на гитхаб.
Anton Petrov @antonpv
карма
12,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (19)

  • +6
    Голова я садовая! Забыл указать пароль на открытие архива: habr
    Интерtсно, хоть кто-то из скачавших пытался его открыть? (:
  • 0
    Если идет пост запрос от клиента, сниффер может поймать параметры? :)))
    • 0
      Если, допустим, речь идет о HTTP, то если сниффер бежит на сервере и запрос не в HTTPS, то да. Но, вообще-то, в этом случае и сам сервер может поймать параметры. А если это не HTTP, то все зависит от протокола и шифрации.
  • –8
    Можно вопрос: зачем?

    лечение
    wireshark: «you could still cock-up with this, or you can use me instead!»

  • 0
    Затем! А если серьезно, Вы саму статью хоть читали, или только заголовок? Можно я не буду сам себя цитировать?)
    • –7
      Перечитал еще раз, все что обнаружил:

      Сей труд предназначен прежде всего для новичков в сетевом программИровании


      Для чего конкретно он может понадобиться новичкам в сетевом программировании? Узнать устройство сетевого стека — windows internals + sir Andrew Tanenbaum. Узнать как написать «программу которая работает с berkeley-сокетами»? Вас самую малость опередили. Узнать как работают снифферы? Скачиваем wireshark source code и смотрим как на самом деле все происходит, в реальном мире. Я ничего не имею против содержания статьи, но её академичность и отдаленность от реальных задач ставит под вопрос вообще смысл её написания, т.к. эти вопросы в таком виде уже тысячи раз освещались.

      Все ИМХО.
      • +2
        Подходит вполне для обучения, особенно на начальных этапах, когда хочется видеть простые каркасы, а главное — работающие. И думается, что чтение исходников wireshark новичек не осилит.
      • 0
        >>Узнать устройство сетевого стека — windows internals + sir Andrew Tanenbaum.
        Очень многие люди, и я в том числе, пока не «по щупают» фиг че запомнят или поймут! Если теория это «проявка», то практика это «фиксаж». Когда Вы говорите о WireShark, тулзах деда Руссиныча, то вы говорите о «проявке». Но многие вещи вылетают из головы, достаточно быстро. В случае «фиксажа» многие вещи остаются на долго и освежаются в памяти подзабытые вещи значительно быстрее!

        Из своего опыта.
        Я очень много читал об упаковщиках и вирусах когда-то, потом занимался практикой распаковывания. Но когда я взялся за написание своего первого упаковщика, тогда я понял, что ничего не смыслю в упаковке! При разработке упаковщика я открыл «бездну» просто огромное множество деталей, которых бы не смог открыть занимаясь только лишь распаковкой!
        К примеру банальный вопрос: «Сколько TLS-калбаков может быть в PE-файле?» многих, не писавших упаковщик, ложит на лопатки!
        • –2
          Т.е. чтение чужих исходников вы считаете «фиксажом», и, что более интересно, «практикой»?
          • +2
            Я считаю, что чтение исходных кодов минимум на шаг ближе чем просмотр значений показываемых различными тулзами. Хотя, многое зависит от исходного кода.
            При просмотре исходного кода, Вы можете столкнуться с некоторыми спорными, на Ваш взгляд, моментами и пытаясь понять почему программист так закодировал Вы можете получить те знания, которые бы вероятно и не получили бы при просмотре значений в выводе тулзов.
            • –2
              В моем понимании практика — нечто большее. Когда я хочу в чем-то разобраться, я делаю так:

              1) Теория
              2) Написание программульки, либо использование уже существующей
              3) Windbg для полного понимания, что происходит «под капотом»

              Чтение исходников, особенное дилетантских, обычно ни к чему не приводит кроме как «о да, вот тут я вижу что эта функция вызывается».
      • +1
        Перечитал еще раз

        Спасибо :)

        Узнать как работают снифферы?

        Вы не поверите, но — да! Именно для этого написана эта статья, по мотивам собственного опыта. Симпатичная розовая меточка tutorial как-бы намекает ;)

        Скачиваем wireshark source code и смотрим как на самом деле все происходит, в реальном мире.

        Этот путь подойдет для профи. Лично мне больше нравится найти отдельные фрагменты, ключевые моменты, как это работает, ну а все остальное писать самому. Читать программы в сотни тысяч строк не так уж и просто, тем более делать это ради десятка строк кода, которые хочется понять.
        Вы если хотите повысить свой уровень, узнать что-то новое, освоить новую технологию, тоже всегда открываете исходники проекта класса ААА? Слабо себе представляю, что можно изучить, например, написание ОС по ядру Linux (на начальной стадии).

        Я ничего не имею против содержания статьи...

        Ваш предыдущий комментарий говорит как раз об обратном.

        но её академичность и отдаленность от реальных задач ставит под вопрос вообще смысл её написания

        No problem. Статья не академичная и отдаленная от реальных задач. В меня убедили. Больше писать не буду. *пошел форматировать винчестер*
        • –1
          Всего вам хорошего :)
  • 0
    А модификацию трафика сложно реализовать?
    • 0
      Методом, который описан в статье, трафик модифицировать не получится при всем желании.
  • +2
    А на чем вы тестировали свой софт? Я когда-то тоже так разогнался, думал зачем мне WinPCap, сделаю на raw сокетах, а по факту:
    Windows XP SP1 — все работает
    Windows XP SP2 и выше — есть входящий трафик, но нет исходящего
    Windows 7 — есть исходящий трафик, но нет входящего.
    Посмотрев на это все, я решил что мне raw сокеты не подходят, и сделал на WinPCap.

    p.s. Архив не качал.
    • 0
      На Windows 7 все работает отлично. Должно работать на всех ОС выше Windows 2000.
      Масштабный тест не проводил, по-моему, на Windows XP тоже все ок.
      Может, кто-то из скачавших прокомментирует работоспособность (хотя, наверняка у большинства Windows 7 или что-то из Linux).
      • 0
        Чуть позже проверю на WinXP SP2 в виртуалке, но мне кажется ничего не поменялось, и будет только половина трафика (либо исходящий, либо входящий)
  • +2
    Отличная статья, спасибо. С удовольствием «плюсанул». Пишите ещё, пусть будет больше статей на Хабре именно технических, для читателей разного уровня. И не слушайте всякие «а зачем?» :)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.