Pull to refresh

Перехват и анализ сетевого трафика с помощью библиотеки pcap

Reading time5 min
Views10K

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

  • посещение нежелательных сайтов;

  • скачивание файлов, потенциально зараженных;

  • просмотр видеозаписей и скачивание изображений;

  • общение в соц. сетях, онлайн-игры.

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

Packet Capture позволяет создавать программы, анализирующие данные, поступающие на сетевую карту компьютера, и (в более новых версиях), передающие пакеты в сеть. В различных подходах к мониторингу и тестированию сети, так называемых программах-снифферах (англ. sniff - нюхать), используют эту библиотеку. Она предназначена для использования совместно с языками C/C++, а для работы с библиотекой на других языках используют обертки. Для Unix-подобных систем - библиотека libpcap, а для Microsoft Windows - WinPcap.

В качестве примера, создадим простейшее консольное приложение, демонстрирующее работу библиотеки pcap под ОС Microsoft Windows, которое будет состоять из двух частей: отправителя, формирующего каждый байт сообщения, и получателя.

Отправитель

1. Определим IP-адрес отправителя. В действительности компьютер может входить в несколько подсетей одновременно и для каждой из них иметь отдельный IP-адрес. Для простоты кода ограничимся  единственным IP.

u_char* learn_IP()
{
	addrinfo hnts, *pRes;
	char hostName[1024];
	u_char* ip_addr = NULL;
	if (gethostname(hostName, sizeof hostName) == 0)
	{
	memset(&hnts, 0, sizeof hnts);
	hnts.ai_family = AF_INET; hnts.ai_socktype = SOCK_DGRAM; hnts.ai_flags = AI_PASSIVE;
	if (getaddrinfo(hostName, NULL, &hnts, &pRes) == 0)
	{
	struct addrinfo* res;
	char buffer[INET_ADDRSTRLEN];
	for (res = pRes; (res != NULL) && (res->ai_family != AF_INET); res = res->ai_next);
	ip_addr = (u_char* )inet_ntop(AF_INET, &((struct sockaddr_in *)res->ai_addr)->sin_addr, buffer, INET_ADDRSTRLEN);
	freeaddrinfo(pResults);
	}
	WSACleanup();
	}
	return ip_addr;
}

Далее специальная функция определяет MAC-адрес сетевой карты компьютера, отправляющего сообщение.

u_char* learn_MAC()
{
IP_ADAPTER_INFO ip_ainf[128];
PIP_ADAPTER_INFO pip_ainf = ip_ainf;
u_long bufLen = sizeof(ip_ainf);
GetAdaptersInfo(ip_ainf, &bufLen);
u_char* mac_addr = pip_ainf->Address;
return mac_addr;
}

2. В Интернет-заголовке необходимо указать MAC-адреса отправителя и получателя, которые определяются посредством ARP-запроса по известному IP-адресу. Если отправитель и получатель находятся в разных локальных сетях, то ARP-запросом требуется определить MAC-адрес маршрутизатора сети отправителя и его же указывать в сообщении. Если отправитель и получатель в одной сети, то конечного адресата. В любом случае ARP-пакет шлется в широковещательном режиме.

IPAddr DestIp = 0, SrcIp = 0;        		
static u_long MacAddr[2];       		
u_long PhysAddrLen;  	 		
SrcIpAddr = inet_addr(SrcIpString);   // IP-адрес отправителя
DestIpAddr = inet_addr(destIpString);// IP-адрес маршрутизатора или конечного получателя
memset(&MacAddr, 0xff, sizeof(MacAddr)); // Указатель на широковещательную рассылку
if (SendARP(DestIpAddr, SrcIpAddr, &MacAddr, &PhysAddrLen) == NO_ERROR)
		*bPhysAddr = (BYTE *) & MacAddr;   // Искомый MAC-адрес

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

pcap_if_t *alldevs, *dev;
char errbuf[PCAP_ERRBUF_SIZE];
int inum, i = 0;	
pcap_findalldevs(&alldevs, errbuf);
scanf_s("%dev", &inum);
for (dev = alldevs, i = 0; i < inum - 1; dev = dev->next, i++);

Открываем его.

pcap_t *adhandle;
adhandle = pcap_open_live(dev->name, 65536, 0, 1000, errbuf);

