Статья ASM для х86. (5.0.) Cтек TCP/IP. WinSock

Предыдущие части 1,2,3,4 можно найти здесь..
В этой части:
  1. Общие сведения
  2. Сокеты и библиотека WinSock
  3. Обзор функций WinPcap
Привет всем!
Продолжу тему ассемблера, и в этой части уделю внимание программированию сетей.
Дело это не для слабонервных, ..придётся осваивать кучу системных структур, таких как заголовки пакетов и прочее. Обстановку нагнетают и неимоверно богатые возможности сетевых сокетов, однако всё это с лихвой окупается новыми знаниями. Если рассматривать каждую мелочь, то запутаемся в терминах и на выходе получим винегрет. С другой стороны, в архитектуре сети всё переплитается в клубок и если что-то упустить, то на следующих уровнях нас ждёт инфо-дыра. Так-что выберу золотую середину, не углубляясь сильно в теоритическую муть. Материал рассчитан на тех, кто никогда не имел дело с программированием сети, но хотел-бы заняться этим.


5.0. Сетевая модель - общие сведения.

Ритм разработчикам сетевых приложений задаёт модель OSI, от которой пляшет и четырёх-уровневый стек TCP/IP. На каждом уровне действует свой протокол – этакий стандарт, описывающий правила взаимодействия на обоих концах связи. Само название TCP/IP взято от фонаря и его с таким-же успехом можно обозвать в стек UDP/IP, поскольку внутри транспортного уровня(3) стека протоколов, TCP и UDP имеют одинаковые права, но разные протоколы общения.

Поскольку наше WinSock-приложение будет находиться на самом-верхнем уровне(4), здесь и далее мы будем выступать в роли клиента, а не сервера – на мой взгляд это проще для понимания общих принципов. То-есть мы будем инициаторами связи с сервером, а он нам будет отвечать:

tcp_ip_00.png


Тут главное понять, что каждый узел (компьютер) в сети имеет свой стек TCP/IP, и каждый уровень этого стека взаимодействует только с одноимённым уровнем связанных в сеть узлов. Например протокол IP не имеет никакого отношения к оккупировавшим его с двух сторон уровням TCP и Ethernet – с ним имеют дело только уровни IP соседних машин.

Вне зависимости от того, по какой топологии построена сеть (звезда, кольцо или шина), по умолчанию сеть является одноранговой, где все носят погоны рядового и нет генералов. Это означает, что любой выставленный на линию запрос получают все машины в сети. А дальше, фильтрация запросов происходит на МАС-уровне, внутри сетевой карты. Если МАС-адрес карты не совпадает с тем, что прописан в Ethernet-кадре запроса, то узел игнорирует запрос. В результате, пакет получает только один из всех, чей МАС совпал.

Однако среди регистров контроллёра сетевой карты, есть регистр-управления RCTL (стр.264 ), обратившись к которому мы можем включить неразборчивый режим адаптера сети – его назвали "Promiscuous", что в дословном переводе означает "распутный". В этом режиме, внутри МАС-контроллёра фильтр отключается и адаптер пропускает на верхний уровень IP даже те запросы, которые не предназначены данному узлу. Этим поспешили воспользоваться хакеры и мир увидел класс утилит типа "Снифферы сетевых пакетов". Отмечу, что при настройке чипсета, системный BIOS/EFI включает режим "Promisc" в адаптере, а виндовый NDIS-драйвер потом отключает его, загнав в длинный список потенциальных угроз.

Основываясь на этом факте, было решено включить в топологию сети устройство под названием "Коммутатор" или Switch. Эти устройства подключаются в разрыв линии передачи и при первом включении в пассивном режиме собирают базу МАС-адресов участников сети. В результате коммутатор получает ARP-таблицу, и при очередных запросах уже сам фильтрует трафик, освободив от занимаемой должности МАС-контроллёр сетевой карты с пометкой "..в связи с неоправданием доверия". Так в сети LAN уже появился майор, и её нельзя теперь назвать одноранговой.

