Когда придумал как удалить 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мб файлик у себя и потерять его спустя пару часов непрерывной работы. Зато был получен позитивный опыт работы с низкоуровневыми сокетами.

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

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

Комментарии (26)


  1. adron_s
    15.07.2023 12:41
    +4

    Пан знает толк в извращеньях :-)


    С учебной точки зрения опыт интересный ! ICMP действительно может нести данные и в контексте текущей задачи не отличается от UDP. Кроме уровня в модели OSI.


    1. anonymous
      15.07.2023 12:41

      НЛО прилетело и опубликовало эту надпись здесь


    1. drygdryg
      15.07.2023 12:41

      Это может иметь применение в стеганографии.


  1. ProstoTyoma
    15.07.2023 12:41
    +14

    Вы переизобрели pingfs )

    https://code.kryo.se/pingfs/


    1. galqiwi
      15.07.2023 12:41
      +1

      Ещё тут такое упоминается:


  1. maikuss
    15.07.2023 12:41
    +1

    Ping tunnel ещё можно было бы изобрести. Но он тоже уже изобретён. ) http://www.cs.uit.no/~daniels/PingTunnel/


    1. adron_s
      15.07.2023 12:41
      +2

      Кстати часто операторы связи приоритезируют ICMP, чтобы скрыть перегрузки каналов связи. И "ping туннель" через таких операторов работает лучше чем UDP.


      1. blind_oracle
        15.07.2023 12:41
        +4

        В ранней сети Корбины между районами Москвы ходили только пинги и мы с друзьями туннелировались как раз через них ;)


        1. CodeDroidX Автор
          15.07.2023 12:41
          +1

          обидно бывает читать рассказы тех, кто застал рождение интернета из соединения своего компа с компом друга по проводу для quake по локалке) когда родился позже этого...


          1. megaslowpoke
            15.07.2023 12:41
            +1

            обиднее — периодически наблюдать что у кого-то в запасе больше лет чем у тебя, а старпёрские рассказы миллениалов — это всё фигня


            1. Wesha
              15.07.2023 12:41

              обиднее — периодически наблюдать что у кого-то в запасе больше лет чем у тебя

              Зачем обижаться? Я тихо радуюсь, что я буду червей кормить — а они ещё помучаются!


  1. kryptograf
    15.07.2023 12:41
    +2

    Если кому интересно, картинка на обложке из аниме "Врата штейна"


  1. shasoftX
    15.07.2023 12:41
    +1

    отправляем на другой конец земного шара и на протяжении 0.5сек он хранится, будучи в кэшах роутеров и маршрутизаторов по дороге!

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

    У вас (или на промежуточном роутере) выключился свет на N секунд и все пакеты с паролем от крипто кошелька потерялись. Б - но это уже не слово безопасность :)


    1. blind_oracle
      15.07.2023 12:41

      Поэтому нужно отправлять данные на несколько хостов сразу, репликация, очевидно же


      1. shasoftX
        15.07.2023 12:41
        +3

        А какая разница на сколько хостов, если у вас света нет, к примеру, минуту. Все хосты пришлют ответы которые потеряются раз вас нет.

        Т.е. как я понял информация жива только пока ходит запрос. Как только вас нет, то запрос потеряется и вся его информация тоже.


        1. CodeDroidX Автор
          15.07.2023 12:41

          Верно, однако зачастую информация теряется не в моем узле. Но факт остается фактом - хранить что либо таким способом не стоит.


    1. NooneAtAll3
      15.07.2023 12:41
      +1

      оно открытым текстом отправляется


      так что С-сарказм


  1. AndrewSu
    15.07.2023 12:41
    +3

    Память на линиях задержки


    1. Wesha
      15.07.2023 12:41

      Новое — это хорошо забитое забытое старое!


  1. Wesha
    15.07.2023 12:41
    +1

    пахнуло китайским алибабой...
    пахнуло китайским алибабой...


  1. pulsatrix
    15.07.2023 12:41

    Сказочка про ping.

    Задумал школьный учитель детей ping-у обучить. И умер весь интернет ненадолго. Почему не надолго? Да потому что мертвый интернет не пингуется.


  1. redfox0
    15.07.2023 12:41

    Интересная идея, но бесполезное.

    Вспомнилось, как я в своё время реализовал ICMP-туннель между двумя подсетями. Дано: две подсети A, B, между которыми отсутствуют acl - даже пинги не ходят. И есть общий ресурс в третьей подсети C, который доступен всем. Отправляем пинг на общий ресурс, подделывая IP-адрес отправителя. В итоге пакет ходит по маршруту A->C->B. Из встреченных сложностей: нужно знать все три ip-адреса, но позже ICMP-туннель оброс функционалом служебных сообщений, чтобы можно было протестировать туннель или сообщить реальный ip-адрес отправителя. Внутри туннеля бегал обычный tcp, так что потери пакетов восстанавливались им. Для браузинга интернета по выходным этого хватало.


    1. CodeDroidX Автор
      15.07.2023 12:41

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


      1. redfox0
        15.07.2023 12:41

        nat'a между подсетями не было в моём случае, просто связь между двумя машинами, на одной из которых был доступ в интернет, и на обеих админские права.


        1. CodeDroidX Автор
          15.07.2023 12:41

          nat'a между подсетями не было в моём случае

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


      1. chuvechello
        15.07.2023 12:41

        Я, конечно, могу ошибаться, но NAT никак не мешает подделыванию ip-адреса отправителя icmp-пакета, роутер просто пошлёт echo-ответ в поддельный ip-адрес (он то не в курсе ваших махинаций). На похожем принципе реализована ddos атака через icmp - smurf attack.