ByeDPI

Программа, реализующая разные методы запутывания DPI из пользовательского пространства в Linux и Windows. Предоставляет локальный SOCKS прокси сервер, не требующий для работы повышенных привилегий.

Не компилируется, к сожалению.


cc -std=c99 -O2 packets.c main.c conev.c proxy.c desync.c -I . -o ciadpi
In file included from main.c:11:
./params.h:47:26: warning: backslash-newline at end of file
   47 | #define LOG(s, str, ...) \
      |                           
main.c: In function ‘main’:
main.c:205:35: error: ‘INT_MAX’ undeclared (first use in this function)
  205 |             if (val <= 0 || val > INT_MAX/4 || *end)
      |                                   ^~~~~~~
main.c:14:1: note: ‘INT_MAX’ is defined in header ‘<limits.h>’; did you forget to ‘#include <limits.h>’?
   13 | #include <packets.h>
  +++ |+#include <limits.h>
   14 | 
main.c:205:35: note: each undeclared identifier is reported only once for each function it appears in
  205 |             if (val <= 0 || val > INT_MAX/4 || *end)
      |                                   ^~~~~~~
main.c:269:23: error: ‘INT_MIN’ undeclared (first use in this function)
  269 |             if (val < INT_MIN || val > INT_MAX || *end)
      |                       ^~~~~~~
main.c:269:23: note: ‘INT_MIN’ is defined in header ‘<limits.h>’; did you forget to ‘#include <limits.h>’?
In file included from proxy.c:21:
./params.h:47:26: warning: backslash-newline at end of file
   47 | #define LOG(s, str, ...) \
      |                           
In file included from desync.c:21:
./params.h:47:26: warning: backslash-newline at end of file
   47 | #define LOG(s, str, ...) \
      |                           
desync.c: In function ‘fake_attack’:
desync.c:18:35: warning: implicit declaration of function ‘fileno’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
   18 | #define memfd_create(name, flags) fileno(tmpfile())
      |                                   ^~~~~~
desync.c:30:15: note: in expansion of macro ‘memfd_create’
   30 |     int ffd = memfd_create("name", O_RDWR);
      |               ^~~~~~~~~~~~
desync.c:18:42: warning: implicit declaration of function ‘tmpfile’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
   18 | #define memfd_create(name, flags) fileno(tmpfile())
      |                                          ^~~~~~~
desync.c:30:15: note: in expansion of macro ‘memfd_create’
   30 |     int ffd = memfd_create("name", O_RDWR);
      |               ^~~~~~~~~~~~
desync.c:32:9: warning: implicit declaration of function ‘perror’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
   32 |         perror("memfd_create");
      |         ^~~~~~
desync.c:39:13: warning: implicit declaration of function ‘ftruncate’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
   39 |         if (ftruncate(ffd, pos) < 0) {
      |             ^~~~~~~~~
desync.c:48:9: warning: implicit declaration of function ‘memcpy’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
   48 |         memcpy(p, pkt.data, psz < pos ? psz : pos);
      |         ^~~~~~
desync.c:23:1: note: include ‘<string.h>’ or provide a declaration of ‘memcpy’
   22 | #include <packets.h>
  +++ |+#include <string.h>
   23 | 
desync.c:48:9: warning: incompatible implicit declaration of built-in function ‘memcpy’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wbuiltin-declaration-mismatch-Wbuiltin-declaration-mismatche]8;;]
   48 |         memcpy(p, pkt.data, psz < pos ? psz : pos);
      |         ^~~~~~
