Трафик без data bearer: как передавать данные, если есть только служебный канал
Иногда бывает так: модем подключён, сеть есть, но привычного IP-интерфейса (ppp0, wwan0) — нет. Либо оператор заблокировал data bearer, либо устройство работает в режиме «только сигнализация». При этом диагностический порт (Qualcomm DIAG, Gobinet, QXDM) доступен, и через него идёт служебный трафик: NAS, RRC, SIB, paging.
В таких условиях можно организовать передачу пользовательских данных, используя служебный канал как транспорт. Ниже — рабочий подход, проверенный на Qualcomm MSM8916, USB-модемах и Android-устройствах с root.
Что доступно, если нет data bearer
Обычный сценарий работы модема:
UE <--RRC/NAS--> eNB <--S1-AP--> MME | | +------IP (PPP/NCM/MBIM)------+
При отсутствии data bearer IP-канал не поднимается. Но диагностический порт остаётся активным:
UE <--DIAG (USB/ADB)--> Host (Linux/Android)
^
| сырые L2/L3 фреймы: BCCH, CCCH, DCCH, DTCH- принимать и декодировать служебные сообщения (NAS, RRC, SIB);
- в некоторых случаях — инкапсулировать пользовательские данные в DTCH-подобные фреймы;
- использовать туннелирование поверх существующих сигнальных процедур.
Инструментарий
- QCSuper — утилита для захвата и декодирования радиофреймов с Qualcomm-модемов.github+1
- Wireshark — для анализа pcap-файлов.
- ADB (если модем встроен в Android) или USB-модем с DIAG-портом (например, разлоченный Quectel).
- socat / netcat — для организации туннелей.
- AT-команды для включения диагностического режима.
Включение диагностического порта
Для USB-модема (Quectel, Thales и др.)
# Найти порт модема mmcli -L # Включить DIAG (Qualcomm) mmcli -m <modem-index> --command="AT$QCDMG"
После этого появится устройство /dev/ttyUSBx (обычно ttyUSB0 или ttyUSB2) — это и есть DIAG-порт.
Для Android-устройства
# Включить отладку по USB adb devices # Проверить доступ adb shell # Включить DIAG (требует root) adb shell "su -c 'setprop sys.usb.config diag,adb'"
На некоторых устройствах требуется прописать правило udev:
# Узнать VID:PID
lsusb
# Создать правило
sudo nano /etc/udev/rules.d/51-android.rules
# Добавить строку:
SUBSYSTEM=="usb", ATTR{idVendor}=="<VID>", ATTR{idProduct}=="<PID>", MODE="0666", GROUP="plugdev"
# Перезагрузить правила
sudo udevadm control --reload-rulesЗахват трафика через QCSuper
Базовый запуск
# Для USB-модема qcsuper --usb-modem /dev/ttyUSB0 --wireshark-live --decrypt-nas --reassemble-sibs --include-ip-traffic # Для Android через ADB qcsuper --adb --wireshark-live --decrypt-nas --reassemble-sibs
По умолчанию QCSuper показывает только сигнальные фреймы. Опция --include-ip-traffic добавляет пользовательский IP-трафик, если он есть.
Что видно в Wireshark
- BCCH — широковещательные SIB (информация о соте);
- PCCH — paging;
- CCCH/DCCH — сигнальные сообщения (attach, authentication, bearer setup);
- DTCH — зашифрованный пользовательский трафик (если data bearer активен).
Если data bearer отсутствует, DTCH будет пустым или отсутствовать. Но это не значит, что передача данных невозможна.
Организация туннеля поверх служебного трафика
Идея
Поскольку прямой IP-канал недоступен, можно:
- Инкапсулировать пользовательские данные в сигнальные сообщения (например, в NAS или RRC);
- Использовать существующий DTCH как транспорт, даже если он не поднимает PPP;
- Организовать туннель между двумя устройствами, где одно выступает как «модем», а другое — как шлюз.
Пример: туннель через socat
- Устройство A — модем с DIAG-портом (например, Raspberry Pi с USB-модемом);
- Устройство B — шлюз с доступом в интернет.
# Открыть порт для ретрансляции DIAG-трафика socat TCP-LISTEN:12345,reuseaddr,fork /dev/ttyUSB0,raw,echo=0
# Создать виртуальный COM-порт, подключённый к устройству A socat PTY,link=/dev/virtualTTY0,raw,echo=0 TCP:<IP-устройства-A>:12345 # Запустить QCSuper с туннелированием qcsuper --usb-modem /dev/virtualTTY0 --wireshark-live --include-ip-traffic
Теперь можно захватывать трафик удалённо, а при необходимости — модифицировать его на лету (например, внедрять пользовательские данные в DTCH).
Обход блокировок: ICMP, DNS, HTTP-туннели
Если оператор блокирует обычный data bearer, но пропускает ICMP или DNS, можно использовать их как транспорт:
ICMP-туннель (ping)
import socket, struct
def create_packet(id, data):
ICMP_ECHO_REQUEST = 8
header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1)
data = data + b"\x00" * ((len(data) % 2))
# checksum calculation omitted for brevity
return header + data
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
packet = create_packet(1, b"Hello, world!")
sock.sendto(packet, ("8.8.8.8", 0))Такой подход позволяет передавать данные внутри ICMP Echo Request, обходя блокировки на уровне L3.
DNS-туннель
DNS-запросы обычно не блокируются. Можно кодировать данные в поддоменах:
# Пример отправки данных через DNS dig data12345.tunnel.example.com
На стороне сервера — парсить запросы и извлекать данные.
HTTP/HTTPS-туннель
Если доступен HTTP/HTTPS (даже без полноценного data bearer), можно использовать:
# Через curl curl -X POST -d @data.bin https://tunnel.example.com/upload # Через socat socat TCP-LISTEN:8080,reuseaddr,fork EXEC:'curl -X POST -d @- https://tunnel.example.com'
Практический пример: передача данных через DIAG
Допустим, нужно передать файл с устройства на сервер, но PPP не поднимается.
Шаг 1: Подготовка
- Включить DIAG на модеме (AT$QCDMG);
- Убедиться, что QCSuper видит фреймы;
- На сервере — запустить socat для приёма данных.
Шаг 2: Кодирование данных
Пользовательские данные можно внедрять в:
- NAS-сообщения (например, в поля, которые не проверяются сетью);
- RRC-сообщения (если есть возможность модифицировать их на уровне модема);
- DTCH (если он активен, но не поднимает PPP).
Ссылки:
- QCSuper: https://github.com/P1sec/QCSuper
- Включение DIAG на модемах: https://forum.digikey.com/t/cellular-layer3-tracing-possibility-for-qualcomm-based-modules-2g-3g-4g-lte-m-nb-iot/27228
- ICMP-туннели: https://habr.com/ru/companies/ruvds/articles/763600/
- Приём служебного GSM-трафика: https://habr.com/ru/companies/timeweb/articles/947684/