Как написать свой сниффер

Время на прочтение
7 мин

Количество просмотров 151K

В этой статье мы рассмотрим создание простого сниффера под ОС 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, выложил на гитхаб.

В этой статье мы рассмотрим создание простого сниффера под ОС 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-трафика. Интерфейс ее приведен ниже на рисунке.

Пишем простой сниффер под Windows

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

Что у нас получилось в итоге:

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

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

Скачать проект

Автор: antonpv

Источник

Перевод статьи: http://www.codeproject.com/Articles/17031/A-Network-Sniffer-in-C

Скачать: Исходный код с сайта codeproject

MJsniffer

В этом переводе будет показано как создать простой снифер, с парсингом IP, TCP, UDP и DNS пакетов на c#. Без использования сторонних библиотек типа SharpCap.

//Для захвата пакетов сокетом
//он должен иметь тип raw
//с протоколом IP
mainSocket = newSocket(AddressFamily.InterNetwork, SocketType.Raw,
                       ProtocolType.IP);

// Привязываем сокет к выбранному IP
mainSocket.Bind(newIPEndPoint(IPAddress.Parse(cmbInterfaces.Text),0));

//Устанавливаем опции у сокета
mainSocket.SetSocketOption(SocketOptionLevel.IP,  //Принимать только IP пакеты
                           SocketOptionName.HeaderIncluded, //Включать заголовок
                           true);                           

byte[] byTrue = newbyte[4]{1, 0, 0, 0};
byte[] byOut = newbyte[4];

//Socket.IOControl это аналог метода WSAIoctl в Winsock 2
mainSocket.IOControl(IOControlCode.ReceiveAll,  //SIO_RCVALL of Winsock
                     byTrue, byOut);

//Начинаем приём асинхронный приём пакетов
mainSocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None,
                        newAsyncCallback(OnReceive), null);

Для захвата пакетов, мы используем raw(сырой) сокет и привязываем его к IP-адресу. После установки некоторых параметров для сокета, вызываем метод IOControl. Обратите внимание, что IOControl аналогичен методу Winsock2WSAIoctl. IOControlCode.ReceiveAll означает, что захватываться будут абсолютно все пакеты как входящие так и исходящие.

Второй параметр, передаваемый в IOControl с IOControlCode.ReceiveAll должен содержать именно такое значение массива byTrue (спасибо Леониду Молочному за это). Далее мы начинаем получать все пакеты асинхронно.

Анализ пакетов

IP датаграммы инкапсулируются TCP и UDP пакетами. Они также содержатся в  данных, передаваемыъ по протоколам прикладного уровня, таких как DNS, HTTP, FTP, SMTP, SIP и т.д. Таким образом, пакет TCP содержит в себе дейтаграммы IP, например:

+--------------+---------------+----------------------+
| IP заголовок | TCP заголовок |        Данные        |  
+--------------+---------------+----------------------+

Итак, первое, что мы должны сделать, это проанализировать IP-заголовок. Для этого создан урезанный класс classIPHeader. Комментарии описывают что и как происходит.

