Ping пакеты как временное хранилище данных на python raw socket

Когда придумал как удалить 100кб файл с компа

Когда придумал как удалить 100 кб файл с компа

Когда неподкованные пользователи (пенсионеры, школьники, офисный планктон) первый раз сталкиваются с чёрным окошком, именуемым «Командной строкой» они зачастую хотят с профессиональным видом проверить интернет соединение.

ping 8.8.8.8

Магический C:\Windows\System32\PING.EXE выполняет вполне интуитивно понятные действия: отправляет запрос на указанный адрес (в данном случае 8.8.8.8 — dns google), и если адрес отвечает, то выводит задержку ответа, по которой можно косвенно судить о скорости интернет соединения…

Если же вы имеете начальное представление о принципе работы интернетов этих ваших и примерно понимаете сетевую модель, то вам до следующего заголовка)

База базовая

Сидели умные люди такие в восьмидесятых и думали, как бы интернет сделать общий. Сидели они, будучи соединёнными сотнями Ethernet кабелей и разрабатывали иерархическую модель интернета, дабы каждый был соединён с каждым, и было всё стильно, да без централизации…

Классическая 7и уровневая модель OSI - Источник

Классическая 7и уровневая модель OSI — Источник

Зелёные (верхние) уровни нас не интересуют, их реализовывают конкретные приложения и сервисы, их зоопарк мы трогать не будем. Нужно идти с низов.

Первый уровень (просто витая пара, да) - Источник

Первый уровень (просто витая пара, да) — Источник

Провод передаёт приёмнику логическое состояние передатчика (0\1 — 1 бит), является самым низким (физическим) уровнем.

Второй уровень (Коммутатор) - Википедия

Второй уровень (Коммутатор) — Википедия

Второй уровень добавляет понятие MAC адресов. Теперь тот, кто отправляет данные, идентифицирует того, кто на другом конце провода. Каждому устройству в сети даётся уникальный шестибайтный адрес, и появляется возможность маршрутизировать данные к конкретному абоненту (в зоне действия провода).

Сетевой уровень (Wifi Роутер) - Википедия

Сетевой уровень (Wifi Роутер) — Википедия

Теперь данные пакуются в пакеты и адресуются некому IP адресу получателя, а специальные устройства вроде вашего роутера или огромного шкафа-маршрутизатора в общаге заботятся об их доставке (но не гарантируют её!)

Поверх 3его уровня, в частности IP адресов были построены такие протоколы передачи данных как TCP, HTTPS, UDP, FTP, ICMP и т.д. Они заботятся о многих вещах: гарантии передачи данных, безопасности и передаче конкретных высокоуровневых структур данных.

Идея!

Тут лучше просто взглянуть на оригинал:

Его план такой (конечно, интернет подразумевается безлимитным, а сетевой адаптер достаточно мощным):

мой друг в телеге - он исходник

мой друг в телеге — он исходник

план по захвату мира

план по захвату мира

  1. Находим точки А и В максимально удалённые друг от друга в сети.

  2. Отправляем большие данные из А в В

  3. Они фрагментируются и тормозят по пути, оседая в кэшах маршрутизаторов, которые их соединяют. (растёт задержка)

  4. Пункт В просто отправляет данные обратно к А

  5. А использовал модель интернета как «дикое» хранилище файлов на некоторое время (сравнимое с секундой)

Звучит бесполезно, но интересно. Надо подумать

Есть проблема: В в данном случае остается ни с чем. И совершать такие пересылки взаимно не выгодно.

Вот если бы можно было обойтись без В

Ты не можешь контролировать улетевший пакет

Или можешь?… Улетевший пакет…? Ping?

Ping — утилита для проверки целостности и качества соединений в сетях (или, проще говоря, штука которая отправляет ICMP Echo пакеты)

План по абьюзу интернета

План по абьюзу интернета

Созревает план. Кладём пароль от крипто-кошелька в нагрузку пинг пакета (удаляем его локально), отправляем на другой конец земного шара и на протяжении 0.5сек он хранится, будучи в кэшах роутеров и маршрутизаторов по дороге!

Б-безопасность П-практичность