4. Далее собираем воедино пакет для отправки. Определяем его Интернет, IP и UDP-заголовки, канального, сетевого и транспортного уровней. Особого внимания заслуживает функция вычисления контрольной суммы для IP и UDP-заголовков.

u_short checksum(u_char *buffer, int size)
{
	u_long chksum = 0;
	while (size > 1)
	{
		chksum += *buffer++;
		size -= sizeof(u_short);
	}
	if (size)
		chksum += *(u_char*)buffer;
	chksum = (chksum >> 16) + (chksum & 0xffff);
	chksum += (chksum >> 16);
	return (u_short)(~chksum);
}

Затем пакет отправляется посредством стандартной функции библиотеки pcap.

pcap_sendpacket(adhandle, packet, 43);

adhandle – ранее определенный указатель на интерфейс отправки пакета, packet – сам пакет, последний параметр – размер пакета в байтах.

Получатель

1. С помощью стандартных функций pcap определяем интерфейс, который будет использоваться для перехвата сетевого трафика и открываем его (по аналогии с отправкой пакетов). Далее специальной функцией библиотеки pcap настраиваем и устанавливаем фильтр пакетов.

char pckfilter[] = "ip dst host 192.168.1.1";
struct bpf_program fcode;
if (d->addresses != NULL)
	netmask = ((struct sockaddr_in *)(d->addresses->netmask))-> 			sin_addr.S_un.S_addr;
else netmask = 0xffffff;
pcap_compile(adhandle, &fcode, pckfilter, 1, netmask);
pcap_setfilter(adhandle, &fcode);

2.Далее запускается функция, которая в бесконечном цикле обрабатывает перехваченные пакеты.

void packet_handler(u_char *param, const struct pcap_pkthdr    *pkt_header, const u_char *pkt_data)

а) Для начала из заголовка функции выуживается время передачи пакета и преобразуется в удобный для чтения формат.

time_t local_tv_sec = header->ts.tv_sec;
char strtime[16];
struct tm ltime;
localtime_s(&local_tv_sec, &ltime);
strftime(strtime, sizeof strtime, "%H:%M:%S", &ltime);

б) Из всего заголовка выделяется та часть, которая соответствует IP-протоколу сетевого уровня. Проверяется контрольная сумма, и в случае ее совпадения сообщение обрабатывается вверх по стеку TCP/IP, иначе – сообщение игнорируется.

ip_header *ip_hd;
ip_hd = (ip_header *)(pkt_data + 14);
if (!Count_ip_check_sum(ip_hd))
return;

в) Определяем UDP-заголовок принятого пакета и проверяем его контрольную сумму. Для этого формируем псевдозаголовок, состоящий из IP-адресов отправителя и получателя, поля, определяющего протокол нижележащего уровня, и длины UDP-заголовка и данных. Контрольная сумма вычисляется по       UDP-заголовку плюс псевдозаголовок. От нее зависит дальнейшая судьба пакета: принять или отбросить.

u_int ip_len = (ip_hd->ver_ihl & 0xf) * 4;
udp_header *udp_hd;
udp_hd = (udp_header *)((u_char*)ip_hd + ip_len);
u_char* pshd = new u_char[12];	// псевдозаголовок
pshd[0] = ip_hd->src_ip.byte1; pshd[1] = ip_hd->src_ip.byte2; 
pshd[2] = ip_hd->src_ip.byte3; pshd[3] = ip_hd->src_ip.byte4;
pshd[4] = ip_hd->dest_ip.byte1; pshd[5] = ip_hd->dest_ip.byte2; 
pshd[6] = ip_hd->dest_ip.byte3; pshd[7] = ip_hd->dest_ip.byte4;
pshd[8] = 0x00; pshd[9] = 0x11; pshd[10] = 0x00; pshd[11] = 0x09;
if (!Count_udp_check_sum(udp_hd, pshd))
	return;

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

u_short sendport = ntohs(udp_hd->src_port);
u_short destport = ntohs(udp_hd->dest_port);
printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
     ip_hd->src_ip.byte1, ip_hd->src_ip.byte2, ip_hd->src_ip.byte3, ip_hd->src_ip.byte4, sendport,
     ip_hd->dest_ip.byte1, ip_hd->dest_ip.byte2, ip_hd->dest_ip.byte3, ip_hd->dest_ip.byte4, destport);

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

Tags:
Hubs:
-3
Comments4

Articles

Change theme settings