public classIPHeader 
{ 
  //Поля IP заголовка
  private byte byVersionAndHeaderLength; // Восемь бит для версии 
                                         // и длины 
  private byte byDifferentiatedServices; // Восемь бит для дифференцированного 
                                         // сервиса
  private ushort usTotalLength;          // 16 бит для общей длины 
  private ushort usIdentification;       // 16 бит для идентификатора
  private ushort usFlagsAndOffset;       // 16 бит для флагов, фрагментов 
                                         // смещения 
  private byte byTTL;                    // 8 бит для TTL (Time To Live) 
  private byte byProtocol;               // 8 бит для базового протокола
  private short sChecksum;               // 16 бит для контрольной суммы 
                                         //  заголовка 
  private uint uiSourceIPAddress;        // 32 бита для адреса источника IP 
  private uint uiDestinationIPAddress;   // 32 бита для IP назначения 

  //Конец полей IP заголовка   
  private byte byHeaderLength;             //Длина заголовка
  private byte[] byIPData = new byte[4096]; //Данные в дейтаграмме
  public IPHeader(byte[] byBuffer, int nReceived)
  {
    try
    {
    //Создаём MemoryStream для принимаемых данных
    MemoryStream memoryStream = newMemoryStream(byBuffer, 0, nReceived);

    //Далее создаем BinaryReader для чтения MemoryStream
    BinaryReader binaryReader = newBinaryReader(memoryStream);

    //Первые 8 бит содержат верисю и длину заголовка
    //считываем 8 бит = 1 байт
    byVersionAndHeaderLength = binaryReader.ReadByte();

    //Следующие 8 бит содержат дифф. сервис
    byDifferentiatedServices = binaryReader.ReadByte();

    //Следующие 8 бит содержат общую длину дейтаграммы
    usTotalLength = 
             (ushort) IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //16 байт для идентификатора
    usIdentification = 
              (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //8 бит для флагов, фрагментов, смещений
    usFlagsAndOffset = 
              (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //8 бит для TTL
    byTTL = binaryReader.ReadByte();

    //8 бит для базового протокола
    byProtocol = binaryReader.ReadByte();

    //16 бит для контрольной суммы
    sChecksum = IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());

    //32 бита для IP источника
    uiSourceIPAddress = (uint)(binaryReader.ReadInt32());

    //32 бита IP назначения
    uiDestinationIPAddress = (uint)(binaryReader.ReadInt32());

    //Высчитываем длину заголовка
    byHeaderLength = byVersionAndHeaderLength;

    //Последние 4 бита в версии и длине заголовка содержат длину заголовка
    //выполняем простые арифметические операции для их извлечения
    byHeaderLength <<= 4;
    byHeaderLength >>= 4;

    //Умножаем на 4 чтобы получить точную длину заголовка
    byHeaderLength *= 4;

    //Копируем данные (которые содержат информацию в соответствии с типом 
    //основного протокола) в другой массив
    Array.Copy(byBuffer, 
               byHeaderLength, //копируем с конца заголовка
               byIPData, 0, usTotalLength - byHeaderLength);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "MJsniff", MessageBoxButtons.OK, 
                        MessageBoxIcon.Error);
    }
 }

}

Класс содержит элементы данных, соответствующих полям заголовка IP. Ознакомтесь с RFC 791 для подробного изучения  IP заголовка и его полей. Конструктор класса берет полученные байты и создает MemoryStream для полученной информации. Затем создает BinaryReader, чтобы считать данные байт за  байтом из MemoryStream. Также отметим, что данные, полученные от сети, находятся в форме с обратным порядком байтов, в связи с этим мы используем IPAddress. NetworkToHostOrder, чтобы исправить порядок байтов. Это должно быть сделано для всех элементов данных.

P.S. На основе этого примера буду писать программку для мониторинга сети. До этого писал с использованием SharpPcap, но необходимость установки WinPcap заставила попробовать изобрести велосипед 8)

python icon

Сниффер — это программный инструмент для перехвата сетевого трафика, который используется системными администраторами и хакерами. В этой статье мы напишем простой сниффер для перехвата трафика на Python.

Еще по теме: Как установить пакеты Python без интернета

Хакеры и специалисты по информационной безопасности могут использовать снифферы для выявления недостатков безопасности подключенных к сети устройств. С помощью сниффера можно перехватить пароли и другую личную информацию.

Снифферы делятся на две основные категории:

  • Активные снифферы —  анализаторы трафика, которые взаимодействуют с устройствами.
  • Пассивные снифферы — собирают дополнительные данные.

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

Как работают анализаторы пакетов

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

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

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

Создание сниффера для анализа трафика на Python

В этом руководстве рассмотрим создание пассивного сниффера.

Итак, откройте VS Code и создайте файл
main.py. Начнем с трех операций импорта, которые помогут открыть сокет для захвата сетевых данных и распаковки отформатированных данных. Этот импорт выглядит следующим образом:

import socketi

mport struct

import binascii

Далее откроем сокет и возьмем переменные длиной до 2048 бит. В функцию сокета нужно будет передать три переменные:

  1. Первая указывает интерфейс пакета Windows (AF_INET).
  2. Вторая указывает, что мы открываем необработанный сокет.
  3. Третья указывает интересующий протокол, который в данном случае является протоколом IP.

Для получения пакетов размером 2048 в бесконечном цикле будем использовать функцию recvfrom. Код выглядит следующим образом:

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket. htons(0x0800)) #Creating socket

