| RSS



Меню

Bookmark and Share


Статистика
Ваш IP: 3.15.183.119
Вы используете: v





Сейчас на сайте:

Тех поддержка ->

Облако тэгов
ОС видио Tor Обратная сторона антенна 4.6 PHP Эксплоит Windows Server 2008 qip Virtual chroot kernel proc sysctl tune FreeBSD bridge Boot Disk Bluetooth GEO game directx Emulator Python Shell DDoS червь Conficker вирус троян Лаборатория Касперского пиратство apple iPhone ИТ-отрасль Щеголев Microsoft экономический кризис Twitter социальная сеть анонимность Лицензия Open Source ASP.NET MVC уязвимость MySQL база данных файлообмен закон франция пират Skype мобильный Deutsche Telekom Хакер киберпреступник Trend Micro кибератака Германия робот утечка данных персональные данные ноутбук интернет Китай цензура ядро Linux Торвальдс Windows Vista Acer Linux патент браузер Firefox Internet Explorer Opera Net Applications Safari Intel Linux Foundation Moblin Oracle патч банкомат кардер HSM IBM X-Force Cofee сша кибервойна Эстония Dell ИТ-специалист хакерские атаки Pirate Bay контроль кибербезопасность язык программирования The Pirate Bay Пиратская партия утечка информации приговор Mozilla Chrome безопасность Госдума СМИ Windows 8 Баллмер взлом Пентагон ботнет Украина Facebook Cisco cloud Windows XP нетбук торрент музыка биометрический nokia ФБР IP-адрес CIPAV Comcast sms RSA java Google CAPTCHA Symantec спам конфиденциальная информация инсайдер Perimetrix антивирус тест Anti-Malware Windows 7 операционная система Windows провайдер авторское право RapidShare UNIX свиной грипп шантаж дети EFF BluWiki копирайт экстремизм Panda Security cloud computing McAfee Cybercrime Response Unit Bottle Domains HTTPS ICANN студент шпионское ПО Норвегия школьник New York Times XSS YouTube Warner Music кибершпионаж КНДР Ubuntu свободное ПО AMD ATI касперский Россия РФ сервер хостинг фальшивый антивирус Comodo CA Wi-Fi D-Link суд пароль блог фишинг Одноклассники медведев контрафакт мошенник штраф Sony GPS по Gumblar JAVASCRIPT хакеры вредоносное ПО Yahoo ФАС компьютер Софт MPAA кибероружие PandaLabs Red Hat Минкомсвязи сбой ASUSTeK Computer мошенничество Доктор Веб ВКонтакте Cyber-Arc исходный код PCI DSS МВД фильтр порнография BREIN свобода слова Казахстан GEMA Autodesk сисадмин Gmail кредитная карта кибермошенник LiveJournal шифрование криптография Deep Purple банк нанотехнологии Wikipedia zero-day ColdFusion выборы кража данных DNS BIND Android BASIC атака Black Hat Mac OS X Click Forensics Clampi домен фсб Прокуратура Уголовное дело icq Barrelfish киберпреступность Sophos AT&T ошибка Electa Gamma Knife OpenBSD DARPA военные Сайт Visual Studio 2010 .NET Framework 4 Chrome OS электронная почта турция конференция спамер FTC полиция российская ОС Koobface Великобритания БЕЛОРУССИЯ грузия BSA Bittorrent облачные вычисления Azure Европа Dr.Web Билл Гейтс спецслужбы Cryzip Живой Журнал Royal Bank of Scotland смартфон Canonical Pwn2Own F-Secure Symbian Hotmail фильм

Главная » Статьи » Общие Статьи

Пишем сниффер под Linux
PF_PACKET

Функция socket() открывает сокет (или, как сказано в 'man 2 socket' моего линукса, "оконечную точку коммуникации") и возвращает её дескриптор. Синтаксис функции таков:

int socket(int domain, int type, int protocol);

Первый параметр, int domain, задаёт семейство (или, более привычно, стек) протоколов, для которого создаётся сокет. Стеку IPv4 (Internet Protocol version 4), например, соответствует константа PF_INET или её эквивалент, AF_INET. Все доступные (но не обязательно поддерживаемые ядром) константы перечислены в <bits/socket.h>. Так вот, в Linux'е поддерживается особый стек протоколов (который на самом деле стеком не является), PF_PACKET. Апеллируя к нему, можно открывать сокеты с доступом к самому низкому, сетевому уровню (Local Networking Level по модели OSI), а это именно то, что нам с тобой нужно для создания сниффера.

