Туннелирование трафика внутри диалогов ВК

Это не история рабочего решения. Сам я не считаю получившийся результат пригодным к использованию, но необходимая для меня программа-минимум была выполнена.
Летом 2025го года в моём регионе по вечерам через мобильный интернет стали недоступны уже ставшие привычными сервисы - ВК, Одноклассники, Рутуб Ютуб и Телеграм. Связка Reality + Vless которую я тогда использовал не помогала справиться с проблемой. Это были белые списки.
Однако некоторые ресурсы остались доступными, главное для истории наблюдение - я мог писать и читать сообщения в ВК.
Во время вечерних прогулок стало заметно больше возможностей подумать - меня не отвлекала ни музыка, ни подкаст на ютубе, ни уведомления о пришедших сообщениях. И я прикинул - если и я и пользователь из-за рубежа могут писать в диалог и читать его содержимое, значит мы можем обмениваться информацией. А это открывает возможность для туннелирования трафика.
К тому же у ВК есть API, которое позволяет автоматизировать отправку сообщений и чтение диалога.
Технические ограничения
Есть очевидные проблемы такого решения:
Пропускная способность. У ВК есть ограничение на число API-запросов в секунду для одного пользователя. В одном сообщении в ВК может содержаться до 4096 символов в кодировке UTF-8, теоретически это даёт до 16 Кбайт на сообщение (если каждый символ — четырёхбайтный). На практике же выяснилось, что ВК портит не-ASCII текст — экранирует спецсимволы и ломает многобайтовые последовательности. Единственная надёжная кодировка для передачи бинарных данных — Base64, в которой каждый символ несёт 6 бит данных. С учётом заголовков протокола это даёт около 3 Кбайт полезной нагрузки на одно сообщение.
Задержки. И это, пожалуй, самая большая проблема. Загрузка современной веб-страницы - это тысячи tcp-пакетов, которые летят в обе стороны.
Детекция. По сигнатурам используемый протокол если сообщения не шифруются довольно легко определяется.
К счастью, для сообществ лимиты ВК намного мягче. Бот сообщества может отправить тысячи API-запросов в секунду. Это вполне резонно, ведь одно сообщество может единомоментно взаимодействовать с тысячами чатов. К сожалению, есть лимиты для API-запросов на отправку сообщения в один и тот же чат, слать от имени сообщества в один чат сообщения можно с ограниченной скоростью. Публичной информации с чётким описанием лимитов я не нашёл, но эмпирически это что-то вроде 4 сообщений в секунду в один чат.
Решить это можно, например, созданием множества чатов с их циклической заменой. Каждый чат повышает нашу пропускную способность на эти 4 сообщения в секунду.
К тому же помимо текста мы можем слать ещё и документы. Для отправки документа нужно 4 запроса вместо одного (получение URL загрузки, HTTP-загрузка файла на сервер ВК, сохранение документа через API, отправка сообщения с вложением), но зато нести на себе документ может намного больший информационный объём. Так что если в буфере накопится большой объём данных, можно прибегнуть к загрузке документа вместо отправки кучи сообщений.
Грубый расчёт даёт, что при использовании 16 чатов мы сможем иметь скорости на уровне полутора мегабит в секунду (16 чатов × 4 сообщения/с × 3 КБ на сообщение ≈ 192 КБ/с), чего вполне достаточно для сёрфинга в интернете и даже просмотра видео в приемлемом качестве.
А вот проблему с задержками решить не получится никак, это техническое ограничение самого выбранного подхода, ведь цикл обмена одним “пакетом” занимает 2 API-запроса и ещё одно LongPoll уведомление от сервера ВК о пришедшем сообщении. Даже если время одного запроса/уведомления 1/4 секунды, это уже даёт дополнительный пинг в 750 мсек.
Проблему детектирования можно было бы решить общим секретом — клиент и сервер договариваются о ключе шифрования ещё до установки первого соединения. В таком случае детекция сильно осложняется, каких-то типовых сигнатур внутри сообщений больше нет, остаётся анализировать энтропию или паттерны поведения аккаунта, что ресурсоёмко. В текущей реализации шифрование реализовано посредством AES-256-GCM.
Архитектура
Чтобы не изобретать велосипед, можно использовать протокол SOCKS5 для организации проксирования. Наша задача тогда будет сведена к описанию работы транспорта — вместо того чтобы по-человечески обмениваться информацией через TCP, нам придётся заворачивать трафик внутрь сообщений VK. В силу описанных выше ограничений трафик придётся буферизировать и слать чанками. В случае если в буфере накопилось много данных, будем отгружать всё разом через документ.
На клиентской стороне на каком-то порту слушает SOCKS5-сервер, в него свой трафик заворачивают приложения, дальше этот сервер упаковывает трафик в сообщения ВК. В нормальной юрисдикции на VDS’ке запущена серверная часть, она слушает диалоги, ждёт запрос на открытие соединения, когда запрос получен — начинает распаковывать из диалогов поступающий трафик и слать во внешний мир.
Обнаружение чатов
Клиент и сервер не знают заранее, какие чаты они разделяют. При запуске клиент сканирует доступные чаты и рассылает в них ready-сообщения с кодовой фразой. Сервер слушает через Long Poll, получает эти сообщения и отвечает ready_ack. Так за несколько секунд они динамически собирают пул общих чатов без ручной конфигурации.
Жизненный цикл сессии
Каждое TCP-соединение от браузера получает UUID и проходит через управляющие сообщения: connect → ready/ready_ack → обмен data → close, состояние соединения приходится эмулировать поверх сообщений VK.
Отправка данных
Пул из 16 sender-воркеров отправляет пакеты через чаты по кругу (round-robin). Это распределяет нагрузку и обходит per-chat rate limit. Если данные не влезают в одно текстовое сообщение, они разбиваются на части с заголовком Part: N/M. Если в буфере накопилось больше порогового значения (по умолчанию 6 КБ), данные отправляются как документ.
URL загрузки документов кэшируется на 5 минут для каждой пары (токен, чат), что снижает стоимость отправки документа с 4 до 3 запросов в устойчивом режиме. Если загрузка документа не удалась (ВК периодически отвечает 405), предпринимаются ещё 2 попытки со свежим URL. Если и они провалились, данные автоматически дробятся на текстовые сообщения — медленнее, но без потери пакетов.
Приём и сборка
Сообщения через разные чаты приходят не по порядку. Принимающая сторона собирает пакеты в буфер, упорядочивает по sequence number и выдаёт TCP-стеку строго последовательно. Многочастевые сообщения (Part: N/M) склеиваются до передачи в TCP-поток.
Языком реализации выбран Go. Потому что он быстрый и под него есть готовые библиотеки под нашу задачу.
Реализация
Я не буду лукавить, весь код был написан нейронкой по описанию таски. Ознакомиться можно на моём GitHub.
Вместо постскриптума
Реализация была готова ещё летом 2025го, тогда я продемонстрировал её своим друзьям. К сожалению, мобильное приложение сделать тогда было невозможно, поэтому чтобы выйти в интернет вне дома пришлось бы тащить с собой ноут. Но необходимость отпала, буквально через неделю я встретился со знакомой из Санкт-Петербурга, которая показала мне прилично работающий даже в моих условиях VPN-клиент на основе vless с промежуточным хостом с ip из белых списков. Тогда такую VDS’ку можно было выбить у хостинг-провайдров за пару десятков минут перебора.
Когда же решения на vless перестали работать (минцифры сильно сузило подсети в белых списках), я нашёл другое — туннелирование трафика внутри webrtc-датаканала. Эта идея мне пришла ещё летом 25го, но не хватило знаний для реализации.