allasm.ru |
|
Данная статья познакомит вас с интересным миром, летающим в сетевом кабеле, наподобие витых пар, телефонной лапши, оптоволокна и т.д. и т.п. Нет, я не буду расписывать, для чего нужны эти сетевые кабели. Не буду писать вообще про них ни строчки. Но с миром, обитающим в них - познакомлю. Для использования этой статьи на практике вам необходимы следующие инструменты:
Обязательно скачайте полную и последнюю версию выше предложенной библиотеки c набором lib файлов. "..Умереть ничего - если выпить немного.." - Общая информация - В былые времена, когда компьютеры были действительно большими, а программисты были действительно умными, организации начали придумывать различные технологии обмена данными, были созданы первые сети. Был придуман протокол TCP/IP. Ученые работали над вопросом построения надлежащей модели этого протокола. Такую модель разработали и назвали, как модель OSI (Open System Interconnection - Взаимодействие открытых систем). OSI была разработана в рамках ISO (International Organization for Standardization - Международная организация по стандартизации). Подробнее об этом читайте соответствующую литературу (см. в конце статьи). OSI и TCP\IP модели делятся на уровни. Каждый уровень имеет свою функцию и свое назначение.
К физическому уровню относят разъемы, кабели - носители информации в общем смысле, сигналы. Уровень связи данных (еще так называемый канальный уровень) организует данные в кадры (frame). Данные обворачиваются заголовочной информацией о физических адресах источника и приемника, тип протокола (связан с интерфейсом сетевой карты (например, Ethernet)). Очень интересно, ведь, получив доступ к этому уровню, можно обволакивать свои данные в дополнительные заголовки или изменять уже существующие заголовки, используемые сетевыми картами в вашей сети. Во многих книгах и руководствах говорят, что об этом уровне можно не заботиться, но именно на этот уровень я обращу все ваше внимание. Сетевой уровень IP (Internet Protocol). Данные этого протокола пересылаются в элементах называемых датаграммами (datagram). В такой датаграмме содержится заголовок IP содержащий IP-адреса приемника и источника, длину пакета и другие параметры (подробнее смотрите в предложенной литературе ниже). Транспортный уровень включает в себя TCP (Transmission Control Protocol) и UDP (User Datagram Protocol) протоколы передачи данных. Эти протоколы не являются темой нашей статьи (подробнее о них можно узнать предложенной литературе ниже). Три верхних уровня соответствуют уровню приложений. Это может быть браузер, FTP сервер, Telnet.. Собственно это нам не интересно, с точки зрения программирования. Рассмотрим, как пакетируются данные, в зависимости от уровня TCP/IP:
Обратите внимание на то, почему нам так интересен канальный уровень. Он полностью контролирует все вышестоящие уровни и, кроме того, позволяет на своем уровне менять и добавлять параметры для каждого вышестоящего уровня. - Чем черт не шутит - Рассмотрим канальный уровень поподробнее. Для этого поставим перед собой задачу. Пусть нам необходимо послать пакет канального уровня. ARP (Address Resolution Protocol) протокол прекрасно подойдет для этой цели. Прежде чем хосты в сети Ethernet откроют соединение, они обязаны знать физические адреса назначения. Для этой цели и используется ARP протокол. Он осуществляет трансляцию между IP-адресом и соответствующим ему физическим адресом. На хосте содержится таблица, называемая ARP таблица. Она содержит в себе список IP-адресов и соответствующие им физические адреса. Существует два типа трансляции - динамический и статический. При динамической трансляции хост посылает широковещательный пакет ARP содержащий искомый IP-адрес. Целевой хост узнает свой IP-адрес и принимает этот запрос. При этом изменяется таблица - в нее включается IP-адрес и физический адрес отправителя. После этого хост передает отправителю свой физический адрес. Отправитель, получив такой ответ, обновляет свою таблицу ARP и становится готовым к пересылке данных по локальной сети. В статической трансляции никаких широковещательных данных не посылается, и никакие данные не принимаются. Вся ARP таблица заполняется самим пользователем системы. ARP пакет состоит из Ethernet кадра и ARP кадра. Рассмотрим подробнее эти кадры:
Первое поле Preamble можно опустить - оно заполняется на аппаратном уровне сетевой картой, его не видно в снифферах. Считаем что начало кадра - это следующее поле. Второе поле Destination Address - здесь находится информация о физическом (далее MAC) адресе хоста-приемника. Третье поле Source Address - информация о MAC (Media Access Control) адресе источника. Четвертое поле EtherType - тип Ethernet среды (это может быть Ethernet, Token Ring, Frame relay, ATM). Пятое поле Payload - это поле, по сути, является полезной нагрузкой - может содержать все что угодно. Если мы хотим послать ARP пакет, то в этом поле должна содержаться полная информация ARP кадра, то есть его заголовок и его полезная нагрузка. Шестое поле FCS - это циклическая контрольная сумма всего пакета данных. Является замыкающим полем пакета и заполняется на аппаратном уровне сетевой картой. Это поле нельзя увидеть в сниффере.
Первое поле Hardware Type - указывает на тип канала связи данных (Ethernet, Token Ring, Frame relay, ATM). Второе поле Protocol Type - содержит тип протокола. В нашем случае это ARP протокол. Третье поле Hardware Address Length - содержит длину поля физического адреса (MAC адреса). Четвертое поле Protocol Address Length - содержит длину поля протокольного адреса (IP адреса). Пятое поле Operation - указывает на тип ARP кадра. Это может быть кадр запроса, кадр ответа, и обратные варианты запроса и ответа. Шестое поле Sender Hardware Address - сюда записывается MAC адрес отправителя. Седьмое поле Sender Protocol Address - в него записывается IP адрес отправителя. Восьмое поле Target Hardware Address - соответственно MAC адрес получателя. Девятое поле Target Protocol Address - соответственно IP адрес получателя. В итоге, мы должны собрать эти два кадра вместе, добавить полезную нагрузку, если это необходимо, и у нас получится ARP пакет. - Думай, что делаешь - Теперь мы имеем достаточное представление организации данных, можно перейти к практической части. Тут есть одна сложность. Windows не позволяет получить доступ к канальному уровню, без дополнительных усилий и временных затрат. К счастью, опытные разработчики уже решили этот вопрос за нас - нам не придется писать протокольный драйвер NDIS. И этим решением является библиотека winpcap. Она позволяет осуществлять наши цели, не задумываясь о том, как же устроены механизмы взаимодействия драйверов сетевой карты и подсистемы ввода/вывода. Нам дается возможность, послать в сеть пакет как есть (ну почти, если не учитывать аппаратно добавляемые поля). Создадим простенькое приложение. Это будет окно, на котором находятся кнопка и поле ввода. В поле ввода мы вводим IP-адрес того, кто должен получить ARP пакет. Опущу код создания окна. Это вы должны уметь делать сами. Рассмотрим действия, вызываемые по нажатию кнопки. Для начала подключим 2 файла (смотрите в архиве, в конце статьи):
В packet.inc файле содержатся объявления прототипов используемых нами функций и структур. Файл packet.lib - это файл библиотеки импорта. Как уже говорилось выше - скачайте его у разработчиков или создайте сами из динамической библиотеки packet.dll. Объявим используемые переменные:
очищаем буфер для дальнейшего использования:
для обращения к сети необходимо узнать имя сетевого адаптера в системе. Функция PacketGetAdapterNames(), экспортируемая из packet.dll (как и все другие начинающиеся со слова Packet), позволяет получить имена всех адаптеров, установленных в системе. Ее прототип следующий: PacketGetAdapterNames PROTO STDCALL :DWORD, :DWORD Первый параметр - это адрес буфера куда запишутся имена всех адаптеров. Вызовем ее:
обязательно проверяем, не возвратила ли функция ошибку:
возвращаемый формат строки имеет следующий вид: <имя первого устройства>0<имя второго устройства>0<…..>00<описание первого устройства>0<описание второго устройства>0<…..>0000000….. Возьмем первое попавшееся устройство и запишем его в отдельный буфер (без его описания):
покажем имя нашего адаптера:
далее открываем адаптер. Фактически winpcap скрывает от нас все тонкости и работает с адаптером как с файлом. Для открытия адаптера используем функцию PacketOpenAdapter(). Она имеет следующий прототип: PacketOpenAdapter PROTO STDCALL :DWORD Единственный параметр - это адрес буфера, содержащий имя адаптера. Вызываем:
проверяем, возвратился ли описатель адаптера или произошла ошибка:
сохраняем описатель адаптера:
Далее необходимо выделить память для структуры PACKET. Используем функцию PacketAllocatePacket(). Она не имеет параметров. Сразу проверим на ошибку. Если все прошло гладко, то сохраним описатель структуры PACKET:
считываем введенный нами IP-адрес назначения пакета и приведем сразу в little endian формат:
Все подготовлено - можно начинать формировать пакет. Вспомним предыдущие рисунки. Первым заполняется Ethernet кадр. Укажем в нем, что этот пакет должен быть широковещательным. Это поле должно содержать 1 в каждом бите:
Второе поле - это наш физический адрес. Не будем подставлять туда реальный. Установим его в 0:
Третье поле - это тип среды - по RFC тип среды Ethernet равен 0806h. Так и установим:
С Ethernet кадром разобрались. Заполним ARP кадр. Первое поле - тип канала связи. Для Ethernet надо установить в 0001h:
Второе поле - это тип протокола. В нашем случае ARP. По RFC - это 0800h:
Третье поле соответствует длине физического адреса. Для Ethernet длина этого поля равна 6 байтам. Так и напишем:
Четвертое поле - длина IP-адреса. Максимум 4 байта для IPv4:
Пятое поле - это тип ARP пакета. Установим как ARP запрос (по RFC 0001h):
Шестое поле - наш физический адрес. Установим в 0 все байты этого поля:
Седьмое поле - наш IP-адрес. Думаете, свой адрес будем писать? Ошибаетесь. Запишем сюда IP-адрес самого получателя, который мы ввели в поле ввода. Он содержится в соответствующем на виде в регистре eax (ранее подготовили функцией inet_addr()). Сдвигаем циклически этот регистр, помещая значение побайтно в четырех байтное поле:
Восьмое поле - физический адрес получателя. Вспомните, что мы туда писали в Ethernet кадре:
Девятое поле - IP-адрес получателя заполним его корректно. Сдвигая в прошлый раз eax побайтно, сдвигаем и в этот раз так же:
Кадры заполнены. Еще можете дописать в конец сообщение, например:
Пакет сформирован. Необходимо его послать. Первая функция, которая поможет нам это сделать - PacketInitPacket(). Ее прототип следующий:
Первый параметр - это описатель структуры PACKET. Второй параметр - адрес буфера содержащего пакет. Третий параметр - длина пакета. Вызываем функцию: push 50 lea eax,packetbuff push eax push lpPacket call PacketInitPacket еще одна функция PacketSetNumWrites() (необязательная). Она устанавливает количество отправленных за 1 сеанс пакетов. По умолчанию оно равно единице. Можете указать больше. Функция имеет прототип: PacketSetNumWrites PROTO STDCALL :DWORD, :DWORD Первый параметр - описатель адаптера. Второй параметр - количество пакетов. Устанавливаем:
теперь пошлем пакет 100 раз с интервалом 1 пакет в пол секунды (используем для ожидания функцию Sleep()). Для отсылки используется ключевая функция PacketSendPacket(). Она имеет следующий прототип: PacketSendPacket PROTO STDCALL :DWORD, :DWORD, :DWORD Первый параметр - это описатель устройства. Второй параметр - описатель структуры PACKET Третий параметр - режим выполнения операции (синхронный \ асинхронный). В нашем случае установим синхронный режим. Пока 100 пакетов не отправится - функция блокирует приложение:
Всю работу сделали. Необходимо прибрать за собой. Функция PacketFreePacket() освобождает память, заказанную под структуру PACKET. Имеет прототип: PacketFreePacket PROTO STDCALL :DWORD Единственный параметр - это описатель структуры PACKET Вызываем:
Последнее действие с нашей стороны - это освобождение адаптера. Функция PacketCloseAdapter() высвобождает структуру ADAPTER. Прототип функции:
Единственный параметр - описатель адаптера.
Все сделали - выходим:
Сюда мы попадем, если на этапе работы программы возникла ошибка. Просто выведем сообщение. Для анализа ошибки используйте функцию GetLastError(). Ограничимся одним пустым сообщением:
Вот и весь код. Результаты работы нашей программы посмотрим в сниффере. Установим его в режим захвата пакетов с уровня Ethernet и поймаем наши пакеты:
Что послали, то и получили. - Эпилог - Вы наверняка задались вопросом - "почему именно такой пакет, и именно такие параметры". Ответ очень прост. В реализации протокола ARP есть недочеты. Это одна из них - так называемая разновидность ARP-poisoning. Хост, принимая наши пакеты, отключает себя от сети с ошибкой "Конфликт IP адреса". А все потому, что указанный физический адрес назначения в пакете является широковещательным, а физический адрес источника - ложный. ARP запрос в этом случае просто говорит хосту (то есть всем хостам), получившему этот пакет, что такой IP-адрес в сети уже существует. В итоге хост с этим IP-адресом не сможет ни с кем соединится. Firewall`ы, как ни странно, видят эти пакеты. Но нам они их не показывают. Ни один из попадавшихся мне широко распространенных firewall`ов не позволял устанавливать для ARP протокола какие-либо правила - например проверка пакета по шаблону или проверка на предмет левых MAC адресов. Выйти из такой ситуации можно разными способами. Например, есть возможность закрыть таблицу ARP (сделать ее статической). Но для большой сети это неприемлемо. И мало того - операционная система Windows 9x-2000 все равно будет обновлять статическую ARP таблицу. Второй вариант - написание фильтра трафика канального уровня на предмет зловредных пакетов. Под конец добавлю, что я не несу ответственности за то, как будет использован этот материал. Вся информация в этой статье несет сугубо информативный характер в помощь администраторам. Используемая литература:
Ссылки: [C] TermoSINteZ |