Но windows не позволяет редактировать содержимое, отправляемое ping.exe (

Но, для начала, проверим: есть ли оно вообще?

Качаем Wireshark — (вики) — (офиц. сайт) — утилиту для перехвата пакетов. Запускаем перехват и ping 8.8.8.8 в консоли.

Банальный вопрос - моментальный ответ (50мс)

Банальный вопрос — моментальный ответ (50 мс)

Пакет от меня к гуглу

Пакет от меня к гуглу

Ответ от гугла

Ответ от гугла

Полезная нагрузка (данные) в этом пакете действительно есть, однако до реальной пользы им далеко — это английский алфавит (нет, я не испытываю ненависть к латинице, просто мне хотелось бы уметь редактировать это содержимое).

Точка невозврата

С этого момента начинается практическое создание скрипта, который автоматизировал бы это. Идея изначально провальная, ведь icmp не гарантирует доставку, и очень ценные файлы, которые я буду фрагментировать и гонять таким образом рано или поздно будут по частям теряться).

Ну, а мне и не жалко, ведь дальше я хотел просто шутки ради реализовать этот механизм на Python\Sockets потестить, и забыть как страшный сон — поехали:

ICMP

Протокол межсетевых управляющих сообщений, или проще говоря — проверки связи.

Не гарантирует доставку данных, не использует шифрование — сплошной минимализм. Но, зато он просит получателя отправить себя обратно! Если получатель (сетевое устройство 3его уровня сетевой модели) не стало намеренно отклонять подключение, то оно скорее всего так и поступит.

icmp пакетами можно конечно отправлять полезные коды, которые помогут администрировать сеть, но мы его будем рассматривать как примитивный тестер связи, ок?

(я специально не упоминаю порты и всё что с ними связано, ведь 1й порт для ICMP это скорее условность, да и эта информация нигде далее не понадобится)

Структура ICMP пакета

Источник

Начнём с низов этой вложенной структуры.

  1. Заголовок кадра это всё, что о пакете нужно знать для MAC адресации — мы с этим уровнем взаимодействовать не будем, так что идём далее.

  2. Заголовок дейтограммы (Заголовок IP) — IP header 20 байт под адреса получателя\отправителя и прочей служебной информации для сетевой маршрутизации по IP.

  3. ICMP заголовок и ICMP данные — в нашем случае данные это:

    Идентификатор + Номер последовательности + Данные (payload, хехе…)

Ре-а-ли-за-ци-я

Строгое представление ICMP header:

type (8), code (8), checksum (16), id (16), sequence (16)

def create_packet(id):
    ICMP_ECHO_REQUEST=8 #Код типа ICMP - в нашем случае ECHO
    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, (тут должен быть хэш пакета), 0, id, 1)
    data = "hello!"
    return header + data

Подготовим icmp пакет используя STRUCT для превращения типов данных в байты.

Но есть один нюанс. Это дырка (тут должен быть хэш пакета), checksum — если вам так угодно. Без него наш пакет будет сочтён за битый. Парадоксально, ведь мы должны посчитать хэш пакета, ещё до его создания. Однако, допустимо использовать для подсчёта хэша хэш в заголовке = 0

def create_packet(id):
    ICMP_ECHO_REQUEST=8 #Код типа ICMP - в нашем случае ECHO
    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1)
    data = b"hello!"

    my_checksum = checksum(header + data)
    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), id, 1)
    return header + data

Создаем шаблон пакета, добавляем наши данные в виде «hello!», считаем checksum (пока не ясно как), и создаем окончательный пакет, зная хэш.

Функцию для подсчёта хэша пакета я подсмотрел где-то на просторах интернета, и не пожалел. Нужно было только немного адаптировать под python3.

def checksum(source_string):
    sum = 0
    count_to = (len(source_string) / 2) * 2
    count = 0
    while count < count_to:
        this_val = source_string[count + 1]*256+source_string[count]
        sum = sum + this_val
        sum = sum & 0xffffffff
        count = count + 2
    if count_to < len(source_string):
        sum = sum + source_string[len(source_string) - 1]
        sum = sum & 0xffffffff
    sum = (sum >> 16) + (sum & 0xffff)
    sum = sum + (sum >> 16)
    answer = ~sum
    answer = answer & 0xffff
    answer = answer >> 8 | (answer << 8 & 0xff00)
    return answer

Построитель пакетов полностью готов.

Остаются: функция send и функция recv

Вторая будет выполняться через время после первой и получать обратно отправленное.

def send(dest_addr):
    my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE)
    host = socket.gethostbyname(dest_addr)

    packet_id = random.randint(0,65535)
    packet = create_packet(packet_id)
    
    while packet:
        sent = my_socket.sendto(packet, (dest_addr, 1))
        packet = packet[sent:]

    return my_socket,packet_id

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