Второй параметр, int type, определяет тип протокола, требуемый внутри выбранного семейства. Таких типов протоколов немного: SOCK_STREAM (двустороннее потоковое соединение), SOCK_DGRAM (односторонняя дейтаграммная передача без установления соединения), SOCK_RAW (доступ к получению/формированию заголовков протоколов выбранного семейства) и нужный нам SOCK_PACKET (доступ к получению/формированию кадров данных). Существуют и другие типы протоколов специального назначения, но они в настоящее время либо не используются, либо используются крайне редко.

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

sock = socket(PF_INET,SOCK_STREAM,0);

откроет нам IP/TCP'шный сокет, а вызов

sock = socket(PF_INET,SOCK_RAW,IPPROTO_ICMP);

откроет нам IP/ICMP'шный сокет.

В нашем же случае, третий параметр указывает на протокол (Internetworking Level по модели OSI) сетевого уровня, с которым необходимо проассоциировать сокет. Все доступные значения для сокета PF_PACKET/SOCK_PACKET перечислены в <linux/if_ether.h>. В своём примере я буду использовать параметр ETH_P_ALL, что означает приём всех доступных ядру системы сообщений. Необходимо кастовать эту двухбайтную константу к обратному порядку следования байт, макросом htons(). Итак,

sock = socket(PF_PACKET,SOCK_PACKET,htons(ETH_P_ALL));

откроет нужный нашему снифферу сокет.

Обрати внимание, дружище, ты не сможешь открыть такой сокет, не обладая привилегиями рута. Из соображений безопасности, ядро не позволит открывать сокеты с SOCK_PACKET (и даже с SOCK_RAW) юзеру с UID'ом, не равном нулю.

ПРИВЯЗКА СОКЕТА К УСТРОЙСТВУ

Необходимо привязать сокет к физическому устройству, которое мы будем прослушивать. Это можно (и нужно) делать для любого устройства, кроме псевдоустройств (типа loopback). Привязка осуществляется системным вызовом setsockopt():

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);

int s -- сокетный дескриптор;
int level -- уровень, на котором необходимо произвести операцию;
int optname -- непосредственная операция, зависящая от второго параметра;
const void *optval -- указатель на начало аргумента к операции (либо NULL, если аргументов не требуется);
socklen_t optlen -- длина аргумента.

Вообще говоря, setsockopt() -- функция, весьма богатая своими возможностями, но сейчас нас интересует только возможность привязки сокета устройству, имя которого мы сообщим в четвёртом параметре, например:

rc = setsockopt(sock,SOL_SOCKET, SO_BINDTODEVICE,"eth0\x00",strlen("eth0\x00")+1);

привяжет сокет sock к устройству eth0. Обрати внимание, необходимо завершать имя устройство нулевым символом. Если setsockopt() совершит привязку успешно, она вернёт нуль. В противном случае необходимо проверять глобальную переменную errno для детальной диагностики ошибки.

PROMISCIOUS MODE

В нормальном режиме работы, устройство будет принимать (и, соответственно, доставлять ядру) лишь те кадры данных, которые содержат в поле Destination MAC Address проассоциированный с этим устройством MAC-адрес. Иными словами, при нормальном режиме работы устройства, ты сможешь ловить из сети только те сообщения, которые направлены к тебе.

Для того, чтобы получить возможность вылавливать из сети все кадры данных без исключения, необходимо перевести устройство в promiscious mode ("промискуитетный", т.е. неразборчивый режим). Это осуществляется с помощью вызова ioctl():

int ioctl(int d, int request, ...);

int d -- файловый (или, как частный случай файлового, сокетный) дескриптор;
int request -- системный вызов ядра Linux (перечислены в man 2 ioctl_list и разбросаны по хидерам в <linux/...>);
... -- переменное число аргументов ко второму параметру.

Для осуществления этой операции нам потребуется сгрузить текущие параметры и настройки устройства (интерфейса) в структуру struct ifreq (описан в <linux/if.h>) с помощью системного вызова SIOCGIFFLAGS:

struct ifreq interface;
...
ioctl(sock,SIOCGIFFLAGS,&interface);

И взвести в элементе ifr_flags структуры interface бит IFF_PROMISC:

interface.ifr_flags |= IFF_PROMISC;

А затем, с помощью системного вызова SIOCSIFFLAGS, загрузить изменённую структуру interface обратно:

ioctl(sock,SIOCSIFFLAGS,&interface);

попутно проверяя результаты ioctl на предмет наличия ошибок.

Обязательно учти тот момент, что при большой нагрузке на сеть (более нескольких тысяч кадров данных в секунду), сниффер, переведённый в promiscious mode, будет "задыхаться", не успевая обрабатывать поток сообщений. Это может привести к непредсказуемым последствиям. Начиная с того, что такой сниффер будет попросту пропускать некоторые сообщения (ядро не будет успевать "сгребать" эти сообщения с устройства), и заканчивая тем, что активность такого сниффера можно легко обнаружить различными анти-снифферскими методами. Эта опасность не преувеличена: даже пара десятков windows-машин в локальном сегменте создадут ощутимую нагрузку на сеть, обмениваясь пустопорожним NETBIOS-траффиком друг с другом.