desync.c:48:9: note: include ‘<string.h>’ or provide a declaration of ‘memcpy’
desync.c:59:9: warning: implicit declaration of function ‘usleep’; did you mean ‘sleep’? [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
   59 |         usleep(params.sfdelay);
      |         ^~~~~~
      |         sleep
desync.c: In function ‘desync’:
./params.h:48:28: warning: implicit declaration of function ‘printf’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
   48 |     if (params.debug >= s) printf(str, ##__VA_ARGS__)
      |                            ^~~~~~
desync.c:116:5: note: in expansion of macro ‘LOG’
  116 |     LOG(LOG_S, "host: %.*s\n", len, host);
      |     ^~~
desync.c:23:1: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
   22 | #include <packets.h>
  +++ |+#include <stdio.h>
   23 | 
./params.h:48:28: warning: incompatible implicit declaration of built-in function ‘printf’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wbuiltin-declaration-mismatch-Wbuiltin-declaration-mismatche]8;;]
   48 |     if (params.debug >= s) printf(str, ##__VA_ARGS__)
      |                            ^~~~~~
desync.c:116:5: note: in expansion of macro ‘LOG’
  116 |     LOG(LOG_S, "host: %.*s\n", len, host);
      |     ^~~
./params.h:48:28: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
   48 |     if (params.debug >= s) printf(str, ##__VA_ARGS__)
      |                            ^~~~~~
desync.c:116:5: note: in expansion of macro ‘LOG’
  116 |     LOG(LOG_S, "host: %.*s\n", len, host);
      |     ^~~
desync.c:120:13: warning: implicit declaration of function ‘fprintf’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-function-declaration-Wimplicit-function-declaratione]8;;]
  120 |             fprintf(stderr, "mod http error\n");
      |             ^~~~~~~
desync.c:120:13: note: include ‘<stdio.h>’ or provide a declaration of ‘fprintf’
desync.c:120:13: warning: incompatible implicit declaration of built-in function ‘fprintf’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wbuiltin-declaration-mismatch-Wbuiltin-declaration-mismatche]8;;]
desync.c:120:13: note: include ‘<stdio.h>’ or provide a declaration of ‘fprintf’
desync.c:120:21: error: ‘stderr’ undeclared (first use in this function)
  120 |             fprintf(stderr, "mod http error\n");
      |                     ^~~~~~