def recv(my_socket, packet_id):
    ready = select.select([my_socket], [], [], 2) #таймаут 2с
    rec_packet, addr = my_socket.recvfrom(1024)
    icmp_header = rec_packet[20:28]    # Байты с 20 по 28 - заголовок ICMP
    type, code, checksum, p_id, sequence = struct.unpack(
            'bbHHh', icmp_header)

    data = rec_packet[28:] # Наш hello будет лежать после заголовка ICMP
    return data

Recv получает пакет с заданным ID из старого сокета

Последние штрихи для запуска

import struct
import socket
import random
import select
ICMP_CODE = socket.getprotobyname('icmp')

Тест

Пора бы и продемонстрировать работу сия кода:

Попробуем отправить hello гуглу

send("8.8.8.8")

Мило, мы не остались без ответа.

Мило, мы не остались без ответа.

hello!

hello!

Попробуем отправить и получить обратно через время…

sock, id=send("8.8.8.8")
...
print(recv(sock,id))

кажется, работает

кажется, работает

Добавил функции send аргумент data, для кастомизации этого Hello

Итоговый код?

import struct
import socket
import random
import select
ICMP_CODE = socket.getprotobyname('icmp')

def checksum(source_string):
    sum = 0
    count_to = (len(source_string) / 2) * 2
    count = 0
    while count < count_to:
        this_val = source_string[count + 1]*256+source_string[count]
        sum = sum + this_val
        sum = sum & 0xffffffff
        count = count + 2
    if count_to < len(source_string):
        sum = sum + source_string[len(source_string) - 1]
        sum = sum & 0xffffffff
    sum = (sum >> 16) + (sum & 0xffff)
    sum = sum + (sum >> 16)
    answer = ~sum
    answer = answer & 0xffff
    answer = answer >> 8 | (answer << 8 & 0xff00)
    return answer

def create_packet(id,data):
    ICMP_ECHO_REQUEST=8 #Код типа ICMP - в нашем случае ECHO
    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1)
    data = data

    my_checksum = checksum(header + data)
    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), id, 1)
    return header + data

def send(dest_addr,data):
    my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE)
    host = socket.gethostbyname(dest_addr)

    packet_id = random.randint(0,65535)
    packet = create_packet(packet_id,data)
    
    while packet:
        sent = my_socket.sendto(packet, (dest_addr, 1))
        packet = packet[sent:]

    return my_socket,packet_id
def recv(my_socket, packet_id):
    ready = select.select([my_socket], [], [], 2) #таймаут 2с
    rec_packet, addr = my_socket.recvfrom(1024)
    icmp_header = rec_packet[20:28]    # Байты с 20 по 28 - заголовок ICMP
    type, code, checksum, p_id, sequence = struct.unpack(
            'bbHHh', icmp_header)

    data = rec_packet[28:] # Наш hello будет лежать после заголовка ICMP
    return data

sock, id=send("8.8.8.8",b"hohoho")
print(recv(sock,id))

Была отправлена строка, и получена через 50 мс, однако, это не есть решенная задача.

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

Я дописал и решил проблему маленького пинга до 8.8.8.8 (не успевал отправить много данных, как они начинали возвращаться). Нашел адрес одного стабильного сайта с хостингом в Новой Зеландии (пинг ~400). Что?

Однако есть ограничения, которые не позволяют получить зрелищный результат.
Один icmp пакет не хочет нести больше 1 кб, а если отправлять их с очень большой частотой, то они бьются еще на старте.
Максимальный размер файла, который мне удалость удержать таким образом был текст 0.1 мб (что вполне прилично для текста). Конечно, он со временем исчезал по кускам, но это следует из отсутствия гарантий доставки у ICMP.

Conclusion

Под конец могу сказать, что в итоге получилась (как и ожидалось) не самая полезная и практичная программа, которая может сохранить 0.1 мб файлик у себя и потерять его спустя пару часов непрерывной работы. Зато был получен позитивный опыт работы с низкоуровневыми сокетами.

Будет интересно, дочитал ли вообще кто-то это до конца?

Оцените, пожалуйста первую статью на Хабре:-) Здравая критика, а что в особенности важно — ещё бесполезные, но весёлые идеи приветствуются!

© Habrahabr.ru