Анализатор сетевого трафика::Журнал СА 1.2002
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Подписка
Архив номеров
Где купить
Наука и технологии
Авторам
Рекламодателям
Контакты
   

  Опросы
  Статьи

Дата-центры  

Дата-центры: есть ли опасность утечки данных?

Российские компании уже несколько лет испытывают дефицит вычислительных мощностей. Рост числа проектов,

 Читать далее...

Событие  

В банке рассола ждет сисадмина с полей фрактал-кукумбер

Читайте впечатления о слете ДСА 2024, рассказанные волонтером и участником слета

 Читать далее...

Организация бесперебойной работы  

Бесперебойная работа ИТ-инфраструктуры в режиме 24/7 Как обеспечить ее в нынешних условиях?

Год назад ИТ-компания «Крок» провела исследование «Ключевые тренды сервисного рынка 2023». Результаты

 Читать далее...

Книжная полка  

Читайте и познавайте мир технологий!

Издательство «БХВ» продолжает радовать выпуском интересных и полезных, к тому же прекрасно

 Читать далее...

СУБД PostgreSQL  

СУБД Postgres Pro

Сертификация по новым требованиям ФСТЭК и роль администратора без доступа к данным

 Читать далее...

Критическая инфраструктура  

КИИ для оператора связи. Готовы ли компании к повышению уровня кибербезопасности?

Похоже, что провайдеры и операторы связи начали забывать о требованиях законодательства

 Читать далее...

Архитектура ПО  

Архитектурные метрики. Качество архитектуры и способность системы к эволюционированию

Обычно соответствие программного продукта требованиям мы проверяем через скоуп вполне себе понятных

 Читать далее...

Как хорошо вы это знаете  

Что вам известно о разработках компании ARinteg?

Компания ARinteg (ООО «АРинтег») – системный интегратор на российском рынке ИБ –

 Читать далее...

Графические редакторы  

Рисование абстрактных гор в стиле Paper Cut

Векторный графический редактор Inkscape – яркий представитель той прослойки open source, с

 Читать далее...

День сисадмина  

Учите матчасть! Или как стать системным администратором

Лето – время не только отпусков, но и хорошая возможность определиться с профессией

 Читать далее...

День сисадмина  

Живой айтишник – это всегда движение. Остановка смерти подобна

Наши авторы рассказывают о своем опыте и дают советы начинающим системным администраторам.

 Читать далее...

Виртуализация  

Рынок решений для виртуализации

По данным «Обзора российского рынка инфраструктурного ПО и перспектив его развития», сделанного

 Читать далее...

Книжная полка  

Как стать креативным и востребованным

Издательский дом «Питер» предлагает новинки компьютерной литературы, а также книги по бизнесу

 Читать далее...

Книжная полка  

От создания сайтов до разработки и реализации API

В издательстве «БХВ» недавно вышли книги, которые будут интересны системным администраторам, создателям

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 6241
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 6947
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

12.03.2018г.
Просмотров: 4233
Комментарии: 0
Глубокое обучение с точки зрения практика

 Читать далее...

12.03.2018г.
Просмотров: 3015
Комментарии: 0
Изучаем pandas

 Читать далее...

12.03.2018г.
Просмотров: 3813
Комментарии: 0
Программирование на языке Rust (Цветное издание)

 Читать далее...

19.12.2017г.
Просмотров: 3829
Комментарии: 0
Глубокое обучение

 Читать далее...

19.12.2017г.
Просмотров: 6324
Комментарии: 0
Анализ социальных медиа на Python

 Читать далее...

19.12.2017г.
Просмотров: 3174
Комментарии: 0
Основы блокчейна

 Читать далее...

19.12.2017г.
Просмотров: 3468
Комментарии: 0
Java 9. Полный обзор нововведений

 Читать далее...

16.02.2017г.
Просмотров: 7285
Комментарии: 0
Опоздавших не бывает, или книга о стеке

 Читать далее...

17.05.2016г.
Просмотров: 10652
Комментарии: 0
Теория вычислений для программистов

 Читать далее...

30.03.2015г.
Просмотров: 12371
Комментарии: 0
От математики к обобщенному программированию

 Читать далее...

18.02.2014г.
Просмотров: 14008
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

 Читать далее...

13.02.2014г.
Просмотров: 9132
Комментарии: 0
Читайте, размышляйте, действуйте

 Читать далее...

12.02.2014г.
Просмотров: 7084
Комментарии: 0
Рисуем наши мысли

 Читать далее...