desync.c:120:21: note: ‘stderr’ is defined in header ‘<stdio.h>’; did you forget to ‘#include <stdio.h>’?
desync.c:120:21: note: each undeclared identifier is reported only once for each function it appears in
./params.h:48:28: warning: incompatible implicit declaration of built-in function ‘printf’ [e]8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wbuiltin-declaration-mismatch-Wbuiltin-declaration-mismatche]8;;]
   48 |     if (params.debug >= s) printf(str, ##__VA_ARGS__)
      |                            ^~~~~~
desync.c:129:5: note: in expansion of macro ‘LOG’
  129 |     LOG(LOG_L, "split pos: %d, n: %ld\n", pos, n);
      |     ^~~
./params.h:48:28: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’
   48 |     if (params.debug >= s) printf(str, ##__VA_ARGS__)
      |                            ^~~~~~
desync.c:129:5: note: in expansion of macro ‘LOG’
  129 |     LOG(LOG_L, "split pos: %d, n: %ld\n", pos, n);
      |     ^~~
make: *** [Makefile:7: all] Error 1

Спасибо, поправил

На провайдерах МТС,Yota,Теле2,Мегафон мне кажется такой инструмент не будет работать как положено,там DPI таки конченный у этих провайдров стоит что с горем попалам Zapret работает при блокировках по домену заглушку провайдера выдает часто.

Тестировал на Tele2, работает с такими параметрами “-m disorder -s 3” или “-m fake -t 5 -s -1”

Предложение у меня по развитию программы, добавьте возможность обхода блокировки OpenVPN UDP/TCP ,ну и обход блокировки протокола Wireguard. Ну и на всякий пожарный еще обход возможной блокировки в будущем это IKEv2/IPsec

А как это реализовать в openwrt ? Хотелось бы попробовать, проверить. Какую не будь инструкцию для юзеров вы бы написали, как установить и как настроить, было бы круто, а так догадайся сам, просто не реально.
Может какие нить пакеты нужно дополнительно поставить, сколько надо памяти свободной, ведь не каждый роутер подойдёт, никто этого никогда не пишет, такие кругом пишут инструкции, что не каждый профессор поймёт, как будто памятку пишут для себя чтоб не забыть. Это же нужно быть на одной волне, а если примерно понимаешь, то и в жизни никогда не поймёшь что это, куда и зачем. Я ни разу не видел вообще нормальной инструкции, нигде и никогда

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

Если для вас инструкции того же zapret’а не являются нормальными, то в принципе мало чего можно посоветовать. Зачастую у плюс-минус популярных репозиториев инструкции на гх не сильно уступают документации линукса; но, правда, намного хуже бсд — там вообще всё прекрасно и для всех.

У хакерского ПО инструкции это либо то, что написано в самом репозитории, либо ещё есть обсуждения на разных закрытых и не очень ресурсах. Если не хотите искать и вычитывать эти ресурсы или в целом разбираться, то скорее всего вам это и не нужно, используйте готовые решения, например, покупайте впн на v2ray/xray.

По сути все, что нужно сделать, это закинуть бинарник на устройство и запустить, параметры обхода можно подобрать с помощью blockcheck из проекта zapret и затем найти аналогичные в byedpi. Так как это socks сервер, то нужно настроить браузер/систему на работу через прокси или же использовать tun2socks. На роутерах я все же рекомендовал бы использовать zapret.

Собрать на линуксе это можно так. На гитхабе зеленая кнопка Code - Local - Download ZIP - распаковать, войти в раскакованную папку в терминале командой cd (cd путь/к/byedpi)
Если установлен git, вместо скачивания zip, в терминале можно выполнить:
git clone https://github.com/hufrea/byedpi
Это склонирует репозиторий в рабочую папку.

Надо установить gcc и make с помощью пакетного менеджера дистрибутива. В Debian/Ubuntu и производных, это, например, Synaptic. Или в терминале.
sudo apt install git gcc g++ make binutils build-essential
build-essential дебиановский метапакет, он притащит с собой gcc и make.
В папке с исходниками выполнить make
Среди исходников появится бинарный файл ciadpi, всё остальное можно удалить.
Можно удалить отладочные символы, это уменьшит его размер.
strip --strip-unneeded путь/к/byedpi/ciadpi
А потом этот бинарник положить в PATH (например, /usr/local/bin или /usr/bin) на нужной системе. И тогда в терминале можно писать просто ciadpi

Но есть ограничения в переносе:

  • Новая система должна быть той свежести или новее. Если glibc будет старее, чем тот, с которым собирался бинарник, то так просто его не запустить. А посему имеет смысл собрать на более старом линуксе (например, в виртуалке).
  • Архитектура должна совпадаеть. Если openwrt на ARM, то собирать надо или в армовом линуксе (виртуалке?) или кросскомпилировать для ARM (как это сделать я точно не знаю). К слову, если собрать в 32 битном линуксе, можно запустить на 64 битном, доустановив в нем 32 битный glibc (для deb дистров: sudo dpkg --add-architecture i386 && sudo apt update && sudo apt install libc6:i386). Но не наоборот.

Это что касается компиляции. Запуск, настройка это другой вопрос. Тут пишут, что это прога предоставляет локальный прокси, который надо вписать в браузере. Для openwrt это не очень типичное использование, я так понимаю.

На МТС и Мегафон не тестили работает ли ByeDPI там?

Мне кажется эта прога в принципе не работоспособна, судя по отсутствию требований к привилегиям и после беглого просмотра исходников.
Прогон показывает, что при любых ключах -m выполняется просто split. Отсылка частями кусков данных tcp, на первый из которых ставится низкий TTL. Что по логике спровоцирует SACK со стороны сервера (ведь он не получит 1 куска) и ретрансмиссию.
Никакого disorder или fake я не наблюдаю. Потому что их невозможно отослать через обычный SOCK_STREAM.
Собственно, ничего другого ожидать и не приходится, функционал сильно напоминает tpws --socks

Или я что-то не понял ?

Верно, отправляется первый кусок с низким TTL (в частности равное одному, чтобы даже до DPI не дошел, по сути это пустышка для ОС), затем второй с нормальным, второй доходит до сервера, первый нет, ОС выполняет ретрансмисию и снова отправляет первый кусок, итог - DPI обрабатывает сначала второй кусок, а затем только первый. Вот обрезанный вывод tshark:

Спойлер
1 192.1.1.1 → 209.2.2.2 TCP 74 63 → 80 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=64
2 209.2.2.2 → 192.1.1.1 TCP 74 80 → 63 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1366 WS=128
3 192.1.1.1 → 209.2.2.2 TCP 66 63 → 80 [ACK] Seq=1 Ack=1 Win=64256 Len=0
4 192.1.1.1 → 209.2.2.2 TCP 69 63 → 80 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=3 // первый кусок с ttl=1, DPI и сервер пакет не видят
5 192.1.1.1 → 209.2.2.2 TCP 142 63 → 80 [PSH, ACK] Seq=4 Ack=1 Win=64256 Len=76 // второй кусок, сервер его получил
6 209.2.2.2 → 192.1.1.1 TCP 78 [TCP Window Update] 80 → 63 [ACK] Seq=1 Ack=1 Win=30080 SLE=4 SRE=80
7 192.1.1.1 → 209.2.2.2 TCP 69 [TCP Retransmission] 63 → 80 [PSH, ACK] Seq=1 Ack=1 Win=64256 Len=3 // опять первый кусок, на этот раз сервер его получает 
8 209.2.2.2 → 192.1.1.1 TCP 66 80 → 63 [ACK] Seq=1 Ack=80 Win=30080 Len=0
9 209.2.2.2 → 192.1.1.1 HTTP 1290 HTTP/1.1 200 OK

OK, с disorder я уже сам догадался. Но что с fake ?
Каким образом отослать fake, чтобы он дошел до DPI, но не дошел до сервера ?
Ведь все, что будет засунуто в TCP socket ОС будет доставлять до адресата, что собьет протокол

C TTL идея неплохая, может быть стоит добавить это в tpws.
Типа как лайт версия disorder без привилегий

А для fake используется вызов sendfile. Его особенность в том, что он не копирует данные в буфер ядра, а читает их напрямую из файла, притом данные во время вызова могут быть перезаписаны. И так: создаем файл в озу (memfd/tmpfile), записываем в него поддельные данные (допустим Client Hello с безобидным SNI), выставляем TTL таким, чтобы пакет прошел через DPI, но не дошел до сервера, и отправляем. Затем записываем в файл уже настоящие данные и восстанавливаем TTL - ОС не получает ответа (или получает SACK если пакет разбиваем на два) и снова отправляет файл, на этот раз в нем уже оригинальные данные и нормальный TTL - пакет успешно доходит до сервера. Конечно есть ограничение - фейк не может быть больше оригинала. Если использовать sendfile только до определенного смещение то порядок отправки будет такой:

  1. Первая фейковая часть
  2. Вторая настоящая часть
  3. Первая настоящая часть

disorder, действительно, работает. правда, на некоторых DPI на некоторых сайтах http вызывает подвисание, но это проблема некоторых DPI, и о ней мне известно.
ядро freebsd при аналогичной технике ведет себя несколько иначе, чем linux. сначала отсылает первую часть с TTL=1 (указанным), потом ретрансмиссия идет полная, а не частичная

а вот fake не похоже, что срабатывает у меня.

router: /tmp/ciadpi -p 1080 -m f
client: curl --socks5 router http://lenta.ru
router: tcpdump -ni wan -vX tcp port 80

не вижу fake. есть только split
проверил на router mips и виртуалке с ubuntu. на http и https

Дело в том, что sendfile используется для первой части пакета, а т.к. параметр split-position по умолчанию равен 3, то отправляются только первые 3 байта от фейка, стоит добавить параметр “-s -1” (разбить по предпоследнему байту)

да, так работает. думаю это надо отразить в документации, поскольку неочевидно

IP_TTL касается только ipv4
на ipv6 работает IPPROTO_IPV6, IPV6_UNICAST_HOPS
IPPROTO_IPV6, IPV6_HOPLIMIT - это не то.

ага, windows ведет себя также

спасибо, поправлю.