ВЫХОД ИЗ PROMISCIOUS MODE

После завершения работы сниффера, НЕОБХОДИМО вернуть устройство в первоначальное (нормальное) состояние, сняв флаг IFF_PROMISC. Для этого, производим обратную описанной выше операцию:

interface.ifr_flags ~= IFF_PROMISC;

и

ioctl(sock,SIOCSIFFLAGS,&interface);

RECVFROM

Итак, после того, как мы создали сокет, привязали его к нужному интерфейсу и перевели устройство в promiscious mode, мы можем получать кадры данных (начиная с заголовка EtherNet, соответственно), следующим вызовом:

rc = recvfrom(sock,buf,sizeof(buf),0,0,0);

где buf -- достаточно крупный, чтобы вместить максимально возможный кадр данных, буфер. sizeof(buf) я рекомендую делать не менее 65536 байт (хотя, на самом деле, во многих локальных сетях MTU не превышает 1.5-4 тысячи байт). Последние три параметра, за ненадобностью, опускаются.

Обрати внимание: сокет остаётся в блокирующем режиме. Это не очень эффективно, но зато очень просто :)) В самый раз для демонстрационного примера :) Итак, recvfrom() будет отдавать управление всякий раз, как только в буфере сокета sock появится хоть что-нибудь для чтения и это "что-нибудь" будет прочитано.

LOOPBACK

Для желающих сниффить loopback device (а такая необходимость нередко возникает, особенно у разработчиков сетевого ПО, тестирующих свой софт на локалхосте), следует не привязывать сокет к устройству и, соответственно, не вводить его в promiscious mode, а просто отфильтровывать (на программном уровне) сообщения с нулевыми Source и Destination MAC-адресами. Этот подход проиллюстрирован в примере.

ПАРСИНГ

Я сделал небольшой примитивный парсинг вывода в демо-сниффере. Метод, конечно же, оставляет желать лучшего :))) Но, всё же, это эффективнее, чем вызывать по тысяче раз функцию printf() непосредственно на консоль при парсинге одного-единственного сообщения :) Вообще говоря, парсинг в сниффере -- это самая муторная и ресурсоёмкая задача. Но, уверен, тебе не составит особого труда её решить в том виде, в котором ты захочешь.

ЭФФЕКТИВНОСТЬ

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

Выход, тем не менее, есть. Он был предложен аж в начале 80-х годов и давно реализован в виде библиотеки libpcap для UNIX, используемой популярным сниффером tcpdump. Это решение, именуемое Berkeley Packet Filtering (bpf), заключается в установке фильтра на устройстве таким образом, что устройство генерирует прерывание и отдаёт ядру лишь те сообщения, которые этот фильтр пропускает. Фильтр можно настраивать на MAC-адреса, на IP-адреса, на протоколы транспортного уровня (ICMP, TCP, UDP, etc), на определённые порты, на количество байт, которое необходимо сгребать с каждого кадра данных и на многое другое.

BPF можно программировать с помощью команд SO_ATTACH_FILTER и SO_DETACH_FILTER уровня SOL_SOCKET вызова setsockopt():

rc = setsockopt(sock,SOL_SOCKET,SO_ATTACH_FILTER,&opcode,sizeof(int));

особым BPF-ным псевдокодом (а это весьма муторное занятие), либо с помощью вызова pcap_compile() библиотеки libpcap, компилируя псевдокод из текстовой строчки, как это делается в tcpdump'е (вроде "host 192.168.0.1 proto 6 port 23\x00").

Но обо всём этом я напишу в следующей статье...

P.S.

Мне очень хотелось бы услышать твоё мнение относительно того, хочешь ли ты увидеть продолжение этой статьи? Интересна ли эта тема? Жду откликов во "мнениях"...

С уважением, [Privacy]

Исходники

Категория: Общие Статьи | Добавил: aka_kludge (19.08.2008) | Автор: Privacy
Просмотров: 2961 | Рейтинг: 5.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
    Главная      
...
На службе : дней

13:21
Обновить


Пользователи
aka_kludge
qwerty
LeadyTOR
aka_Atlantis
AdHErENt
mAss
Sissutr
hiss
DrBio
tHick

Поиск


Copyright tHR - TeAM 2024 г. admin: aka_kludge (ICQ:334449009) Moderator's: LeadyTOR, ... Яндекс.Метрика