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

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

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

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

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

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

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

    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 в задачах контроля и анализа сетевого трафика. Оптимизация использования сетевых ресурсов является обязанностью системного администратора, и данная библиотека является хорошим помощником в этом.

    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 3

      +2

      Зря указан Python. Это все же С)

        0
        у вас куча UB в примерах
          0
          Под С++ Builder packet_handler использовать не получится. Линкер ругается. Можно определить ее как static в классе, но тогда нет доступа к объектам класса. Лучше заменить эту функцию на pcap_next_ex.

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

          Самое читаемое