10.02.2014г.
Просмотров: 5395
Комментарии: 3
Страна в цифрах

 Читать далее...

18.12.2013г.
Просмотров: 4624
Комментарии: 0
Большие данные меняют нашу жизнь

 Читать далее...

18.12.2013г.
Просмотров: 3434
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

 Читать далее...

04.12.2013г.
Просмотров: 3165
Комментарии: 0
Паутина в облаках

 Читать далее...

03.12.2013г.
Просмотров: 3406
Комментарии: 0
Рецензия на книгу «MongoDB в действии»

 Читать далее...

02.12.2013г.
Просмотров: 3031
Комментарии: 0
Не думай о минутах свысока

 Читать далее...

Друзья сайта  

 Анализатор сетевого трафика

Архив номеров / 2002 / Выпуск №1 (1) / Анализатор сетевого трафика

Рубрика: Сети /  Сети

ВЛАДИМИР МЕШКОВ

Анализатор сетевого трафика

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

Обзор технологии Ethernet

Для начала вспомним, как функционирует сеть Ethernet (те из вас, кто уже знаком с данным вопросом, могут пропустить этот параграф). IP-пакеты (дейтаграммы), источником которых является приложение пользователя, инкапсулируются в Ethernet-кадры (пакеты Ethernet-протокола, передаваемые в сети). Каждый кадр содержит исходный IP-пакет и другую информацию, необходимую для доставки его адресату, в частности, 6-ти байтовый Ethernet-адрес (MAC-адрес) назначения, который при помощи протокола ARP ставится в соответствие IP-адресу назначения. Таким образом, сформированный кадр, содержащий пакет, начинает свое путешествие от хоста-отправителя к хосту-получателю через кабельное соединение.

На уровне протокола Ethernet маршрутизация отсутствует. Другими словами, кадр, отправленный хостом-отправителем, не попадает напрямую хосту-получателю, а будет доступен для всех хостов, объединенных в сеть. Каждая сетевая карта принимает кадр и считывает из него первые 6 байт. Эти байты содержат MAC-адрес хоста-получателя, но только одна карта в сети определит его как свой собственный и передаст кадр для дальнейшей обработки сетевому драйверу. Сетевой драйвер проверит поле «Тип протокола» заголовка кадра и, основываясь на этом значении, направит инкапсулированный пакет соответствующей приемной функции данного протокола. В большинстве случаев это протокол IP. Приемная функция изымает IP заголовок из принятой дейтаграммы и передает инкапсулированное сообщение соответствующему модулю протокола транспортного уровня (например, TCP или UDP). Эти протоколы, в свою очередь, обрабатывают свои заголовки и передают данные протоколам прикладного уровня. В течение этой «экскурсии» по различным уровням сетевого стека исходный пакет теряет все служебные поля протоколов и, в конце концов, данные, передаваемые в пакете, принимаются пользовательским приложением.

Пакетные сокеты

При создании сокета стандартным вызовом socket (int domain, int type, int protocol) параметр domain определяет коммуникационный домен, в котором будет использоваться сокет. Обычно используются значения PF_UNIX для соединений, ограниченных локальной машиной, и PF_INET для соединений, базирующихся на протоколе IPv4. Аргумент type определяет тип создаваемого сокета и имеет несколько значений. Значение SOCK_STREAM указывается при создании сокета для работы в режиме виртуальных соединений (протокол TCP), а значение SOCK_DGRAM – для работы в режиме пересылки дейтаграмм (протокол UDP). Последний параметр protocol определяет используемый протокол (в соответствии с IEEE 802.3).

В версиях LINUX, начиная с 2.2, появился новый тип сокетов – пакетные сокеты. Пакетные сокеты используются для отправления и приема пакетов на уровне драйверов устройств. Сокеты данного типа создаются вызовом socket (SOCK_PACKET, int type, int protocol). Параметр type равен или SOCK_RAW, или SOCK_DGRAM.

Пакеты типа SOCK_RAW передаются драйверу устройства и принимаются от него без всяких изменений данных пакета.

SOCK_DGRAM работает на более высоком уровне. Физический заголовок (MAC-адрес) удаляется перед тем, как пакет отправляется на обработку пользователю.

Пример реализации

Итак, приступим непосредственно к созданию анализатора. Для этого нам необходимо:

  • определить необходимые переменные;
  • получить параметры сетевого интерфейса (IP-адрес, маску подсети, номер подсети, размер MTU, индекс (номер) интерфейса);
  • создать пакетный сокет и привязать его к определенному интерфейсу;
  • принять сетевой пакет и проанализировать его структуру. Этим будет заниматься главная функция программы.

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