Но для глобальных сетей Интернет и этого оказалось мало.. Выше уровня коммутаторов пришлось вставить ещё одно устройство, которое назвали "Маршрутизатор". Получив данные от коммутатора, маршрутизатор может быть уверен, что МАС-адрес получателя совпал и он проверяет уже его IP-адрес. Другими словами, маршрутизатор живёт на межсетевом уровне IP, в то время-как коммутаторы занимают позиции на канальном уровне МАС. Такую схему применили для случая, когда узел с валидным МАС-адресом является сервером под-сети, например корпоративной или региональной. Он может иметь свою/внутреннюю сеть, на входе в которую стражем стоит свой коммутатор. В конечном итоге, топологию двух узлов можно представить так (про концентраторы типа "хаб" можно забыть, т.к. они вообще без интеллекта):

switch.png


В своём арсенале, Win имеет встроенные утилиты ком.строки ping и tracert. Алгоритм их работы по сути одинаков и позже мы напишем свои программы подобного рода, создав для этих целей сокеты. Первая PING позволяет проверить "пульс" какого-нибудь узла в сети и если узел жив, то он непременно вернёт нам ответ. Вторая тулза TRACEROUT отслеживает путь до любого узла в глобальной сети Интернет, возвращая нам маршруты своего продвижения по линиям передачи. И та и другая опрашивают коммутатор или маршрутизатор, если таковой имеется на линии связи. Вот как выглядит маршрут от меня и, например, до почтового сервера mail.ru:

tracert.png


Первые семь адресов – это айпишники серверов моего регионального назначения. Всего утилитой было сделано 16-запросов, причём время откликов с каждым разом увеличивалось, т.к. маршрутизаторы находятся всё дальше и дальше от моей геолокации. Там-где превышен интервал TTL (time-to-live, время жизни запроса), запрос нарвался на файервол (брандмауэр), который сам не отвечает, но обязан пропустить запрос по-цепочке дальше. Кому принадлежит тот или иной адрес, можно узнать на любом whois-сервере,


5.0.1. Сетевые порты

Термин "порт" фигурирует только на самом-верхнем уровне стека TCP/IP – уровне приложений. Упоминание о портах напрочь отсутствует в описании протокола IP, зато в полной мере раскрывается на транспортном уровне TCP и UDP. Номер порта адресует сетевой пакет в руки конкретного приложения. К примеру, если указать порт(80), пакет получит работающий по протоколу http интернет-браузер. Когда мы из своего приложения отправляем пакет в сеть, то обязательно должны указать номер порта, чтобы получивший наш пакет узел знал, какому именно приложению его передать (telnet, почтовый сервер, веб-браузер и т.д.).

В заголовках пакетов TCP/UDP, под номер порта система выделяет 16-бит 0xffff, которыми можно адресовать всего 65.536 пользовательских программ. Так выглядит заголовок, например, дейтаграммы UDP:

C-подобный:
struct UDP_Header              ;// всего 8 байт //
   src_port       dw  0          ; 2-байтный порт отправителя
   dst_port       dw  0          ; 2-байтный порт получателя
   length         dw  0          ; длина UDP-датаграммы
   checksum       dw  0          ; CRC блока (контрольная сумма)
ends
; Здесь начинаются данные UDP..

Порты от нуля и до 1024 зарезервированы системой, а остальные - разработчики программ и игр стараются занять для своего детища (например сетевая гейма). В девственной Win-XP зарегистрированных портов всего 86-штук, в Win-7 их уже 175. По мере того-как винда обрастает всякими прожками, число занятых системой портов растёт в геометрической прогрессии. При создании RAW-сокета и мы можем выделить для общения со-своим клиентом один из свободных портов, и гонять через этот порт зашифрованый трафик. Большинство известных нам мессенжеров по-сути так и поступают.


5.0.2. Протоколы