while True:

    packet = s.recvfrom(2048)

Теперь, нужно вывести отформатированные данные пакета. Для этого будем использовать импорт struct и binascii. Учитывая, что в этом примере нас интересует IP-адрес источника и получателя, нам нужна только первая строка в пакете. Возьмем первые 16 байт этого заголовка и распечатаем их следующим образом:

ethernet_header = packet[0][0:14] # Get Ethernet Header

eth_header = struct.unpack(«!6s6s2s», ethernet_header)

print («Destination MAC:» + binascii.hexlify(eth_header[0]) + » Source

MAC:» + binascii.hexlify(eth_header[1]) + » Type:» +

binascii.hexlify(eth_header[2]))

Запуск этого кода от имени администратора (или использование
sudo, если вы работаете в Linux) должен отобразить все пакеты в терминале.

Это пример простого пассивного сниффера на Python.

РЕКОМЕНДУЕМ:

  • Создание пейлоада на Python для взлома камеры
  • Написание спуфера на Python для подмены почты
  • Как использовать pynput для создания кейлоггера на Python
/* Simple Sniffer in winsock Author : Silver Moon ( m00n.silv3r@gmail.com ) */ #include «stdio.h« #include «winsock2.h« #pragma comment(lib,»ws2_32.lib») //For winsock #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) //this removes the need of mstcpip.h void StartSniffing (SOCKET Sock); //This will sniff here and there void ProcessPacket (char* , int); //This will decide how to digest void PrintIpHeader (char*); void PrintIcmpPacket (char* , int); void PrintUdpPacket (char* , int); void PrintTcpPacket (char* , int); void ConvertToHex (char* , unsigned int); void PrintData (char* , int); typedef struct ip_hdr { unsigned char ip_header_len:4; // 4-bit header length (in 32-bit words) normally=5 (Means 20 Bytes may be 24 also) unsigned char ip_version :4; // 4-bit IPv4 version unsigned char ip_tos; // IP type of service unsigned short ip_total_length; // Total length unsigned short ip_id; // Unique identifier unsigned char ip_frag_offset :5; // Fragment offset field unsigned char ip_more_fragment :1; unsigned char ip_dont_fragment :1; unsigned char ip_reserved_zero :1; unsigned char ip_frag_offset1; //fragment offset unsigned char ip_ttl; // Time to live unsigned char ip_protocol; // Protocol(TCP,UDP etc) unsigned short ip_checksum; // IP checksum unsigned int ip_srcaddr; // Source address unsigned int ip_destaddr; // Source address } IPV4_HDR; typedef struct udp_hdr { unsigned short source_port; // Source port no. unsigned short dest_port; // Dest. port no. unsigned short udp_length; // Udp packet length unsigned short udp_checksum; // Udp checksum (optional) } UDP_HDR; // TCP header typedef struct tcp_header { unsigned short source_port; // source port unsigned short dest_port; // destination port unsigned int sequence; // sequence number — 32 bits unsigned int acknowledge; // acknowledgement number — 32 bits unsigned char ns :1; //Nonce Sum Flag Added in RFC 3540. unsigned char reserved_part1:3; //according to rfc unsigned char data_offset:4; /*The number of 32-bit words in the TCP header. This indicates where the data begins. The length of the TCP header is always a multiple of 32 bits.*/ unsigned char fin :1; //Finish Flag unsigned char syn :1; //Synchronise Flag unsigned char rst :1; //Reset Flag unsigned char psh :1; //Push Flag unsigned char ack :1; //Acknowledgement Flag unsigned char urg :1; //Urgent Flag unsigned char ecn :1; //ECN-Echo Flag unsigned char cwr :1; //Congestion Window Reduced Flag //////////////////////////////// unsigned short window; // window unsigned short checksum; // checksum unsigned short urgent_pointer; // urgent pointer } TCP_HDR; typedef struct icmp_hdr { BYTE type; // ICMP Error type BYTE code; // Type sub code USHORT checksum; USHORT id; USHORT seq; } ICMP_HDR; FILE *logfile; int tcp=0,udp=0,icmp=0,others=0,igmp=0,total=0,i,j; struct sockaddr_in source,dest; char hex[2]; //Its free! IPV4_HDR *iphdr; TCP_HDR *tcpheader; UDP_HDR *udpheader; ICMP_HDR *icmpheader; int main() { SOCKET sniffer; struct in_addr addr; int in; char hostname[100]; struct hostent *local; WSADATA wsa; logfile=fopen(«log.txt«,«w«); if(logfile == NULL) { printf(«Unable to create file.«); } //Initialise Winsock printf(«nInitialising Winsock…«); if (WSAStartup(MAKEWORD(2,2), &wsa) != 0) { printf(«WSAStartup() failed.n«); return 1; } printf(«Initialised«); //Create a RAW Socket printf(«nCreating RAW Socket…«); sniffer = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (sniffer == INVALID_SOCKET) { printf(«Failed to create raw socket.n«); return 1; } printf(«Created.«); //Retrive the local hostname if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) { printf(«Error : %d«,WSAGetLastError()); return 1; } printf(«nHost name : %s n«,hostname); //Retrive the available IPs of the local host local = gethostbyname(hostname); printf(«nAvailable Network Interfaces : n«); if (local == NULL) { printf(«Error : %d.n«,WSAGetLastError()); return 1; } for (i = 0; local->h_addr_list[i] != 0; ++i) { memcpy(&addr, local->h_addr_list[i], sizeof(struct in_addr)); printf(«Interface Number : %d Address : %sn«,i,inet_ntoa(addr)); } printf(«Enter the interface number you would like to sniff : «); scanf(«%d«,&in); memset(&dest, 0, sizeof(dest)); memcpy(&dest.sin_addr.s_addr,local->h_addr_list[in],sizeof(dest.sin_addr.s_addr)); dest.sin_family = AF_INET; dest.sin_port = 0; printf(«nBinding socket to local system and port 0 …«); if (bind(sniffer,(struct sockaddr *)&dest,sizeof(dest)) == SOCKET_ERROR) { printf(«bind(%s) failed.n«, inet_ntoa(addr)); return 1; } printf(«Binding successful«); //Enable this socket with the power to sniff : SIO_RCVALL is the key Receive ALL ;) j=1; printf(«nSetting socket to sniff…«); if (WSAIoctl(sniffer, SIO_RCVALL, &j, sizeof(j), 0, 0, (LPDWORD) &in , 0 , 0) == SOCKET_ERROR) { printf(«WSAIoctl() failed.n«); return 1; } printf(«Socket set.«); //Begin printf(«nStarted Sniffingn«); printf(«Packet Capture Statistics…n«); StartSniffing(sniffer); //Happy Sniffing //End closesocket(sniffer); WSACleanup(); return 0; } void StartSniffing(SOCKET sniffer) { char *Buffer = (char *)malloc(65536); //Its Big! int mangobyte; if (Buffer == NULL) { printf(«malloc() failed.n«); return; } do { mangobyte = recvfrom(sniffer , Buffer , 65536 , 0 , 0 , 0); //Eat as much as u can if(mangobyte > 0) { ProcessPacket(Buffer, mangobyte); } else { printf( «recvfrom() failed.n«); } } while (mangobyte > 0); free(Buffer); } void ProcessPacket(char* Buffer, int Size) { iphdr = (IPV4_HDR *)Buffer; ++total; switch (iphdr->ip_protocol) //Check the Protocol and do accordingly… { case 1: //ICMP Protocol ++icmp; PrintIcmpPacket(Buffer,Size); break; case 2: //IGMP Protocol ++igmp; break; case 6: //TCP Protocol ++tcp; PrintTcpPacket(Buffer,Size); break; case 17: //UDP Protocol ++udp; PrintUdpPacket(Buffer,Size); break; default: //Some Other Protocol like ARP etc. ++others; break; } printf(«TCP : %d UDP : %d ICMP : %d IGMP : %d Others : %d Total : %dr«,tcp,udp,icmp,igmp,others,total); } void PrintIpHeader (char* Buffer ) { unsigned short iphdrlen; iphdr = (IPV4_HDR *)Buffer; iphdrlen = iphdr->ip_header_len*4; memset(&source, 0, sizeof(source)); source.sin_addr.s_addr = iphdr->ip_srcaddr; memset(&dest, 0, sizeof(dest)); dest.sin_addr.s_addr = iphdr->ip_destaddr; fprintf(logfile,«n«); fprintf(logfile,«IP Headern«); fprintf(logfile,« |-IP Version : %dn«,(unsigned int)iphdr->ip_version); fprintf(logfile,« |-IP Header Length : %d DWORDS or %d Bytesn«,(unsigned int)iphdr->ip_header_len,((unsigned int)(iphdr->ip_header_len))*4); fprintf(logfile,« |-Type Of Service : %dn«,(unsigned int)iphdr->ip_tos); fprintf(logfile,« |-IP Total Length : %d Bytes(Size of Packet)n«,ntohs(iphdr->ip_total_length)); fprintf(logfile,« |-Identification : %dn«,ntohs(iphdr->ip_id)); fprintf(logfile,« |-Reserved ZERO Field : %dn«,(unsigned int)iphdr->ip_reserved_zero); fprintf(logfile,« |-Dont Fragment Field : %dn«,(unsigned int)iphdr->ip_dont_fragment); fprintf(logfile,« |-More Fragment Field : %dn«,(unsigned int)iphdr->ip_more_fragment); fprintf(logfile,« |-TTL : %dn«,(unsigned int)iphdr->ip_ttl); fprintf(logfile,« |-Protocol : %dn«,(unsigned int)iphdr->ip_protocol); fprintf(logfile,« |-Checksum : %dn«,ntohs(iphdr->ip_checksum)); fprintf(logfile,« |-Source IP : %sn«,inet_ntoa(source.sin_addr)); fprintf(logfile,« |-Destination IP : %sn«,inet_ntoa(dest.sin_addr)); } void PrintTcpPacket(char* Buffer, int Size) { unsigned short iphdrlen; iphdr = (IPV4_HDR *)Buffer; iphdrlen = iphdr->ip_header_len*4; tcpheader=(TCP_HDR*)(Buffer+iphdrlen); fprintf(logfile,«nn***********************TCP Packet*************************n«); PrintIpHeader( Buffer ); fprintf(logfile,«n«); fprintf(logfile,«TCP Headern«); fprintf(logfile,« |-Source Port : %un«,ntohs(tcpheader->source_port)); fprintf(logfile,« |-Destination Port : %un«,ntohs(tcpheader->dest_port)); fprintf(logfile,« |-Sequence Number : %un«,ntohl(tcpheader->sequence)); fprintf(logfile,« |-Acknowledge Number : %un«,ntohl(tcpheader->acknowledge)); fprintf(logfile,« |-Header Length : %d DWORDS or %d BYTESn« ,(unsigned int)tcpheader->data_offset,(unsigned int)tcpheader->data_offset*4); fprintf(logfile,« |-CWR Flag : %dn«,(unsigned int)tcpheader->cwr); fprintf(logfile,« |-ECN Flag : %dn«,(unsigned int)tcpheader->ecn); fprintf(logfile,« |-Urgent Flag : %dn«,(unsigned int)tcpheader->urg); fprintf(logfile,« |-Acknowledgement Flag : %dn«,(unsigned int)tcpheader->ack); fprintf(logfile,« |-Push Flag : %dn«,(unsigned int)tcpheader->psh); fprintf(logfile,« |-Reset Flag : %dn«,(unsigned int)tcpheader->rst); fprintf(logfile,« |-Synchronise Flag : %dn«,(unsigned int)tcpheader->syn); fprintf(logfile,« |-Finish Flag : %dn«,(unsigned int)tcpheader->fin); fprintf(logfile,« |-Window : %dn«,ntohs(tcpheader->window)); fprintf(logfile,« |-Checksum : %dn«,ntohs(tcpheader->checksum)); fprintf(logfile,« |-Urgent Pointer : %dn«,tcpheader->urgent_pointer); fprintf(logfile,«n«); fprintf(logfile,« DATA Dump «); fprintf(logfile,«n«); fprintf(logfile,«IP Headern«); PrintData(Buffer,iphdrlen); fprintf(logfile,«TCP Headern«); PrintData(Buffer+iphdrlen,tcpheader->data_offset*4); fprintf(logfile,«Data Payloadn«); PrintData(Buffer+iphdrlen+tcpheader->data_offset*4 ,(Size-tcpheader->data_offset*4-iphdr->ip_header_len*4)); fprintf(logfile,«n###########################################################«); } void PrintUdpPacket(char *Buffer,int Size) { unsigned short iphdrlen; iphdr = (IPV4_HDR *)Buffer; iphdrlen = iphdr->ip_header_len*4; udpheader = (UDP_HDR *)(Buffer + iphdrlen); fprintf(logfile,«nn***********************UDP Packet*************************n«); PrintIpHeader(Buffer); fprintf(logfile,«nUDP Headern«); fprintf(logfile,« |-Source Port : %dn«,ntohs(udpheader->source_port)); fprintf(logfile,« |-Destination Port : %dn«,ntohs(udpheader->dest_port)); fprintf(logfile,« |-UDP Length : %dn«,ntohs(udpheader->udp_length)); fprintf(logfile,« |-UDP Checksum : %dn«,ntohs(udpheader->udp_checksum)); fprintf(logfile,«n«); fprintf(logfile,«IP Headern«); PrintData(Buffer,iphdrlen); fprintf(logfile,«UDP Headern«); PrintData(Buffer+iphdrlen,sizeof(UDP_HDR)); fprintf(logfile,«Data Payloadn«); PrintData(Buffer+iphdrlen+sizeof(UDP_HDR) ,(Sizesizeof(UDP_HDR) — iphdr->ip_header_len*4)); fprintf(logfile,«n###########################################################«); } void PrintIcmpPacket(char* Buffer , int Size) { unsigned short iphdrlen; iphdr = (IPV4_HDR *)Buffer; iphdrlen = iphdr->ip_header_len*4; icmpheader=(ICMP_HDR*)(Buffer+iphdrlen); fprintf(logfile,«nn***********************ICMP Packet*************************n«); PrintIpHeader(Buffer); fprintf(logfile,«n«); fprintf(logfile,«ICMP Headern«); fprintf(logfile,« |-Type : %d«,(unsigned int)(icmpheader->type)); if((unsigned int)(icmpheader->type)==11) { fprintf(logfile,« (TTL Expired)n«); } else if((unsigned int)(icmpheader->type)==0) { fprintf(logfile,« (ICMP Echo Reply)n«); } fprintf(logfile,« |-Code : %dn«,(unsigned int)(icmpheader->code)); fprintf(logfile,« |-Checksum : %dn«,ntohs(icmpheader->checksum)); fprintf(logfile,« |-ID : %dn«,ntohs(icmpheader->id)); fprintf(logfile,« |-Sequence : %dn«,ntohs(icmpheader->seq)); fprintf(logfile,«n«); fprintf(logfile,«IP Headern«); PrintData(Buffer,iphdrlen); fprintf(logfile,«UDP Headern«); PrintData(Buffer+iphdrlen,sizeof(ICMP_HDR)); fprintf(logfile,«Data Payloadn«); PrintData(Buffer+iphdrlen+sizeof(ICMP_HDR) , (Sizesizeof(ICMP_HDR) — iphdr->ip_header_len*4)); fprintf(logfile,«n###########################################################«); } /* Print the hex values of the data */ void PrintData (char* data , int Size) { char a , line[17] , c; int j; //loop over each character and print for(i=0 ; i < Size ; i++) { c = data[i]; //Print the hex value for every character , with a space. Important to make unsigned fprintf(logfile,« %.2x«, (unsigned char) c); //Add the character to data line. Important to make unsigned a = ( c >=32 && c <=128) ? (unsigned char) c : .; line[i%16] = a; //if last character of a line , then print the line — 16 characters in 1 line if( (i!=0 && (i+1)%16==0) || i == Size1) { line[i%16 + 1] = ; //print a big gap of 10 characters between hex and characters fprintf(logfile ,« «); //Print additional spaces for last lines which might be less than 16 characters in length for( j = strlen(line) ; j < 16; j++) { fprintf(logfile , « «); } fprintf(logfile , «%s n« , line); } } fprintf(logfile , «n«); }

Понравилась статья? Поделить с друзьями:
  • Как написать свой скрипт для роблокс читы
  • Как написать свой ник красивым шрифтом
  • Как написать свой скрипт для samp
  • Как написать свой мюзикл
  • Как написать свой сказ