Переменные

Необходимые заголовочные файлы:

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <errno.h>

#include <linux/if.h

#include <linux/if_ether.h>

#include <linux/in.h>

#include <linux/ip.h>

Структура для хранения принятого IP – пакета:

struct ip_packet {

    struct iphdr ip;

    char *ip_data;

} ip_pack;

Cтруктура для хранения параметров сетевого интерфейса:

struct ifreq     ifr;

Структура для получения адресной информации:

struct sockaddr_in s

Структура для хранения заголовка IP-пакета:

struct iphdr     *ip;

Структура для хранения заголовка Ethernet-кадра:

struct ethhdr    eth;

Вспомогательная структура, содержащая параметры интерфейса:

struct ifparam {

    u_long ip;

    u_long mask;

    u_long subnet;

    int mtu;

    int index;

} ifp;    

int e0_r,  – дескриптор сокета

rec;  – размер принятого пакета в байтах

char  *buff;  – буфер для приема пакетов

Переменные разместим в файле ip.h.

Функция получения параметров сетевого интерфейса

#include "ip.h"

#include <sys/ioctl.h>

int getifconfig (struct ifreq *ifr, char *intf, struct ifparam *ifp)

{

    int fd;      – дескриптор сокета

Создадим сокет:

if (( fd= socket (AF_INET, SOCK_DGRAM, 0)) <0 ) {

    perror ( "socket" );

    return ( - 1 );

}

Скопируем имя интерфейса в поле ifr_name структуры ifr:

    sprintf (ifr->ifr_name, "%s", intf);

Получим IP-адрес интерфейса:

    if (ioctl (fd, SIOCGIFADDR, ifr) <0 ) {

           perror ("ioctl");

           return (-1);

    }

memset(&s, 0, sizeof (struct sockaddr_in));

memcpy(&s, &ifr->ifr_addr, sizeof (struct sockaddr));

memcpy(&ifp-ip, &to.sin_addr.s_addr, sizeof (u_long));

Получим маску подсети:

    if (ioctl (fd, SIOCGIFNETMASK, ifr) <0 ) {

           perror ("ioctl");

           return (-1);

    }

memset(&s, 0, sizeof (struct sockaddr_in));

memcpy(&s, &ifr->ifr_netmask, sizeof (struct sockaddr));

memcpy(&ifp-mask, &to.sin_addr.s_addr, sizeof (u_long));

Получим номер подсети:

    ifp->sunbet = check_subnet(ifp->mask, ifp->ip);

Получим размер MTU:

    if (ioctl (fd, SIOCGIFMTU, ifr) <0 ) {

           perror ("ioctl");

           return (-1);

    }

    ifp -> mtu = ifr -> ifr_mtu;

Получим индекс (номер) интерфейса:

    if ( ioctl (fd, SIOCGIFINDEX, ifr) <0 ) {

           perror ("ioctl");

           return (-1);

    }

    ifp -> index = ifr -> ifr_ifindex;

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

    if ( ioctl (fd, SIOCGIFFLAGS, ifr) <0 ) {

           perror ("ioctl");

           close (fd);

           return (-1);

    }

Установим флаг неразборчивого режима:

    ifr -> ifr_flags |= IFF_PROMISC;

Установим новое значение флагов интерфейса:

    if ( ioctl (fd, SIOCSIFFLAGS, ifr) <0 ) {

           perror ("ioctl");

           close (fd);

           return (-1);

    }

    return 1;

}

Параметрами функции getifconfig являются структура struct ifreq *ifr, имя интерфейса char *intf и структура struct ifparam *ifp. Номер подсети определяется при помощи вызова функции check_subnet, приведенной ниже.

Установкой флага интерфейса IFF_PROMISC мы добиваемся приема всех пакетов, даже если они не адресованы нашему хосту.

Функция определения номера подсети

 BITS 32

    GLOBAL check_subnet

SECTION .text

check_subnet:

    push ebp            – сохраним адрес возврата из функции

    mov ebp, esp

    mov edx, [ebp+8]    – первый параметр - маска подсети

    mov eax, [ebp+12]   – второй параметр - IP адрес

    mov cx, 32          – число разрядов в IP адресе в формате IPv4

    push cx             – сохраним значение в стеке

    xor esi, esi        – обнулим счетчик

.label

    bt edx, esi         – сканируем маску в поисках 1

    jnc .msk            – выход из цикла при совпадении

    inc esi             – инкремент счетчика

    loop .label         – продолжить поиск