Представим ситуацию, когда китаец разговаривает с русским, который не знает китайского языка. Один что-то доказывает, а второй его в упор не понимает. Аналогичная ситуация может возникнуть и при общении клиента с сервером, если они не будут придерживаться общих правил. Из огромной армии протоколов, нас будут интересовать только HTTP, TCP, UDP, IP, ICMP и ARP. Все эти протоколы достаточно примитивны, и на их фоне выделяется только TCP – его и рассмотрим подробней..

Этот протокол считается надёжным, чего нельзя сказать о том-же UDP. Чтобы оправдать своё громкое имя, TCP сначала устанавливает связь с сервером, и если сервер ответит подтверждением ACK (acknowledgement), только потом передаёт ему данные. Теперь TCP опять ожидает ответа от сервера, что тот благополучно принял отправленный пакет. Этот процесс известен в миру как "тройное рукопожатие" Handshake, что приводит к ощутимой потери скорости обмена по данному протоколу:

handshake.png


На рисунке видно, что инициатор связи сначала стучится к серверу с пакетом синхронизации SYN, на что сервер отвечает подтверждением SYN-ACK. Теперь клиент опять обращается к серверу, мол "..получил твоё подтверждение" и следом засылает полезные данные. Когда клиент захочет оборвать связь с сервером, он должен послать ему пакет финализации FIN, на что сервер отвечает уже двумя (сдвинутыми по-времени) посылками. Клиент подтверждает их и связь обрывается.

Протокол TCP изначально родился с дырой, так-что и сейчас является уязвимым. Одно время, по сети прокатилась волна атак под названием , которая заключалась в отправке большого числа SYN-запросов серверу, игнорируя его ответы. Тучи сгущало и то, что RAW-сокеты позволяют подменять IP-адрес отправителя, поскольку сами формируют заголовки пакетов. Сервер не мог даже отследить атакующего и бригада каратистов направлялась совсем по другому адресу.

Проблему нужно было как-то решать, и мелкософт не придумала ничего лучше, как совсем закрыть вход RAW-сокетам на уровень IP, где можно подделать адрес-отправителя. Сейчас для сырых сокетов доступны только уровни TCP, UDP и ICMP, от которых пользы, как от козла молока. Но свято-место пусто не бывает и нишу тут-же заполнила сторонняя либа WinPcap. Её используют все сетевые утилиты, в числе которых и знаменитый "Wireshark". Функции из этой библиотеки мы рассмотрим в самом конце этой части и то поверхностно, т.к. лично я не являюсь сторонником всякого рода атак и стремлюсь к познанию, а не к разрушению.

Остальные протоколы стека TCP/IP не заслуживают такого внимания – весь их протокол вмещается внутрь заголовка пакета. Например UDP вообще не устанавливает предварительной связи, и тупо отправляет свои дейтаграммы в космос. Здесь нет никакой гарантии, что инфа дойдёт до адресата, и мы надеемся только на удачу. И нужно сказать, удача подводит редко. UDP-запросы выигрывают в скорости почти в 3-раза, оставляя протокол TCP нервно курить в сторонке. В большинстве случаях, тёмная сторона организации протоколов нас волновать не будет, поскольку этим занимаются системные библиотеки, типа wsock32.dll и её старший брат ws2_32.dll. От нас требуется только передать соответствующие аргументы и просто вызвать из них API функцию.


5.0.3. Инкапсуляция заголовков

По мере продвижения нашего запроса по стеку вниз, он будет обрастать заголовками всех мастей. Внутри каждого типа заголовка хранится служебная информация, предназначенная для того-же уровня на приёмном узле. Прочитав информацию из своего хидера, уровень очищает пакет от своих данных и передаёт его на следующий уровень, который так-же удаляет из пакета свой заголовок. Когда пакет достигнет самого верхнего уровня приложений, в нём уже не будет никаких заголовков, а только полезные данные. Процесс добавления заголовков Header назвали "инкапсуляцией", а обратный процесс – "декапсуляцией":

incap.png


