Сетевой протокол — это набор правил, которые определяют принципы взаимодействия устройств в сети. Современные сети — это многослойные системы, где каждый уровень отвечает за свою функцию. Удобно представить это как луковицу: каждый слой — свой «слой» луковицы, и все вместе они формируют стек технологий. Такая модель удобна тем, что при замене одного из слоёв остальные остаются работоспособными — при условии соблюдения общих соглашений и стандартов.
Эти соглашения оформляются в виде RFC (Request For Comments). Сегодня первичную публикацию и развитие стандартов курирует IETF — открытое сообщество инженеров, формирующее архитектуру и протоколы Интернета.
Однако, как известно, дьявол кроется в деталях. На практике «идеального» соблюдения стандартов не бывает: разработчики зачастую отступают от рекомендаций. Кто-то не влезает в запланированный канал связи (например, эта проблема особенно актуальна для устройств «умного дома» без постоянного питания). Кто-то таким образом пытается скрыть информацию от злоумышленников или затруднить реверс-инжиниринг их систем (чтобы привязать пользователя к оплате лицензии). А кто-то просто считает, что может сделать лучше имеющихся аналогов. Собственный проприетарный протокол иногда даёт выигрыш по пропускной способности или задержкам, но при интеграции с другими системами и устройствами, это может создать серьёзные проблемы для сетевых инженеров.
Представьте, что ваша система — идеально отлаженный механизм; и вдруг нужно заменить одну шестерёнку, а разработчик уволился, лицензия отозвана (бывает, что купленное вами может не принадлежать вам :), исходники потеряны или устройство пришло в негодность. В таких случаях приходится восстанавливать поведение протокола почти «с нуля».
Привет, Хабр! Меня зовут Виталий, я python-разработчик в SimbirSoft. Цель этой статьи — дать практическое введение в реверс-инжиниринг сетевого трафика и анализ протоколов передачи данных. Сразу уточню: здесь не будет взлома или обхода защиты — я уважаю интеллектуальную собственность и законодательство. Материал посвящён техническим приёмам, которые будут полезны инженерам и специалистам по интеграции систем. Статья будет полезна разработчикам, аналитикам, менеджерам, которые работают с указанными решениями.
Готовы? Поехали.
Что понадобится для работы
Базовые знания
Понимание модели OSI/ISO на концептуальном уровне.
Базовое представление о теории связи.
Готовность не пугаться битов и, по возможности, умение работать с ними.
Инструменты
Просмотр битовых потоков с возможностью смены масштаба и периода, а также, в идеале, выделением определенных бит. Решений много, все имеют свои плюсы и минусы.
Анализатор трафика (обычно встроен в популярный сниффер, типа Wireshark).
GNU Radio или аналоги.
Сразу хочу отметить, что тема практически бесконечна. Количество возможных подходов и решений только растёт, несмотря на все усилия центров стандартизации. Особенно в эпоху революций в области IoT.
Структура канала связи
Начать предлагаю с пресловутой модели OSI, как основного стандарта в отрасли передачи данных. В процессе работы вы удивитесь, насколько далека её теория от реальных практических решений, но всё-таки анализ протоколов канального и транспортного уровней кардинально отличаются друг от друга. В этой статье мы преимущественно остановимся на самых непонятных для обывателя сторонах модели, а именно – нижних уровнях. Для начала давайте определимся по общей структуре канала связи, чтоб говорить на одном языке:
Модулятор/Демодулятор (можно назвать модем, но современные модемы обычно включают огромное количество функций, а не только работу с модуляцией). Блок, который возьмёт ваши данные и передаст на выход в форме, готовой для передачи по каналу. В данной статье рассматривать не будем, так как его реверс-инжиниринг обычно выходит далеко за рамки сетевых инженеров компании.
Помехоустойчивое кодирование (FEC). Блок исправления (или обнаружения) ошибок в канале связи.
Скремблер. Блок превращения данных в псевдослучайный шумоподобный поток. Преимущественно служит для выравнивания показателей и статистик для равномерной передачи в канал.
Транспортные протоколы и мультиплексоры, которые разделяют, кому каждая конкретная посылка предназначена. Да, естественно, задачи этого слоя гораздо обширнее. Но в контексте моей статьи мы будем рассматривать именно этот вариант построения.
Потребители информации (т.е. конкретное приложение, которому пакет предназначался).
С чего начать
Первое и самое важное — необходимо определить точку входа. На каком уровне вы подключились к потоку? Может, это уже готовый IP-пакет, а вы зря ищете скремблер? Чтобы это проверить, нужно собрать информацию: поспрашивать людей, посмотреть на модеме, если снимаете с него, спросить аналитика, откуда записали поток, и так далее. Теоретически, возможно, ни помехоустойчивый, ни скремблер вам не понадобятся, так как будут уже сняты. Но лучше вкратце пробежаться.
Помехоустойчивое кодирование (FEC)
Итак, помехоустойчивый (или корректирующий) код — это код, предназначенный для обнаружения и исправления ошибок. Первое, что поможет нам при его анализе, — поиск информации в интернете. Дело в том, что модулятор, кодирование и зачастую скремблер лучше всего работают именно в связке, которая называется «сигнально-кодовая конструкция».
Сигнально-кодовая конструкция (СКК) — это совокупность методов и устройств канального кодирования информации, а также методов модуляции (то есть способов формирования и приёма сигналов). И если мы знаем, откуда получен поток данных и какая у него была модуляция, — есть все шансы найти стандарт (RFC) или информацию на форумах о том, какой именно помехоустойчивый код используется.
А если нет? Тогда переходим к программе просмотра файла в битовом виде с возможностью смены периода отображения и погружаемся в бинарный вид.
FEC обычно делятся на два вида:
Блочные. Данные разбиваются на блоки, каждый из которых кодируется по определённому алгоритму.
Сверточные. Данные кодируются «на лету» в потоке.
Для блочного кода мы используем простую связку: Google + поиск периода + метод подбора. Итак, что же такое период и чем он может помочь? Напомню: блочный код состоит из блоков одинаковой длины. Если расположить эти блоки друг под другом, есть шанс, что где-то проявятся биты синхронизации:

За счёт этого мы можем применить автоматические математические методы — например, автокорреляцию. Не будем углубляться в математику, отметим лишь суть: выбираем определённый период, берём первый цикл и считаем его корреляцию со вторым (чаще всего применяют критерий Пирсона, хи-квадрат). Затем второй с третьим и так далее, записываем результат. Увеличиваем период, пересчитываем. Смотрим, на каком периоде получаем наибольшее значение критерия. На практике обычно видны чёткие всплески на кратных периодах (и не только — тут нужно будет перепроверить). А если у вас нет программы для автоматического расчёта корреляции — можно просто постепенно менять период и смотреть на битовой канве.
Обращаю внимание: при кратном периоде биты тоже будут заметны. Например, если период последовательности равен 1024 битам, то характерные структуры проявятся и на 512, и на 2048. Определить точный период непросто, поэтому придётся пробовать разные варианты (сначала методом подбора, а со временем «набьёте руку»). Зная период, можно попытаться понять, какие виды кодов подходят под этот вариант, и перебрать их подбором: пробовать декодировать и смотреть на процент ошибок. Если ошибок мало — скорее всего, мы нашли правильный код.
Самый удобный вариант для анализа — собрать тракт приёма в GNU Radio. В интернете и на форумах можно найти как примеры трактов, так и огромное количество плагинов для уникальных видов FEC.
Анализ свёрточного кода, по сути, мало отличается от анализа блочного. Да, у него нет характерных блоков, но со временем поток начинает как бы повторять сам себя. Это не настолько очевидно, как биты синхронизации в блочном коде, зато проявляется по всему файлу, а не только в местах стыковки блоков. Итог тот же: выбираем несколько популярных вариантов, пробуем декодировать. Если ошибок мало — скорее всего, угадали.
Скремблер
Их также бывает два вида: аддитивный и мультипликативный. Визуально скремблер представляет собой шумоподобный поток — выглядит как «каша». Явный признак применения скремблера — равномерное распределение нулей и единиц в потоке (кстати, это отличный способ проверить, что скремблер действительно используется).
Важный момент для анализа: через некоторое время поток начинает слегка повторять сам себя. Происходит это потому, что в скремблере генерируется M-последовательность — псевдослучайная двоичная последовательность, создаваемая регистром сдвига с линейной обратной связью и обладающая максимальным периодом. Искать период лучше всего с помощью программ для расчёта автокорреляции (описанных в разделе про помехоустойчивый код). Но если в случае коррекции ошибок на периоде обычно чётко видны биты синхронизации (см. рисунок выше), то у скремблера самоподобные конструкции «размазываются», и на битовой канве не видно чёткой полоски.
Совет: на практике используется очень небольшое количество скремблеров, поэтому можно попробовать «в лоб» самые популярные варианты, например мультипликативный (0, 3, 20). Подвох в том, что после снятия скремблера вы можете увидеть красивую структуру, но это не обязательно то, что нужно. Если вроде бы сняли, но непонятно, что получилось, — попробуйте другие варианты.
Аддитивный скремблер будет выглядеть аналогично, за исключением того, что для него нужно ещё подбирать начальный сдвиг — тоже методом подбора. Увы, здесь методик нет, так как неизвестно, в какой фазе вы начали приём потока.
Итак, мы закончили с самым «непривычным» для обывателя. Давайте поднимемся чуть выше — на границу физического и канального уровней. Почему именно на границу? Потому что никто вам точно не скажет, где заканчивается один уровень и начинается другой. Да, существуют стандарты и формально определённые границы, но сами протоколы эти границы постоянно нарушают, и в двух соседних источниках одни и те же протоколы могут относиться к разным уровням.
Канальный уровень
После снятия основных элементов физического уровня мы получили данные, но они всё ещё упакованы в множество «обёрток». Каналы разных пользователей перемешаны, пакеты идут вразнобой, а где находится канал управления — неизвестно. Но это уже данные, и у них есть определённая структура. Давайте разберём самые типовые варианты:
Периодическая структура. Данные объединяются в кадры определённой длины. Наша задача — найти период (здесь снова пригодятся автокорреляция и визуальный анализ в бит-анализаторе), а затем разбить кадр на каналы. Например, канал № 1 может забирать с 8-го по 15-й бит каждого кадра. Когда канал не используется, в него передаются пустые «заглушки». Классический пример — плезиохронная цифровая иерархия.
Пакетная передача. Передатчик выходит в эфир, передаёт данные и замолкает. Если никто ничего не передаёт, канал заполняется «заглушками». Обычно перед пакетом идёт специальная последовательность бит, которая сообщает приёмнику: «сейчас будет что-то интересное». Иногда это называют стартстопной передачей. Кроме того, обычно указывается номер пакета по порядку и, если длина пакета не фиксирована, его длина (1–4 байта).
Динамические системы с выделенным каналом управления. Тут всё настолько запутанно, что анализ таких систем выходит далеко за рамки этой статьи.
Предлагаю слишком не углубляться в разнообразие технологий физического и канального уровней (оно огромное, поверьте) и остановимся на самом распространённом варианте уплотнения трафика TCP/IP в сетях — протоколе High-Level Data Link Control (HDLC). Это бит-ориентированный протокол канального уровня. Термин «бит-ориентированный» используется потому, что поток бит приёмник сканирует побитово: сначала ищет стартовый флаг, а затем — стоповый. Поэтому длина кадра здесь не обязательно кратна 8 битам. Но если речь идёт про упакованный внутрь TCP/IP, то и байты, скорее всего, будут целыми (что не факт, но так принято).
Главное преимущество HDLC — его легко заметить на битовой диаграмме по характерным флагам холостого хода (01111110). Ещё один плюс — огромное количество готовых решений в интернете. Ну а если вы любите боль и решили писать декодер самостоятельно, не забудьте про нюансы: бит-стаффинг и управляющие кадры.
Ethernet, Wi-Fi и другие
После снятия канального уровня обычно встречается ещё один протокол канального уровня (мы же помним, что не все любят OSI?). В нём содержатся данные о физических устройствах, участвующих в передаче. Популярные и хорошо знакомые протоколы вроде Ethernet или 802.11 находятся именно здесь.
И хотя в Википедии HDLC указан как протокол канального уровня, а Ethernet — как протокол физического, на практике они располагаются наоборот. К счастью, эти протоколы обычно занимают фиксированное количество байт и чаще всего строго соответствуют стандарту.
Сохранение данных
И вот, наконец, настал момент, когда можно отложить бит-анализатор и взять более привычные инструменты. Но прежде хотелось бы сохранить полученные данные. Напомню: ещё недавно у нас была «каша» — бинарные данные, а теперь это уже аккуратные (надеюсь) пакеты.
pcap — самый распространённый формат, потому что именно его можно «скормить» акуле (Wireshark). Но тут есть и проблема: он же и самый сложный. Дело в том, что в его заголовке указывается, с какого уровня получены данные. С одной стороны, это огромный плюс: на разных этапах анализа протоколов можно загружать данные в Wireshark и смотреть, что он скажет (а говорит он много и подробно). С другой — это может стать проблемой для начинающего сетевика.
sig, lsig — простые пакетные форматы, работающие по принципу: длина (2 или 4 байта соответственно), полезные байты, снова длина.
Некоторые сниферы умеют напрямую читать HDLC. Это прекрасно: можно забыть о предыдущих форматах.
Wireshark
Ну что ж, мы получили обычный IP-поток, и дальше ваши лучшие друзья — типовые анализаторы вроде Wireshark. Друзья, правда, вредные: требуют сноровки, путают версии протоколов, не всегда поддерживают обратную совместимость, но всё же друзья. Например, у меня был случай, когда древняя версия Wireshark нормально читала небольшой протокол управления, а новые — уже нет:

Если всё прошло хорошо и Wireshark понял, какие данные у него на входе, можно попробовать собрать пакет, чтобы точно убедиться:

Если дальше у вас идут типовые решения — прекрасно, поздравляю: задача по первичному анализу завершена. Следующий шаг — оценка возможностей для создания потоковых обработчиков.
Однако если ваш трафик содержит специфичные данные (например, VoIP или IoT-решения), транспортные протоколы могут скрывать множество интересных и неожиданных решений. Но это уже тема для отдельной статьи.
Заключение
Реверс-инжиниринг протоколов — это не просто, но очень увлекательно. Сначала кажется, что перед глазами сплошной хаос из нулей и единиц, а потом постепенно проявляется структура, будто собираешь пазл без картинки на коробке. И если у вас что-то не получилось с первого раза — это нормально. В этой области редко бывает «сразу». Главное — пробовать, задавать вопросы, искать, и шаг за шагом всё начинает складываться. Пусть каждая разгадка, даже маленькая, приносит вам удовольствие. И тогда реверс протоколов перестанет быть головной болью и станет интересным приключением.
Спасибо тем, кто долистал до конца (и тем, кто сразу поставил «минус» — вы тоже моя публика). Да помогут вам неоновые боги матрицы :)
Больше авторских материалов для backend-разработчиков от моих коллег читайте в соцсетях SimbirSoft — ВКонтакте, Telegram и YouTube.