.mask

    pop cx              – извлечь ранее сохраненное значение из стека

    sub cx, si          – число разрядов в адресе, отведенных под хостовую часть

    shl eax, cl         – логический сдвиг на это значение

    mov esp, ebp

    pop ebp             – восстановим стек

    ret                 – возврат из функции

Функция check_subnet сканирует первый переданный параметр (маску подсети) до первого появления 1 и запоминает эту позицию. Эта позиция соответствует числу разрядов, отведенных в адресе на сетевую составляющую. Далее, во втором переданном параметре (адресе) происходит логический сдвиг на число разрядов, отведенных под адрес хоста. Таким образом, у нас остается только сетевая составляющая, которая и является адресом подсети.

Функция для создания пакетного сокета.

Заголовочные файлы:

#include <sys/types.h>

#include <sys/socket.h>

#include <errno.h >

#include <linux/if_packet.h>

#include <linux/if_ether.h> 

int getsock_recv (int index)

{

    int    fd;

Структура для хранения адресной информации об интерфейсе (см. файл <linux/if_packet.h>):

    struct sockaddr_ll  s_ll;

Создадим пакетный сокет:

    if (( fd= socket (SOCK_PACKET, SOCK_RAW, htons (ETH_P_ALL) )) <0 ) {

    perror ( "socket" );

    return ( - 1 );

}

Выделим память для структуры struct sockaddr_ll s_ll:

    memset (&s_ll, 0, sizeof (struct sockaddr_ll));

Заполним поля структуры s_ll необходимыми значениями:

    s_ll.sll_family = PF_PACKET;            – тип сокета

    s_ll.sll_protocol = htons (ETH_P_ALL);  – тип принимаемого протокола

    s_ll.sll_ifindex = index;               – номер интерфейса

    s_ll.sll_pkttype = PACKET_HOST;          – тип пакета (для локальной машины)

Привяжем сокет к интерфейсу:

    if ((bind (fd, (struct sockaddr *) &s_ll, sizeof (struct sockaddr_ll)) <0 ) {

           perror ("bind");

           close (fd);

           return (-1);

    }

Возвратим дескриптор сокета в вызывающую функцию:

    return (fd);

}

Функция getsock_recv принимает в качестве параметра индекс интерфейса и возвращает дескриптор пакетного сокета. Значение поля protocol в системном вызове socket равно htons (ETH_P_ALL). Это означает, что мы будем принимать пакеты всех протоколов. Все входящие пакеты будут передаваться пакетному сокету до того, как они будут переданы протоколам, реализованным в ядре. Список возможных протоколов приведен в файле <linux/if_ether.h>.

Для получения пакетов только с определенного интерфейса используется функция bind: таким образом мы соединяем пакетный сокет с интерфейсом, адрес которого указан в структуре struct sockaddr_ll. Если этого не сделать, мы будем получать пакеты со всех сетевых интерфейсов, которые в данный момент активны.

Главная функция

#include "ip.h"

int main ()

{

Получим параметры сетевого интерфейса:

    memset (&ifr, 0, sizeof (struct ifreq));

    if (getifconfig (&ifr, "eth0", &ifp) <0 ) {

           perror ("getifconfig");

           exit (1);

    }

Выделим память:

    buff = (char *) malloc (ifp.mtu + 18);

    memset(&ip, 0, sizeof (struct ip_packet));

ip_pack.ip_data = (char *) malloc ( ifp.mtu - sizeof (struct iphdr));

    ip=(struct iphdr *)&ip_pack.ip;

Получим дескриптор пакетного сокета:

    if ((e0_r = getsock_recv (ifp.index)) <0 ) {

           perror ("getsock_recv");

           exit(1);

    }

Цикл приема пакетов:

    for (;;) {

Обнулим буфер:

    bzero (buff, ifp.mtu+18);

    rec = 0;

Принять пакет:

           rec=recvfrom (e0_r, (char *)buff, ifp.mtu+18, 0, NULL, NULL);

           if (rec<0) {

                 perror ("recvfrom");

                 exit(1);

           }

Число принятых байт (длина принятого пакета):

           printf (" rec = %d ", rec);

Первые 12 байт в принятом буфере содержат MAC – адреса отправителя и получателя. Заполним структуру struct ethhdr eth адресной информацией:

           memcpy ((char *) &eth, buff, 12);

По смещению 14 в данном буфере расположены данные Ethernet-кадра – IP-пакет:

           memcpy ((char *)&ip.pack, (buff + 14), ifp.mtu );

Проведем анализ принятого Ethernet-кадра.

           if ((ip -> version) !=4) continue;             – версия IP-протокола

MAC-адрес отправителя:

printf (" %.2x: %.2x: %.2x: %.2x: %.2x: %.2x -> ",

eth.h_source[0], eth.h_source[1], eth.h_source[2],

eth.h_source[3], eth.h_source[4], eth.h_source[5]);

MAC-адрес получателя:

printf (" %.2x: %.2x: %.2x: %.2x: %.2x: %.2x",

eth.h_dest[0], eth.h_ dest[1], eth.h_ dest[2],

eth.h_ dest[3], eth.h_ dest[4], eth.h_ dest[5]);

 

           printf ("%d ", ip -> ihl);                         – длина заголовка IP-пакета

           printf ("%d ", ntohs (ip -> tot_len));               – длина всего пакета

           printf ("%d ", ip -> protocol);                    – протокол верхнего уровня

           printf ("%s -> ", inet_ntoa (ip -> saddr));      – адрес источника

           printf ("%s \n", inet_ntoa (ip -> daddr));            – адрес назначения

}

    return (1);

}

Прием пакетов осуществляется с помощью функции recvfrom. Эта функция принимает данные через дескриптор e0_r. Принятое сообщение копируется в структуру ip_pack.

В принятом пакете первым следует заголовок Ethernet-кадра. По смещению 14 расположен IP-пакет. Поле «Версия» указывает тип данного пакета. Для IPv4-пакета данное поле содержит значение 4 в двоичной форме. Значение длины заголовка лежит в диапазоне между 20 и 60 байтами и находится в поле «Длина заголовка». Поле «Протокол» содержит идентификацию протокола следующего, более высокого уровня, содержащегося в разделе данных (т.е. в теле сообщения) данного IP-пакета. В документе RFC 1700 перечислены все значения, которые могут содержаться в поле «Протокол» в заголовке IP-пакета. Поле «Адрес источника» и поле «Адрес назначения» содержат соответственно IP-адрес отправителя пакета и IP-адрес предполагаемого получателя.

Дальнейшая обработка принятого пакета зависит от полей «Длина заголовка» и «Протокол». В принятом буфере по смещению, указанном в поле «Длина заголовка» (с учетом заголовка кадра Ethernet) будет расположен заголовок протокола следующего уровня. Его анализ аналогичен вышеприведенному анализу заголовка IP.

Для получения исполняемого модуля создадим Makefile следующего содержания:

# Компилятор С

    CC = gcc

# Компилятор ассемблера

    NASM = nasm

# Имя исполняемого модуля

    name = ip

    IP = ip.o check_snet.o getsock_recv.o getifconf.o

    $(name): $(IP)

           $(CC) -g -o $(name) $(IP)

    ip.o: ip.c

           $(CC) -c ip.c

    check_snet.o: check_snet.asm

           $(NASM) -f elf check_snet.asm

    getsock_recv.o: getsock_recv.c

           $(CC) -c getsock_recv.c

    getifconf.o: getifconf.c

           $(CC) -c getifconf.c

clean:

    rm -f *.o

В файле ip.c находится главная функция программы. Командой make мы получим исполняемый модуль ip. Команда make clean удалит все объектные файлы. Результаты работы программы будут отображаться на консоли. Иногда это не совсем удобно, поэтому, немного модифицировав программу, результаты можно сохранять в файле.

Компиляцию производим с ключем -g для возможности последующей отладки. Надеюсь, что читателю это не понадобится, но все-таки хочу показать простой прием поиска неисправностей в программе (не только в этой). Иногда программа, хотя и компилируется без ошибок, при запуске выдает сообщение Segmentation fault и завершается. Для нашего примера, если программа работает некорректно, запустите исполняемый файл ip на отладку командой gdb ip. В командной строке отладчика наберите run и изучите информацию, которую выдаст отладчик. Он укажет место, где программа аварийно завершилась, и Вам останется только устранить неисправность. Если все в порядке, то перекомпилируйте программу с ключем -s.

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

Литература:

  1. Сэтчелл С., Клиффорд Х. LINUX IP Stacks в комментариях: Пер. с англ. - К.: Издательство «ДиаСофт», 2001 г. – 288 с.
  2. Теренс Чан. Системное программирование на С++ для UNIX: Пер. с англ. - К.: Издательская группа BHV, 1999 г. – 592 с.
  3. Gianluca Insolvibile. Kernel Korner: The Linux Socket Filter: Sniffing Bytes over the Network: http://www.linuxjournal.com/article.php?sid=4659


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-45
E-mail: sa@samag.ru