Перед тем как отправить фрейм адресату, логика сетевого адаптера выставляет на линию 8-байтный пакет. Этот пакет предназначен для синхронизации приёмо-передающих узлов и известен как "Преамбула фрейма". Преамбула имеет значение 0xAAAAAAAAAAAAAAAB, а последний байт 0xAB – это флаг начала передачи. Никакого отношения к заголовку-фрейма преамбула не имеет, и её нет в системных структурах WinSock. Она отправляется на аппаратном уровне, сетевым адаптером NIC (Network Interface Card).

Существуют три версий Ethernet-фрейма, и каждая добавляет свои поля в Ethernet-заголовок.
DDX - устаревшая спецификация с заголовком в 14 байт присутствует во-всех версия. Кроме неё может быть: 802.3 (основная) и 802.3-SNAP. Структура DDX-версии выглядит следующим образом и когда нам будет нужно, мы будем пользоваться именно ею:

C-подобный:
struct Eth_Header                ;// всего 14 байт
   dst_addr       db  6 dup(0)   ; 6-байтный MAC получателя
   src_addr       db  6 dup(0)   ; 6-байтный МАС отправителя
   typelen        dw  0          ; длина заголовка и тип кадра (IPv4 =0x0800, ARP =0x0806 и т.д)
ends

Мин.размер заголовков IP и TCP равен 20 байт (хотя может быть и больше), а заголовок UDP всегда = 8 байт. Размер полезных данных может варьироваться от 46 и до 1500 байт. Если IP-пакет превышает 1500-байтную границу, он разбивается на кадры и отправляется в сеть частями. В этом случае получаем фрагментированный пакет, а номер фрагмента(id) указывается в IP-заголовке :

C-подобный:
struct IP_Header               ;// всего 20 байт
   iph_verlen     db  0          ; 4 бита = версия, 4 бита = длина заголовка в dword'ах
   iph_tos        db  0          ; тип сервиса (приоритет пакета 0..7 для маршрутизаторов)
   iph_len        dw  0          ; длина IP-пакета (фрагмента)
   iph_id         dw  0          ; id пакета
   iph_offset     dw  0          ; 3 бита = флаги, 13 бит = смещение кадра от начала пакета
   iph_ttl        db  0          ; время его жизни TTL (time_to_live)
   iph_proto      db  0          ; тип протокола (TCP, UDP, ARP, ICMP)
   iph_xsum       dw  0          ; контрольная сумма CRC-16
   iph_src        dd  0          ; 4-байтный IP отправителя (раньше можно было менять)
   iph_dest       dd  0          ; 4-байтный IP получателя
; Здесь начинаются данные..
ends

Посмотрим на поле iph_offset, в котором старшие 13-бит выделяется под смещение, а младшие 3-бита под флаги. На самом деле под смещение выделяются все 16-бит, просто младшие 3-бита логически обнуляются, в результате чего смещение всегда кратно 8. Поле Offset привязано к полю Id, и вместе они позволяют отсортировать фрагментированные пакеты. Дело в том, что кадры одного пакета могут следовать не в том порядке, как нам хотелось-бы, ..т.е. не ровным строем один-за-другим. Вполне возможна ситуация, когда они идут в разнобой.

id_offset.png


Если мы отправляем пакет размером, например 6000 байт, то уровень IP почекает его на 4 фрейма (кадра) по 1500 байт каждый, и запишет в поле-id заголовка, рандомное число от фонаря. Это число будет уникальным для данного пакета, и одинаковым для всех его фреймов. Если в поле id лежит зеро, значит пакет не фрагментирован и имеет размер меньше 1500 байт (жёлтый кадр на рисунке выше).

Id всегда ходит в связке с полем Offset. Как мы помним это 16-битное поле, которым можно адресовать смещение от начала нашего/6000 байтного пакета. Если в Id лежит какое-то значение, а в поле Offset лежит нуль, значит это первый кадр фрагментированного пакета. В следующем кадре этого-же пакета, в поле Offset будет лежать размер предыдущего, и т.д. Фрагментированные пакеты мы должны читать в цикле до тех пор, пока функция WinSock чтения данных recvfrom() не вернёт нам ошибку.

