vpnhide/README.md
okhsunrog 8025be14cc docs: align README and dev guide with current code and CI
Six unrelated drift fixes that accumulated since they were last
synced. Each is independent of the rest:

* README{.en,}.md — kmod claim "filters /proc/net/*" trimmed to
  /proc/net/route. The other /proc/net files are SELinux-blocked
  for untrusted apps and the coverage table already says so.

* kmod/README.md — hook table and architecture note updated from
  dev_ifconf to sock_ioctl. dev_ifconf gets inlined by Clang LTO
  on GKI 5.10 so the kretprobe silently never fires; sock_ioctl
  has been the actual hook target since the vpnhide_kmod.c fix.

* zygisk/README.md — five inline hooks now, not four (recv was
  added separately because bionic's recv tail-calls recvfrom).
  Also clarified pre_app_specialize runs in the forked child, not
  zygote, matching the lifecycle block in lib.rs.

* docs/development.md — JDK requirement matches CI image (17, not
  21); document ANDROID_NDK_ROOT quirk for Gobley; CI lint list
  expanded to match what ci.yml actually runs.

* docs/development.md + lsposed/README.md — explain Gobley (the
  Gradle plugin pair that builds lsposed/native/ and bundles the
  .so + UniFFI Kotlin bindings into the APK). Previously absent
  from all *.md.
2026-04-26 15:32:30 +03:00

261 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<img src="assets/logo.png" width="200" alt="VPN Hide" />
</p>
<h1 align="center">VPN Hide</h1>
<p align="center">Скрывает активное VPN-соединение на Android от выбранных приложений.</p>
<p align="center">
<a href="https://github.com/okhsunrog/vpnhide/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/okhsunrog/vpnhide/ci.yml?label=CI" alt="CI"></a>
<a href="https://github.com/okhsunrog/vpnhide/releases/latest"><img src="https://img.shields.io/github/v/release/okhsunrog/vpnhide" alt="Release"></a>
<a href="https://github.com/okhsunrog/vpnhide/releases"><img src="https://img.shields.io/github/downloads/okhsunrog/vpnhide/total" alt="Downloads"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="License"></a>
</p>
<p align="center"><strong><a href="README.en.md">English version</a></strong></p>
## Чем vpnhide лучше аналогов?
Существующие модули, такие как [NoVPNDetect](https://bitbucket.org/yuri-project/novpndetect) и [NoVPNDetect Enhanced](https://github.com/BlueCat300/NoVPNDetectEnhanced), покрывают только **Java API** обнаружение и хукают **внутри процесса** целевого приложения через Xposed. У этого подхода две критические проблемы:
1. **Обнаружение anti-tamper защитой** — любое приложение с проверкой инъекций в память обнаруживает хуки Xposed и отказывается работать. Автор NoVPNDetect Enhanced прямо пишет: *«Модуль не будет работать если у подключаемого приложения есть защита от LSPosed, проверка на инъекции в память. Например MirPay, Т-Банк.»*
2. **Нет нативного покрытия** — приложения, использующие C/C++ код, кроссплатформенные фреймворки (Flutter, React Native) или прямые системные вызовы, могут обнаружить VPN через `ioctl`, `getifaddrs`, netlink-сокеты и `/proc/net/*`. Java-хуки эти векторы полностью пропускают.
vpnhide решает обе проблемы многослойной архитектурой:
**Уровень 1 — Java API (модуль lsposed):** хукает `system_server`, а не целевое приложение. `NetworkCapabilities`, `NetworkInfo` и `LinkProperties` фильтруются на уровне Binder *до того*, как данные попадут в процесс приложения. Приложение получает чистые данные через IPC — никаких инъекций в его процесс, нечего обнаруживать.
**Уровень 2 — нативный (kmod или zygisk):** покрывает все нативные пути обнаружения:
- **kmod** (рекомендуется) — хуки `kretprobe` на уровне ядра. Фильтрует `ioctl` (SIOCGIFFLAGS, SIOCGIFNAME, SIOCGIFCONF), `getifaddrs`/netlink-дампы (RTM_GETLINK, RTM_GETADDR) и чтение `/proc/net/route` — всё до возврата системного вызова в пользовательское пространство. Остальные файлы `/proc/net/*` (`tcp*`, `udp*`, `dev`, `if_inet6` и т.д.) на Android 10+ заблокированы SELinux для untrusted-приложений, так что отдельный хук не нужен. Нулевой след в процессе. Никаких инъекций библиотек. Нечего обнаруживать.
- **zygisk** (альтернатива) — inline-хуки `libc.so` внутри процесса приложения. То же нативное покрытие, что и kmod, но работает в процессе, поэтому теоретически обнаружим продвинутой anti-tamper защитой. Используйте, если ваше ядро не поддерживается kmod.
**Уровень 3 — дополнительные механизмы в самом приложении VPN Hide:**
- **Скрытие интерфейса** — скрывает VPN-интерфейсы, маршруты и VPN-состояние Java API от выбранных приложений
- **Скрытие портов** — блокирует выбранным приложениям доступ к `127.0.0.1` / `::1`, чтобы они не могли проверять локально запущенные VPN / proxy-демоны
- **Скрытие приложений** — скрывает выбранные приложения от выбранных приложений-наблюдателей на уровне PackageManager
Процесс целевого приложения полностью нетронут (при использовании kmod + lsposed) — ни Xposed, ни inline-хуков, ни модифицированных регионов памяти. Благодаря этому vpnhide работает с банковскими и государственными приложениями, которые активно обнаруживают и блокируют модули на основе Xposed.
## Что именно скрывает vpnhide
vpnhide — это не один переключатель, а три разных типа защиты, которые можно включать по отдельности для каждого приложения:
1. **Скрытие интерфейса** — основной слой скрытия VPN. Убирает VPN-интерфейсы и маршруты из нативных API (`ioctl`, `getifaddrs`, `/proc/net/*`, `NetworkInterface`) и из Java API (`NetworkCapabilities`, `NetworkInfo`, `LinkProperties`).
2. **Скрытие портов** — блокирует доступ к localhost для выбранных приложений, чтобы они не могли обнаружить Clash, sing-box, V2Ray, Happ и подобные инструменты через проверку локальных портов.
3. **Скрытие приложений** — позволяет скрыть выбранные установленные приложения от выбранных приложений-наблюдателей. Полезно против проверок package visibility, например когда приложение пытается определить, установлен ли на устройстве VPN или proxy-клиент.
## Какие модули нужны?
Всегда нужно **приложение VPN Hide** (`vpnhide.apk`) плюс один нативный модуль для скрытия интерфейса. Дополнительно приложение может использовать необязательный Ports-модуль для блокировки localhost-портов:
- **`kmod`** (рекомендуется) — полностью out-of-process, невидим для anti-tamper. Требуется поддерживаемое GKI-ядро.
- **`zygisk`** — используйте, если ваше ядро не поддерживается kmod.
- **`portshide`** (необязательно) — установите, если хотите блокировать выбранным приложениям доступ к localhost-портам.
См. [Установка](#установка) для пошаговой инструкции.
## Установка
Скачайте последний релиз из [Releases](https://github.com/okhsunrog/vpnhide/releases).
### Шаг 1 — Приложение VPN Hide + LSPosed
1. Установите `vpnhide.apk` как обычное приложение
2. В менеджере LSPosed включите модуль VPN Hide и добавьте **«System Framework»** в его область действия
3. Перезагрузите устройство (обязательно — хуки LSPosed внедряются в `system_server` при загрузке, поэтому модуль должен быть активен до запуска `system_server`)
4. Откройте приложение VPN Hide и предоставьте ему root-доступ (Magisk запросит автоматически; на KernelSU-Next выдайте разрешение вручную в менеджере)
### Шаг 2 — Нативный модуль для скрытия интерфейса
Откройте приложение VPN Hide. На вкладке **«Обзор»** приложение определит ваше устройство и ядро и покажет, какой именно нативный модуль нужно установить:
- Если ядро поддерживается — будет рекомендован конкретный файл kmod (например, `vpnhide-kmod-android14-6.1.zip`)
- Если нет — будет рекомендован модуль zygisk (`vpnhide-zygisk.zip`)
Установите рекомендованный модуль:
- **kmod:** через менеджер KernelSU-Next → Модули → Установить из хранилища
- **zygisk:** через менеджер KernelSU-Next или Magisk → Модули
Перезагрузите устройство после установки нативного модуля.
### Шаг 3 — Необязательно: установка Ports-модуля
Если вам нужна блокировка localhost-портов, установите `vpnhide-ports.zip` через KernelSU-Next или Magisk manager.
Этот модуль независим от kmod / zygisk и нужен только для режима **Ports** в приложении.
### Шаг 4 — Настройка защит
Откройте приложение VPN Hide → вкладка **«Защита»**.
- Режим **Tun**: используйте переключатели **L** / **K** / **Z** для управления уровнями скрытия интерфейса для каждого приложения (LSPosed, модуль ядра, Zygisk), или нажмите на строку, чтобы переключить все уровни сразу
- Режим **Apps**: выберите, какие приложения нужно скрывать и какие приложения должны выступать наблюдателями
- Режим **Ports**: выберите, каким приложениям нужно запретить доступ к localhost-портам
После изменений нажмите «Сохранить».
После изменения списка принудительно остановите и перезапустите затронутые приложения — хуки вступают в силу при следующем запуске.
> **Примечание:** некоторые приложения обнаруживают хуки Zygisk. Для таких приложений оставьте **Z** выключенным и используйте kmod + LSPosed.
<details>
<summary><b>Настройка через командную строку (для продвинутых)</b></summary>
Редактируйте `/data/adb/vpnhide_kmod/targets.txt`, `/data/adb/vpnhide_zygisk/targets.txt` или `/data/adb/vpnhide_lsposed/targets.txt` напрямую (одно имя пакета на строку). Принудительно остановите и перезапустите затронутые приложения для применения изменений.
</details>
<details>
<summary><b>Ручной подбор GKI (если хотите выбрать файл kmod самостоятельно)</b></summary>
1. На телефоне откройте **Настройки → О телефоне** и найдите строку **Версия ядра**. Она выглядит примерно так: `6.1.75-android14-11-g...`
2. Вам нужны две части из этой строки: версия ядра (`6.1`) и поколение android (`android14`). Вместе они образуют ваше поколение GKI: `android14-6.1`
3. Скачайте соответствующий файл из релиза: `vpnhide-kmod-android14-6.1.zip`
Также можно выполнить `adb shell uname -r` через ADB, чтобы увидеть строку версии ядра.
> **Важно:** `android14` в строке ядра — это НЕ версия Android, а поколение ядра. Например, все Pixel с 6 по 9a используют ядро `android14-6.1` вне зависимости от того, стоит ли на них Android 14 или 15.
</details>
## Скриншоты
| Обзор — всё ОК | Обзор — проблемы | Рекомендация установки |
|:-:|:-:|:-:|
| <img src="assets/screenshots/dashboard-all-ok.png" width="250"> | <img src="assets/screenshots/dashboard-issues.jpg" width="250"> | <img src="assets/screenshots/dashboard-install-recommendation.jpg" width="250"> |
| Защита — Tun | Скрытие приложений | Скрытие портов |
|:-:|:-:|:-:|
| <img src="assets/screenshots/protection-tunnels-list.png" width="250"> | <img src="assets/screenshots/app-hiding-list.png" width="250"> | <img src="assets/screenshots/ports-hiding-list.png" width="250"> |
| Скрытие приложений — помощь | Скрытие портов — помощь | Диагностика |
|:-:|:-:|:-:|
| <img src="assets/screenshots/app-hiding-help.png" width="250"> | <img src="assets/screenshots/ports-hiding-help.png" width="250"> | <img src="assets/screenshots/diagnostics-native.jpg" width="250"> |
## Проверка
В приложении есть встроенная система диагностики, которая автоматически обнаруживает большинство проблем с настройкой.
**Обзор** (запускается при каждом открытии приложения):
- Статус модулей для всех трёх уровней (установлен, активен, версия, количество целей)
- Валидация конфигурации LSPosed — читает базу данных LSPosed и проверяет, что VPN Hide включён, System Framework в scope, и нет лишних приложений в scope (частая ошибка при настройке)
- Обнаружение несоответствия версий — сравнивает версии установленных модулей с версией приложения и подсказывает, что именно нужно обновить
- Рекомендация нативного модуля — определяет ядро устройства и подбирает нужный файл kmod, или рекомендует zygisk, если ядро не поддерживается
- Проверка защиты в реальном времени (при активном VPN) — выполняет 16 нативных и 5 Java API проверок, чтобы убедиться, что VPN действительно скрыт
Все обнаруженные проблемы показываются в виде карточек с конкретными инструкциями по исправлению.
**Диагностика** — детальная разбивка по каждой проверке с индивидуальными результатами PASS/FAIL для всех 26 векторов обнаружения. Полезна для отладки, когда «Обзор» показывает частичную защиту.
## Компоненты
| Директория | Что | Как |
|---|---|---|
| **[kmod/](kmod/)** | Модуль ядра (C) | Хуки `kretprobe` в пространстве ядра. Нулевой след в процессе приложения. ([подробнее](kmod/README.md)) |
| **[lsposed/](lsposed/)** | LSPosed-модуль + приложение (Kotlin + Rust) | Хуки `writeToParcel` в `system_server` для per-UID фильтрации Binder. APK предоставляет обзорную панель (статус модулей, проверка версий, валидация конфигурации LSPosed, рекомендации по установке), режимы защиты для скрытия интерфейса / портов / приложений и диагностику. ([подробнее](lsposed/README.md)) |
| **[portshide/](portshide/)** | Модуль скрытия портов (Shell + iptables) | Блокирует выбранным приложениям доступ к `127.0.0.1` / `::1`, скрывая локально запущенные VPN / proxy-демоны от проверок localhost-портов. ([подробнее](portshide/README.md)) |
| **[zygisk/](zygisk/)** | Zygisk-модуль (Rust) | Inline-хуки `libc.so` в процессе приложения. Альтернатива kmod. ([подробнее](zygisk/README.md)) |
## Покрытие обнаружения
| # | Вектор обнаружения | SELinux | kmod | zygisk | lsposed |
|---|---|---|---|---|---|
| 1 | `ioctl(SIOCGIFFLAGS)` на tun0 | | x | x | |
| 2 | `ioctl(SIOCGIFNAME)` разрешение индекса в имя | | x | x | |
| 3 | `ioctl(SIOCGIFMTU)` фингерпринтинг MTU | | x | x | |
| 4 | `ioctl(SIOCGIFCONF)` перечисление интерфейсов | | x | x | |
| 5 | Все остальные `SIOCGIF*` (INDEX, HWADDR, ADDR и т.д.) | | x | x | |
| 6 | `getifaddrs()` (использует netlink внутри) | | x | x | |
| 7 | netlink `RTM_GETLINK` дамп | блок. | x | x | |
| 8 | netlink `RTM_GETADDR` дамп (IPv4 + IPv6) | блок. | x | | |
| 9 | netlink `RTM_GETROUTE` дамп | блок. | | | |
| 10 | `/proc/net/route` | блок. | x | x | |
| 11 | `/proc/net/ipv6_route` | блок. | | x | |
| 12 | `/proc/net/if_inet6` | блок. | | x | |
| 13 | `/proc/net/tcp`, `tcp6` | блок. | | | |
| 14 | `/proc/net/udp`, `udp6` | блок. | | | |
| 15 | `/proc/net/dev` | блок. | | | |
| 16 | `/proc/net/fib_trie` | блок. | | | |
| 17 | `/sys/class/net/tun0/` | блок. | | | |
| 18 | `NetworkCapabilities` (hasTransport, NOT_VPN, transportInfo) | | | | x |
| 19 | `NetworkInfo` (getType, getTypeName) | | | | x |
| 20 | `ConnectivityManager.getActiveNetwork()` | | | | x |
| 21 | `ConnectivityManager.getAllNetworks()` + VPN-сканирование | | | | x |
| 22 | `LinkProperties` (interfaceName) | | | | x |
| 23 | `LinkProperties` (маршруты через VPN-интерфейсы) | | | | x |
| 24 | `NetworkInterface.getNetworkInterfaces()` | | x | x | |
| 25 | `System.getProperty` (настройки прокси) | | | x | |
| 26 | `/proc/net/route` через Java `FileInputStream` | блок. | x | x | |
**блок.** = SELinux запрещает доступ для обычных приложений (Android 10+). Хуки не нужны.
Строки 16, 21 и 24 — единственные векторы, доступные обычным приложениям. Всё остальное либо заблокировано SELinux, либо проходит через Java API (покрывается lsposed).
## Сборка из исходников
- **kmod**: `cd kmod && make && ./build-zip.py` — см. [kmod/BUILDING.md](kmod/BUILDING.md)
- **zygisk**: `cd zygisk && ./build-zip.py` (Rust + NDK + cargo-ndk)
- **lsposed**: `cd lsposed && ./gradlew assembleDebug` (JDK 17 + Rust + NDK + cargo-ndk)
### Заметки для контрибьюторов, застрявших на Windows
Если вы используете Windows, при сборке некоторых подпроектов возникают определенные неудобства.
**lsposed**: отлично собирается в Android Studio.
**portshide**: `cd .\portshide\; python .\build-zip.py` выполняется без проблем.
Для следующих двух вам (к сожалению) потребуется установить [Docker for Windows](https://docs.docker.com/desktop/setup/install/windows-install/).
**kmod**:
```powershell
$env:KMI="android12-5.10"; docker run --rm -it -v "${PWD}:/workspace" -e KMI=$env:KMI -w /workspace "ghcr.io/ylarod/ddk-min:$($env:KMI)-20260313" bash -c 'cd kmod && python3 ./build-zip.py --kmi $KMI'
```
Обязательно используйте ту же версию образа `ylarod/ddk-min` (дата после названия KMI), которая используется в `ci.yml`.
**zygisk**:
```powershell
docker run --rm -it -v "${PWD}:/workspace" -v "vpnhide_cargo_cache:/usr/local/cargo/registry" -w /workspace ghcr.io/okhsunrog/vpnhide/ci:latest bash -c 'cd zygisk && python3 ./build-zip.py'
```
Причина, по которой `zygisk` нельзя собрать напрямую, заключается в том, что исходный код зависимости `zygisk-api` содержит файл с именем `aux.rs`. Cargo использует `libgit2` для работы с git, в котором есть защита, запрещающая создавать файлы, _содержащие_ зарезервированные слова Windows. Вы получите ошибку: `cannot checkout to invalid path 'src/aux.rs'; class=Checkout (20)`. [Сообщают](https://superuser.com/a/1929659), что после какого-то обновления стало возможным создавать файлы, содержащие зарезервированные слова, **с** расширением, но, похоже, в `libgit2` это поведение не было изменено.
## Проверено на
- [RKNHardering](https://github.com/xtclovver/RKNHardering/) — все векторы обнаружения чисты
- [YourVPNDead](https://github.com/loop-uh/yourvpndead) — все векторы обнаружения чисты
Оба реализуют официальную методику обнаружения VPN/прокси Минцифры РФ ([источник](https://t.me/ruitunion/893)).
## Раздельное туннелирование (split tunneling)
Корректно работает с конфигурациями VPN с раздельным туннелированием. Затрагиваются только приложения из списка целей.
Настоятельно рекомендуется использовать split tunneling в паре с VPN Hide.
Приложения-детекторы, сравнивающие публичный IP устройства с внешними чекерами, лучше оставлять вне туннеля — их трафик должен выходить через оператора, а не через VPN.
## Модель угроз
vpnhide скрывает активный VPN от конкретных приложений. Он НЕ предназначен для:
- Скрытия root или кастомной прошивки
- Обхода Play Integrity
- Обмана серверной детекции (утечки DNS, чёрные списки IP, фингерпринтинг латентности/TLS)
## Известные ограничения
- `kmod` требует GKI-ядро с `CONFIG_KPROBES=y` (стандарт на устройствах Android 12+)
- `lsposed` требует LSPosed, LSPosed-Next или Vector
- `zygisk` — только arm64
- Прямые системные вызовы `svc #0` обходят хуки libc в zygisk — для этого и нужен kmod
- Серверная детекция неисправима на стороне клиента — используйте раздельное туннелирование
## Лицензия
MIT. См. [LICENSE](LICENSE).
Модуль ядра объявляет `MODULE_LICENSE("GPL")`, как требуется ядром Linux для разрешения символов `EXPORT_SYMBOL_GPL` во время выполнения.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=okhsunrog/vpnhide&type=Date)](https://star-history.com/#okhsunrog/vpnhide&Date)