[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-74728":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":25,"hasPages":23,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":34,"readmeContent":35,"aiSummary":36,"trendingCount":16,"starSnapshotCount":16,"syncStatus":37,"lastSyncTime":38,"discoverSource":39},74728,"RKNHardering","xtclovver\u002FRKNHardering","xtclovver","Обнаружние VPN конфигураторов на пользовательских устройствах с дальнейшей целью защиты от обнаружения","https:\u002F\u002Fmatrix.to\u002F#\u002F#RKN_Hardering:matrix.kangel.tech",null,"Kotlin",1310,58,9,4,0,10,14,76,30,87.91,"GNU Affero General Public License v3.0",false,"main",true,[27,28,29,30,31,32,33],"rkn","rknhardering","vless","vpn","vpndetection","vpndetector","xray","2026-06-12 04:01:15","> **Язык \u002F Language:** [Русский](README.md) | [English](docs\u002FREADME.en.md) | [فارسی](docs\u002FREADME.fa.md) | [中文](docs\u002FREADME.zh-CN.md)\n\n# RKNHardering\n\n\u003Ca href=\"https:\u002F\u002Fmatrix.to\u002F#\u002F%23RKN_Hardering:matrix.kangel.tech\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fmatrix-%23000000?style=for-the-badge&logo=matrix&logoColor=white\" alt=\"Matrix\" width=\"200\">\u003C\u002Fa>\n\nAndroid-приложение для обнаружения VPN и прокси на устройстве. Реализует методику РКН по выявлению средств обхода блокировок.\n\nМинимальная версия Android: 8.0 (API 26).\n\n## Нужна помощь сообщества \u002F Community Help Wanted\n\n> **RU**\n\nЭтот проект документирует методы обнаружения VPN и прокси на Android-устройствах. Однако **обратная задача**, как предотвратить детектирование наличия VPN, исследована значительно хуже.\n\nЯ ищу людей, готовых помочь собрать, систематизировать и протестировать информацию о способах обхода детектов, включая, но не ограничиваясь:\n\n- **Маскировка сетевых интерфейсов** (как скрыть `tun0`, `wg0` и другие VPN-подобные интерфейсы от `NetworkInterface.getNetworkInterfaces()` и `\u002Fproc\u002Fnet\u002Froute`)\n- **Подмена NetworkCapabilities** (способы убрать `TRANSPORT_VPN`, `IS_VPN`, `VpnTransportInfo` из ответов `ConnectivityManager`)\n- **Скрытие от dumpsys** (предотвращение утечки информации через `dumpsys vpn_management` и `dumpsys activity services android.net.VpnService`)\n- **MTU-нормализация** (выставление стандартного MTU (1500) для туннельных интерфейсов на различных клиентах)\n- **DNS-утечки** (предотвращение обнаружения loopback\u002Fprivate DNS при активном VPN)\n- **Скрытие localhost-прокси** (как предотвратить обнаружение через `\u002Fproc\u002Fnet\u002Ftcp` и сканирование портов)\n- **Обход нативных проверок** (противодействие JNI-проверкам через `\u002Fproc\u002Fself\u002Fmaps`, `getifaddrs()`, `dlsym`)\n- **Маскировка установленных приложений** (скрытие пакетов VPN-приложений от `PackageManager`)\n\nЕсли вы обладаете знаниями в этих областях, пожалуйста, откройте Issue или Pull Request, либо напишите в [чат Matrix](https:\u002F\u002Fmatrix.to\u002F#\u002F%23RKN_Hardering:matrix.kangel.tech) с описанием метода, условий применимости и ограничений. Любая информация ценна — от теоретических идей до работающих PoC.\n\n> **EN**\n\nThis project documents methods for detecting VPNs and proxies on Android devices. However, **the inverse problem** how to prevent the detection of an active VPN has been studied much less thoroughly.\n\nI am looking for people willing to help collect, organize, and test information about ways to bypass detection, including, but not limited to:\n\n- **Network interface masking** (how to hide `tun0`, `wg0`, and other VPN-like interfaces from `NetworkInterface.getNetworkInterfaces()` and `\u002Fproc\u002Fnet\u002Froute`)\n- **NetworkCapabilities spoofing** (ways to remove `TRANSPORT_VPN`, `IS_VPN`, and `VpnTransportInfo` from `ConnectivityManager` responses)\n- **Hiding from dumpsys** (preventing information leakage through `dumpsys vpn_management` and `dumpsys activity services android.net.VpnService`)\n- **MTU normalization** (setting a standard MTU of 1500 for tunnel interfaces across different clients)\n- **DNS leaks** (preventing detection of loopback\u002Fprivate DNS while a VPN is active)\n- **Hiding localhost proxies** (how to prevent detection via `\u002Fproc\u002Fnet\u002Ftcp` and port scanning)\n- **Bypassing native checks** (countering JNI-based checks through `\u002Fproc\u002Fself\u002Fmaps`, `getifaddrs()`, and `dlsym`)\n- **Masking installed applications** (hiding VPN app packages from `PackageManager`)\n\nIf you have expertise in these areas, please open an Issue or Pull Request describing the method, the conditions under which it applies, and its limitations. Any information is valuable, from theoretical ideas to working PoCs.\n\n## Архитектура\n\nНезависимые модули проверки запускаются параллельно. Итоговый вердикт рассчитывается в `VerdictEngine`.\n\n`IpComparisonChecker` сохраняется в результат и показывается в UI как диагностический блок, но в текущей версии не участвует в `VerdictEngine`.\n\n```text\nVpnCheckRunner\n├── GeoIpChecker           — GeoIP + hosting\u002Fproxy-сигналы\n├── IpComparisonChecker    — RU\u002Fне-RU IP-чекеры (диагностика)\n├── DirectSignsChecker     — NetworkCapabilities, системный proxy, установленные VPN apps\n├── IndirectSignsChecker   — интерфейсы, маршруты, DNS, dumpsys, proxy-tech signals\n├── CallTransportChecker   — STUN\u002FMTProto (утечки и доступность)\n├── CdnPullingChecker      — HTTPS-запросы к CDN\u002Fredirector\n├── LocationSignalsChecker — MCC\u002FSIM\u002Fcell\u002FWi-Fi\u002FBeaconDB\n├── BypassChecker          — localhost proxy, Xray gRPC API, underlying-network leak\n├── RttTriangulationChecker — SNITCH (β): RTT-триангуляция по RU\u002Fиностранным хостам\n└── NativeSignsChecker     — JNI-проверки (маршруты, интерфейсы, хуки, root)\n        └── VerdictEngine  — логика итогового вердикта\n```\n\n---\n\n## Модули проверки\n\n### 1. GeoIP (`GeoIpChecker`)\n\nИсточники:\n\n- `https:\u002F\u002Fapi.ipapi.is\u002F` — основной источник полей GeoIP и сигналов proxy\u002FVPN\u002FTor\u002Fdatacenter\n- `https:\u002F\u002Fwww.iplocate.io\u002Fapi\u002Flookup` — fallback-источник полей GeoIP и дополнительный голос за hosting (`privacy.is_hosting`)\n\nЛогика:\n\n| Сигнал | Что делает код | Итог |\n|--------|----------------|------|\n| `countryCode != RU` | IP считается иностранным | `needsReview`, если одновременно нет `hosting` и `proxy` |\n| `hosting` | Используется majority vote по совместимым ответам одного и того же IP (`ipapi.is`, `iplocate.io`) | `detected = true`, если большинство совместимых источников говорят `hosting=true` |\n| `proxy` | Используются совместимые HTTPS-провайдеры (`ipapi.is`, `iplocate.io`) | `detected = true`, если хотя бы один совместимый провайдер говорит о proxy\u002FVPN\u002FTor |\n| `country`, `isp`, `org`, `as`, `query` | Берутся из `ipapi.is`, а недостающие поля заполняются из `iplocate.io` только для совместимого IP | не влияют напрямую |\n\nИтог категории:\n\n- `detected = isHosting || isProxy`\n- `needsReview = foreignIp && !isHosting && !isProxy`\n\nТаймаут соединения и чтения для HTTP(S)-запросов: 10 секунд. `GeoIpChecker` использует только HTTPS-провайдеры и возвращает ошибку только если ни один GeoIP-провайдер не ответил данными.\n\n---\n\n### 2. Сравнение IP-чекеров (`IpComparisonChecker`)\n\nМодуль сравнивает ответы RU- и не-RU публичных IP-чекеров. Это диагностический блок: он отображается в UI, но сейчас не участвует в `VerdictEngine`.\n\nГруппы сервисов:\n\n| Группа | Сервисы |\n|--------|---------|\n| `RU` | `Yandex IPv4`, `2ip.ru`, `Yandex IPv6` |\n| `NON_RU` | `ifconfig.me IPv4`, `ifconfig.me IPv6`, `checkip.amazonaws.com`, `ipify`, `ip.sb IPv4`, `ip.sb IPv6` |\n\nЛогика:\n\n- внутри каждой группы строится `canonicalIp`, если сервисы согласованы;\n- несовпадение IP внутри группы, частичные ответы и конфликт семейств `IPv4\u002FIPv6` переводят группу в `needsReview` или `detected` в зависимости от полноты данных;\n- общий `detected` ставится только если обе группы дали полный консенсус внутри себя, но RU- и не-RU группы вернули разные canonical IP;\n- ожидаемые ошибки IPv6-эндпоинтов могут игнорироваться и не ломают консенсус IPv4.\n\n---\n\n### 3. Прямые признаки (`DirectSignsChecker`)\n\nСистемные признаки без активного сетевого сканирования localhost.\n\n#### 3.1 NetworkCapabilities (`checkVpnTransport`)\n\nAPI: `ConnectivityManager.getNetworkCapabilities(activeNetwork)`\n\n| Проверка | Метод\u002Fполе | Итог |\n|----------|------------|------|\n| `NetworkCapabilities.TRANSPORT_VPN` | `caps.hasTransport(TRANSPORT_VPN)` | `detected = true` |\n| `IS_VPN` | `caps.toString().contains(\"IS_VPN\")` | `detected = true` |\n| `VpnTransportInfo` | `caps.toString().contains(\"VpnTransportInfo\")` | `detected = true` |\n\n`IS_VPN` и `VpnTransportInfo` проверяются через строковое представление `NetworkCapabilities`.\n\n#### 3.2 Системный proxy (`checkSystemProxy`)\n\nИспользуются:\n\n- `System.getProperty(\"http.proxyHost\")` с fallback на `Proxy.getDefaultHost()`\n- `System.getProperty(\"http.proxyPort\")` с fallback на `Proxy.getDefaultPort()`\n- `System.getProperty(\"socksProxyHost\")`\n- `System.getProperty(\"socksProxyPort\")`\n- `ConnectivityManager.getDefaultProxy()`\n- `ConnectivityManager.allNetworks` + `ConnectivityManager.getLinkProperties(network).httpProxy`\n- `ProxyInfo.getPacFileUrl()`, `ProxyInfo.getExclusionList()`, на API 30+ также `ProxyInfo.isValid()`\n\nЛогика:\n\n| Состояние | Итог |\n|-----------|------|\n| host отсутствует | proxy считается ненастроенным |\n| host есть, но порт невалиден | `needsReview = true` |\n| host есть и порт валиден | `detected = true` |\n| порт относится к известным proxy-портам | добавляется отдельная находка |\n| `ProxyInfo` содержит `PAC URL` | `detected = true` |\n| `ProxyInfo` задан на конкретной сети | в вывод добавляется отдельная строка с interface name |\n| `ProxyInfo` содержит exclusion list | exclusions показываются пользователю |\n| на API 30+ `!ProxyInfo.isValid()` | `needsReview = true` без detected-evidence |\n| на отслеживаемых сетях `ProxyInfo` не найден | добавляется агрегированная строка `ProxyInfo ...: не обнаружен` |\n\nИзвестные proxy-порты: `80`, `443`, `1080`, `3127`, `3128`, `4080`, `5555`, `7000`, `7044`, `8000`, `8080`, `8081`, `8082`, `8888`, `9000`, `9050`, `9051`, `9150`, `12345`, а также диапазон `16000..16100`.\n\n#### 3.3 Установленные VPN\u002FProxy-приложения (`InstalledVpnAppDetector`)\n\nМодуль проверяет три источника:\n\n- известные сигнатуры пакетов из [`VpnAppCatalog`](app\u002Fsrc\u002Fmain\u002Fjava\u002Fcom\u002Fnotcvnt\u002Frknhardering\u002Fvpn\u002FVpnAppCatalog.kt);\n- приложения, которые объявляют `VpnService.SERVICE_INTERFACE` через `PackageManager.queryIntentServices`.\n- в приложении есть \"VPN\" в названии (это конечно не на 100% дает что это впн)\nЭто диагностические сигналы установки или декларации `VpnService`, а не подтверждение активного туннеля. Совпадения переводят категорию в `needsReview`, но сами по себе не делают `DirectSignsChecker.detected = true`.\n\n---\n\n### 4. Косвенные признаки (`IndirectSignsChecker`)\n\n#### 4.1 Capability `NOT_VPN` (`checkNotVpnCapability`)\n\n`ConnectivityManager.getNetworkCapabilities(activeNetwork).toString()` проверяется на наличие строки `NOT_VPN`.\n\n| Результат | Итог |\n|-----------|------|\n| `NOT_VPN` присутствует | норма |\n| `NOT_VPN` отсутствует | `detected = true` |\n\n#### 4.2 Сетевые интерфейсы (`checkNetworkInterfaces`)\n\nAPI: `NetworkInterface.getNetworkInterfaces()`. Проверяются активные (`isUp`) интерфейсы.\n\nПаттерны VPN-подобных интерфейсов:\n\n- `tun\\d+`\n- `tap\\d+`\n- `wg\\d+`\n- `ppp\\d+`\n- `ipsec.*`\n\nЛюбой активный интерфейс, попавший под эти паттерны, даёт `detected = true`.\n\n#### 4.3 Аномалия MTU (`checkMtu`)\n\nЛогика:\n\n| Условие | Итог |\n|---------|------|\n| VPN-подобный интерфейс с MTU `1..1499` | `detected = true` |\n| Нестандартный активный интерфейс (не `wlan.*`, `rmnet.*`, `eth.*`, `lo`) с MTU `1..1499` | `detected = true` |\n\n#### 4.4 Маршрутизация (`checkRoutingTable`)\n\nИсточник данных:\n\n- в первую очередь `LinkProperties.routes` из Android API;\n- fallback: `\u002Fproc\u002Fnet\u002Froute`, если через API не удалось получить default route.\n\nДетекты:\n\n- default route через нестандартный интерфейс;\n- выделенные non-default routes через VPN\u002Fнестандартный интерфейс;\n- паттерн split tunneling: одновременно видны tunnel routes и обычный default route через стандартную сеть.\n\nDefault route через `wlan.*`, `rmnet.*`, `eth.*`, `lo` считается нормой, если сама сеть не помечена как VPN.\n\n#### 4.5 DNS (`checkDns`)\n\nAPI: `ConnectivityManager.getLinkProperties(activeNetwork).dnsServers`.\n\nDNS оценивается вместе со snapshot underlying-сетей, если они доступны.\n\n| Сигнал | Итог |\n|--------|------|\n| loopback DNS (`127.x.x.x`, `::1`) | `detected = true` |\n| private DNS, унаследованный из той же private\u002FULA-подсети основной non-VPN сети | норма |\n| private DNS при активном VPN и отличии от underlying сети | `detected = true` |\n| private DNS без достаточного контекста | `needsReview = true` |\n| public DNS, заменённый при активном VPN | `needsReview = true` |\n| link-local (`169.254.x.x`, `fe80::\u002F10`) | информационно |\n\n#### 4.6 Дополнительные proxy-технические сигналы (`checkProxyTechnicalSignals`)\n\nПроверяются:\n\n- установленные proxy-only утилиты из `VpnAppCatalog` с сигналом `LOCAL_PROXY` без `VPN_SERVICE`;\n- локальные listeners из `\u002Fproc\u002Fnet\u002Ftcp`, `\u002Fproc\u002Fnet\u002Ftcp6`, `\u002Fproc\u002Fnet\u002Fudp`, `\u002Fproc\u002Fnet\u002Fudp6` на известных proxy-портах;\n- большое число localhost listeners на высоких портах.\n\nЛогика:\n\n- listener на известном localhost proxy-порту даёт `detected = true`;\n- наличие proxy-only утилиты или множества localhost listeners даёт `needsReview = true`.\n\nОтдельно фиксируется ограничение: проверки процессов, `iptables`\u002F`pf` и системных сертификатов неполны без root\u002Fprivileged access.\n\n#### 4.7 `dumpsys vpn_management` (`checkDumpsysVpn`)\n\nТолько Android 12+ (API 31+). Запускается `dumpsys vpn_management`.\n\nЕсли парсер (`VpnDumpsysParser`) находит активные записи VPN, они дают `detected = true`. Из записей извлекается пакет, затем он сопоставляется с `VpnAppCatalog`:\n\n- известный пакет: высокая уверенность;\n- неизвестный пакет: `detected = true` и одновременно `needsReview = true`.\n\nПустой вывод, `Permission Denial` или недоступность сервиса считаются отсутствием детекта.\n\n#### 4.8 `dumpsys activity services android.net.VpnService` (`checkDumpsysVpnService`)\n\nЗапускается `dumpsys activity services android.net.VpnService`.\n\nЕсли найдены активные `VpnService`, создаются `activeApps` и evidence:\n\n- известный пакет из каталога: высокая уверенность;\n- неизвестный пакет: `detected = true` и `needsReview = true`.\n\nПустой вывод или отсутствие записей `VpnService` детекта не дают.\n\n---\n\n### 5. Сигналы местоположения (`LocationSignalsChecker`)\n\nМодуль собирает признаки, подтверждающие, что устройство физически находится в РФ или, наоборот, что telephony-сигналы выглядят нетипично.\n\nИсточники:\n\n- `TelephonyManager.networkOperator`, `networkCountryIso`, `networkOperatorName`\n- `TelephonyManager.simOperator`, `simCountryIso`, `isNetworkRoaming`\n- `requestCellInfoUpdate` \u002F `allCellInfo`\n- `WifiManager.scanResults` и текущий `BSSID`\n- `BeaconDB` (`https:\u002F\u002Fapi.beacondb.net\u002Fv1\u002Fgeolocate`) для cell\u002FWi-Fi geolocation\n- reverse geocoding для `countryCode`\n\nРазрешения:\n\n- `ACCESS_FINE_LOCATION` нужен для cell lookup;\n- на Android 13+ `NEARBY_WIFI_DEVICES` нужен для Wi-Fi lookup.\n\nЛогика:\n\n| Сигнал | Итог |\n|--------|------|\n| `networkMcc == 250` | добавляется служебная находка `network_mcc_ru:true` |\n| `BeaconDB`\u002Freverse geocode вернул `RU` | добавляются `cell_country_ru:true` и `location_country_ru:true` |\n| `networkMcc != 250` | `needsReview = true` |\n| отсутствие разрешений или radio data | информационно |\n\nВ текущей реализации `LocationSignalsChecker.detected` всегда `false`. Его основная роль в `VerdictEngine` — подтверждать Россию и усиливать иностранный GeoIP-сигнал.\n\n---\n\n### 6. Bypass-проверка (`BypassChecker`)\n\nТри проверки запускаются параллельно:\n\n- `ProxyScanner`\n- `XrayApiScanner`\n- `UnderlyingNetworkProber`\n\n#### 6.1 Сканер прокси (`ProxyScanner` + `ProxyProber`)\n\nСканируются `127.0.0.1` и `::1`.\n\nРежимы:\n\n| Режим | Описание |\n|-------|----------|\n| `AUTO` | сначала популярные порты, затем полный диапазон |\n| `MANUAL` | проверка одного указанного порта |\n\nПопулярные порты в `AUTO` формируются из `VpnAppCatalog.localhostProxyPorts` и дополнительно включают `1081`, `7890`, `7891`.\n\nПолное сканирование:\n\n- диапазон `1024..65535`\n- параллельность `200`\n- таймаут соединения `80 мс`\n- таймаут чтения `120 мс`\n\nОпределяются только proxy без аутентификации:\n\n| Тип | Как определяется |\n|-----|------------------|\n| `SOCKS5` | greeting `0x05 0x01 0x00` и ответ `0x05 0x00` |\n| `HTTP CONNECT` | `CONNECT ifconfig.me:443 HTTP\u002F1.1` и ответ `HTTP\u002F1.x 200` |\n\nОткрытый localhost proxy сам по себе не считается подтверждённым обходом: он фиксируется как `needsReview`. Подтверждение обхода ставится только если удалось получить прямой IP и IP через proxy, и они различаются.\n\nДополнительно:\n\n- если найден `SOCKS5`, но HTTP-получение IP через него не удалось и порт не похож на Xray, запускается `MtProtoProber`;\n- успешный MTProto probe добавляет информативную находку, но не влияет на итоговый verdict.\n\n#### 6.2 Сканер Xray gRPC API (`XrayApiScanner` + `XrayApiClient`)\n\nСканируются `127.0.0.1` и `::1`.\n\nПараметры:\n\n- диапазон `1024..65535`\n- параллельность `100`\n- TCP connect timeout `200 мс`\n- gRPC deadline `2000 мс` с повтором на увеличенном дедлайне\n\nПроверка выполняется не через сырой HTTP\u002F2 preface, а через реальный gRPC-вызов `HandlerServiceGrpc.listOutbounds(...)`.\n\nПри успехе:\n\n- endpoint даёт `detected = true`;\n- в findings добавляются до 10 summary по outbound'ам (`tag`, `protocol`, `address`, `port`, `sni`) и счётчик оставшихся.\n\n#### 6.3 Underlying network leak \u002F VPN network binding (`UnderlyingNetworkProber`)\n\nЕсли на устройстве активен VPN, модуль:\n\n- перебирает все `ConnectivityManager.allNetworks`;\n- ищет internet-capable сеть без `TRANSPORT_VPN`;\n- привязывает HTTP(S)-запросы к этой сети;\n- запрашивает публичный IP через `ifconfig.me`, `checkip.amazonaws.com`, `ipv4-internet.yandex.net`, `ipv6-internet.yandex.net`.\n\nЕсли underlying-сеть доступна при активном VPN, это трактуется как `VPN gateway leak` и даёт `detected = true`.\n\nИтог категории:\n\n- `detected = confirmed split tunnel || xrayApiFound || vpnGatewayLeak || vpnNetworkBinding`\n- `needsReview = true`, если найден открытый proxy, но подтверждения обхода нет\n\n---\n\n### 7. CDN Pulling (`CdnPullingChecker`)\n\nОтправляет HTTPS-запросы к известным endpoint-ам типа trace и redirector (Google Video, Cloudflare trace, Meduza), чтобы узнать, какой публичный IP или метаданные сети возвращаются. Различия в ответах могут указывать на туннелирование или проксирование.\n\n### 8. Call Transport (`CallTransportChecker`)\n\nПроверяет доступность UDP\u002FSTUN (как глобальных, так и региональных), а также TCP-доступность Telegram MTProto через локальные прокси-серверы. Это позволяет выявить утечки трафика мимо стандартных туннелей или обнаружить подмену IP-адресов.\n\n### 9. SNITCH — RTT-триангуляция (`RttTriangulationChecker`) β\n\nОтправляет ICMP ping к набору российских и иностранных хостов и сравнивает медианные задержки.\n\nРоссийские цели: `yandex.ru`, `mail.ru`, `vk.com`, `sberbank.ru`, `gosuslugi.ru`.\n\nИностранные цели: `facebook.com`, `github.com`, `twitter.com`, `reddit.com`, `instagram.com`.\n\nЛогика:\n\n- если медианный RTT до RU-хостов превышает порог (`80 мс`) — устройство, вероятно, не находится в РФ;\n- высокий jitter (> 60 мс) снижает уверенность вывода;\n- результат переводит вердикт в `NEEDS_REVIEW`, но сам по себе не даёт `DETECTED`.\n\nПроверка опциональна и отключена по умолчанию.\n\n---\n\n### 10. Native Signs (`NativeSignsChecker`)\n\nВыполняет низкоуровневые JNI-проверки прямо из C++:\n- Перечисление нативных интерфейсов и работа `getifaddrs()`\n- Прямой парсинг `\u002Fproc\u002Fnet\u002Froute`\n- Сканирование `\u002Fproc\u002Fself\u002Fmaps` на известные признаки hook-ов\n- Целостность разрешения символов `libc` (dlsym)\n- Обнаружение root (su binary, параметры magisk, selinux, rw \u002Fsystem и др.)\n\nНативные находки транслируются в `needsReview` или общие indirect-признаки.\n\n---\n\n## Вердикт (`VerdictEngine`)\n\n`VerdictEngine` использует не все собранные блоки одинаково.\n\nСначала применяются безусловные правила:\n\n1. `DETECTED`, если в bypass-evidence есть `SPLIT_TUNNEL_BYPASS`.\n2. `DETECTED`, если найден `XRAY_API`.\n3. `DETECTED`, если найден `VPN_GATEWAY_LEAK`.\n4. `DETECTED`, если location-сигналы подтверждают РФ (`network_mcc_ru:true`, `cell_country_ru:true` или `location_country_ru:true`), а `GeoIP` одновременно даёт иностранный сигнал.\n\nПосле этого считается матрица:\n\n- `geoMatrixHit` = иностранный GeoIP-сигнал (`geoIp.needsReview` или evidence `GEO_IP`)\n- `directMatrixHit` = evidence из `DIRECT_NETWORK_CAPABILITIES` или `SYSTEM_PROXY`\n- `indirectMatrixHit` = evidence из `INDIRECT_NETWORK_CAPABILITIES`, `ACTIVE_VPN`, `NETWORK_INTERFACE`, `ROUTING`, `DNS`, `PROXY_TECHNICAL_SIGNAL`\n\nКомбинации:\n\n| Geo | Direct | Indirect | Вердикт |\n|-----|--------|----------|---------|\n| нет | нет | нет | `NOT_DETECTED` |\n| нет | да | нет | `NOT_DETECTED` |\n| нет | нет | да | `NOT_DETECTED` |\n| да | нет | нет | `NEEDS_REVIEW` |\n| нет | да | да | `NEEDS_REVIEW` |\n| любые остальные комбинации | | | `DETECTED` |\n\nПримечания:\n\n- `IpComparisonChecker` сейчас не участвует в `VerdictEngine`;\n- сигналы `INSTALLED_APP` и `VPN_SERVICE_DECLARATION` тоже не входят в матрицу и остаются диагностическими;\n- Действенные (actionable) утечки из `CallTransportChecker` или находки требующие проверки из `NativeSignsChecker` (например, маркеры хуков) переводят вердикт из `NOT_DETECTED` в `NEEDS_REVIEW`.\n\n---\n\n## Сборка\n\nТребования: JDK 17+, Android SDK с Build Tools для API 36.\n\n```bash\n.\u002Fgradlew assembleDebug\n```\n\n---\n\n## Благодарности\n\n[runetfreedom](https:\u002F\u002Fgithub.com\u002Frunetfreedom) — за [per-app-split-bypass-poc](https:\u002F\u002Fgithub.com\u002Frunetfreedom\u002Fper-app-split-bypass-poc), на основе которого реализована детекция per-app split bypass.\n","RKNHardering 是一个用于检测Android设备上VPN和代理的应用程序，旨在帮助用户了解并防止被检测到使用了绕过网络封锁的工具。项目采用Kotlin语言编写，主要功能包括识别各种类型的网络接口、检查NetworkCapabilities以及分析dumpsys输出等手段来发现设备上的VPN活动。它还探讨了如何通过隐藏特定网络接口、修改NetworkCapabilities信息、阻止dumpsys泄露信息等方式来避免被检测到正在使用VPN。适用于需要保护个人隐私或在某些受限环境中访问互联网的场景。",2,"2026-06-11 03:50:33","high_star"]