Из-за ограничения поля Offset в 16-бит, стандартными средствами WinSock мы не можем принимать/отправлять данные размером больше 2^16 = 65536 байт (64Кб). Для операций с файлами больше 64К имеется WinSock-2 функция TransmitFile() со-своей гибкой структурой OVERLAPPED. В этой структуре под Offset выделяется уже не 16, а 64 бита – можете посчитать калькулятором, файлы какого размера она может передавать.

Но вернёмся к IP-заголовку..
Ещё одно интересное поле в нём – время жизни пакета (TTL). Оно определяет число маршрутизаторов, которые может пройти этот пакет. При прохождении каждого маршрутизатора, счётчик TTL уменьшается на единицу. Если значение этого поля равно нулю, то пакет должен быть отброшен, и отправителю посылается ICMP-сообщение типа(11) и кодом(0) – превышен лимит. Мы ещё не раз вернёмся к TTL, когда попытаемся написать свой трассировщик маршрута Tracerout.

Кстати, узнать реальный размер кадра можно утилитой PING. Она имеет ключ(–f) запрещающий фрагментацию, и ключ(–L) для определения размер пакета. Комбинируя их получим такую картину где видно, что допустимый размер одного пакета застыл на отметке в 1492-байта:

ping.png


Как уже упоминалось, в поле Offset младшие 3-бита выделены под флаги, одним из которых является бит(DF) – "Don’t Fragment". Манипулируя этим битом можем запрещать фрагментацию пакетов, для выявления некоторых ошибок.

Под занавес вводной части, сравним приведённую выше структуру IP_Header с выхлопом любого сниффера пакетов, например знаменитого "Wireshark". Здесь видно, что наша структура и лог сниффера полностью совпадают. Конечно-же эта крутая тулза не использует виндовые функции WinSock – у неё имеется своя библиотека wPcap.dll и драйвер NPF.SYS для этих целей. Но в любом случае эта парочка вклинивается фильтром в стек системного драйвера винды NDIS.SYS, на который опирается и WinSock. Это обнадёживает..

Wireshark.png


Проверить наличие драйвера npf.sys в системе (и соответственно возможность использования сырых сокетов во-всей красе) можно в командной строке Win. Так-как этот дров запускается как отдельный сервис, то достаточно ввести в консоли sc query npf и посмотреть, что вернёт нам данная утилита "Service_Control". При наличии драйвера, консоль отзовётся так:

pcap_0.png


В следующем разделе этой части займёмся практикой и посмотрим, какие возможности таят в себе сетевые сокеты. А пока - пытайте гугл на эту тему, и читайте маны MSDN.
 
A

Anonimous273

Жду недождусь продолжения, надеюсь не будешь тянуть с новой статьей две недели
 
  • Нравится
Реакции: Hardreversengineer

Marylin

Mod.Assembler
Red Team
05.06.2019
324
1 441
BIT
649
надеюсь не будешь тянуть с новой статьей две недели
Чтоб не тянуть нужен профит, а раз его нет - приходится искать на стороне, а сюда заглядывать только в свободное время. Отсюда и такие тайм-ауты.. Но следующая уже варится, и скоро будет готова. Спасибо за отзыв!
 
  • Нравится
Реакции: Mike123321 и Mikl___
A

Anonimous273

Чтоб не тянуть нужен профит, а раз его нет - приходится искать на стороне, а сюда заглядывать только в свободное время. Отсюда и такие тайм-ауты.. Но следующая уже варится, и скоро будет готова. Спасибо за отзыв!
К сожалению, образование - неблагодарный труд. Мне нравится что написано по делу и доступным языком (на этот форум заглядываю только чтобы вас почитать, но так ещё не зарегистрировался, по этому часть информации не вижу).
У вас будет раздел работы с графикой на ассемблере и работы в ассемблере на системном уровне (к примеру, написание небольших ос, да и я ни разу не видел GLSL в FASM, хотя очень много гуглил) ? Было бы крайне интересно.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!