Files
QVPN/research/analysis.md
T
Курнат Андрей aae6dd28dd Release QVPN 1.0.5 with Xray and resilient tunnel recovery
Integrate Xray 26.9.9 with the sing-box system tunnel and verify Windows routes during health checks.

Improve endpoint selection, network recovery, subscription refresh deadlines, diagnostics, packaging, and regression coverage.
2026-10-30 06:15:08 +03:00

31 KiB
Raw Blame History

QVPN: обследование BlancVPN и проверяемые решения

Дата обследования: 4 октября 2026 года, Москва. В этом документе нет ссылки подписки, UUID, ключей Reality и адресов серверов из личной подписки.

Что подтверждено на подключённом телефоне

dumpsys package установленного com.blancvpn.app показал versionName=1.8.7, versionCode=189, minSdk=28, targetSdk=36. Запись сохранена в phone-package.txt. Основное окно на phone.png показывает установленное VPN-соединение, выбранную локацию, большой переключатель подключения, переход к локациям и режим «Российские сервисы открываются без VPN».

Из APK исследованы имена компонентов и файлов, без переноса программного кода или ресурсов в QVPN. Обнаружены XrayVpnService, AwgVpnService, Flutter-ресурсы и библиотеки libflutter.so, libapp.so, libgojni.so, libhev-socks5-tunnel.so, libam-go.so, libam-quick.so, libam.so. Это подтверждает наличие Flutter-интерфейса и отдельных нативных VPN-компонентов. Имена файлов сами по себе не устанавливают точную версию ядра Xray, его текущие настройки или причину стабильности телефонного приложения.

В APK также обнаружены названия встроенных правил ru_merged_geoip.dat, ru_merged_geosite.dat, ru_whitelist_rules_v1.json и ru_app_ids.json. Они указывают на отдельную маршрутизацию российских ресурсов. Их содержимое не используется как база правил QVPN; для Windows выбраны открытые наборы и собственные правила. Фирменные изображения, шрифты, код приложения, токены встроенных сервисов и закрытые списки BlancVPN не копируются.

Что подтверждено по подписке

Приватно полученное содержимое содержит 50 локаций VLESS с Reality поверх TCP. Парсер QVPN обработал эти 50 записей; диагностический вывод содержит только LIVE_PARSE nodes=50. Ключ не включён в исходники, тестовые фикстуры и отчёт.

Импорт ограничен HTTPS-ссылкой домена withblancvpn.online или его поддомена, маршрутом /s/…, стандартным портом, без userinfo, фрагмента и query. Отклоняются чужие домены, подмена суффикса, Unicode/punycode-подмена имени провайдера, HTTP и перенаправления HTTP. Для этой версии принимаются только проверенный формат VLESS/Reality TCP и публичные IP-адреса серверов. Локальные, loopback, private и link-local адреса не могут стать целью привилегированного VPN-процесса через подписку. Это намеренно узкая совместимость с обследованной подпиской; новые форматы провайдера требуют отдельной реализации.

Почему Happ разрывает соединение

Причина пока не установлена. В проведённой проверке Windows за семь дней среди последних 300 записей ошибок приложений не найдено подтверждённого падения Happ. Ограниченное окно журнала не доказывает отсутствие всех падений и не отличает остановку ядра, сетевую ошибку, сон, обновление маршрутов и ручное отключение. Нельзя утверждать, что найдена и устранена конкретная ошибка Happ.

Для QVPN выбран Windows Service, работающий отдельно от окна. Закрытие интерфейса не должно прекращать VPN. Доступность проверяется запросом через отдельный локальный прокси, принудительно направленный в VPN; прямой доступ российских сервисов не должен дать ложное подтверждение исправности туннеля. Восстановление после ошибки использует ограниченную задержку 2, 4, 8, 16, 32, 60 секунд и смену локации при разрешённом автоматическом переключении. Успешная единичная проверка сбрасывает серию ошибок, но не накопленный backoff. Намерение пользователя «подключено» хранится отдельно от фактического состояния.

Восстановление процесса, реальная передача данных, рестарт службы, сон/пробуждение и многочасовая стабильность — разные границы проверки. Тест алгоритма восстановления не подтверждает работу сети, а краткий успешный сетевой тест не подтверждает многочасовую стабильность. В итоговой передаче следует указывать отдельно фактически выполненные проверки.

Ядро и маршрутизация

QVPN запускает неизменённый официальный Windows x64 sing-box 1.14.2 отдельным процессом. На момент обследования это последний стабильный релиз, отмеченный upstream как Latest. Источник: официальный релиз v1.14.2.

SHA-256 архива sing-box-1.14.2-windows-amd64.zip повторно вычислен локально и совпал с digest релизного API:

c2d8bfff918755808781dfdeeb8581b6c91eb3a243d9a7b55483cfc0c0684d32.

SHA-256 распакованного sing-box.exe:

7bbef1dea9189ee12799ae834ea4b4658355da25c47a21ad8804904c0ccd9410.

Конфигурация QVPN включает TUN с IPv4/IPv6, auto_route, strict_route, перехват DNS, DoH через VPN, обход локальных IP и управляемый обход российских доменов/открытых rule-set. Официальная документация описывает TUN и ограничения Windows, VLESS, Reality/TLS, DoH и маршрутизацию. Документация публикуется для нескольких версий; допустимость созданного JSON должна проверяться именно бинарником 1.14.2.

Лицензия и исходники ядра

Upstream LICENSE для точного тега разрешает sing-box по GPL версии 3 или более поздней, сохраняет copyright и отдельно ограничивает использование имени/ассоциации производных работ: LICENSE v1.14.2. QVPN не использует имя ядра как своё название и не заявляет принадлежность к SagerNet или BlancVPN.

В пакет с ядром следует включать upstream LICENSE, полный текст GPL-3.0 и уведомление: «sing-box 1.14.2, Copyright (C) 2022 nekohasekai. Ядро распространяется без изменений. Лицензия GPL-3.0-or-later».

Точный исходный тег: SagerNet/sing-box v1.14.2; архив исходников: v1.14.2.zip. При распространении бинарников необходимо сохранить лицензионные уведомления и обеспечить соответствующий исходный код и материалы сборки по условиям GPL. Одна ссылка на репозиторий не заменяет проверку полноты исходного комплекта для выбранного способа распространения.

Автоматические проверки QVPN.Core

dotnet run --project tests/QVPN.Tests/QVPN.Tests.csproj -c Release — 16 групп проверок, 16 успешно, 0 ошибок. Внешний тестовый фреймворк не требуется.

Проверены допустимая подписка и запреты импорта, 50 синтетических записей в открытом и base64-виде, удаление дублей, очистка имён, ошибочные Reality-параметры, запрет локальных адресов, DPAPI roundtrip/замена/backup/повреждение и наследуемые ACL защищённого каталога, отсутствие открытого текста в файлах, двусторонний протокол IPC и ограничения размера, маршрутизация health-проверки, TUN/DNS, переключатель российских ресурсов и политика восстановления. DPAPI LocalMachine требует защищённого каталога службы: шифрование само по себе не ограничивает другие локальные учётные записи.

Для приватной проверки содержимого: QVPN.Tests --live-parse <private-file>. Для интеграционной проверки службы предусмотрено QVPN.Tests --client <command> --value-file <private-file>; запрос и значение ключа не выводятся, ответ содержит только публичный Status. Отчёт не является доказательством многочасовой работы VPN.

QVPN.Tests --engine-check <sing-box.exe> успешно проверил JSON официальным бинарником 1.14.2 для четырёх сочетаний TUN on/off и российского обхода on/off, все exit=0. Проверка использует только синтетический ключ и не подключается к серверу. Она подтверждает допустимость конфигурации для выбранного ядра, отдельно от сетевой работы.

После единичной ошибки загрузки в .NET проведено повторное обследование командой QVPN.Tests --download --value-file <private-file> --diagnose. Непосредственный Subscription.DownloadAsync получил 50 локаций. Диагностические запросы direct, system-proxy и IPv4-direct вернули HTTP 200 с корректной TLS-цепочкой (SslPolicyErrors.None). DNS на момент проверки вернул один IPv4 и не вернул IPv6. Ошибка не воспроизведена; изменение проверки сертификатов или принудительное отключение IPv6 этим замером не обосновано. Диагностика выводит только типы исключений, HRESULT и значения enum, без Exception.Message, URL, адресов и credentials. Устойчивость загрузки должна обеспечиваться повтором временной сетевой ошибки и сохранением последней рабочей подписки.

Проверка обработчика службы после исправлений

В review найдены и исправлены: потеря IPC-слушателя при JsonException, ожидание бесконечного соседнего цикла вместо реакции SCM на fatal, сохранение работающего ядра после ошибки записи при отключении и неограниченное ожидание TCP-измерений при остановке. Дополнительно исправлены права CreateNewInstance диагностического сервера: полный доступ получает текущая серверная учётная запись, тогда как пользовательский клиент в режиме службы SYSTEM сохраняет только клиентские права.

Реальная команда QVPN.Tests --malformed-service --evidence-file research/service-regression.json отправила восемь повреждённых JSON-фреймов с клиентским TokenImpersonationLevel.Identification в уже запущенный обработчик. Каждый запрос был закрыт, после каждого status успешно ответил; до и после оставались 50 локаций и состояние disconnected. Во время проверки ядро не было запущено, поэтому этот замер подтверждает живучесть IPC и сохранение состояния, а не непрерывность активного туннеля. Команда не запускала TUN, не меняла настройки и не перезапускала службу. Публичный JSON содержит только время, счётчики, состояния и PID.

Проверка установленного приложения, 4 октября 2026

Установленная служба LocalSystem и системный адаптер QVPN работают. HTTPS без явного прокси через системную маршрутизацию вернул 204. На рабочем ключе доступны 50 локаций.

Принудительное завершение установленного TUN-ядра: восстановление за 11.1 секунды без ввода ключа. Принудительное завершение службы: SCM восстановил службу и подключение за 11.4 секунды, дочернее ядро предыдущей службы не осталось работать. Последующий HTTPS-запрос через системный маршрут вернул 204. Свидетельство: installed-recovery-verification.json.

Старый installed-verification.json содержит ошибочный отказ теста: после завершения ядра проверка сразу прочитала предыдущий снимок connected со старым PID. Исправлено ожидание нового PID одновременно с проверенным состоянием connected. Это ошибка первоначального проверочного скрипта, а не подтвержденный отказ восстановления приложения.

В конце проверки VPN оставлен включённым, локация Цюрих, Швейцария. Отрисовка установленного интерфейса по реальному статусу — installed-live.png, не демонстрационный режим. Многосуточная эксплуатация и реальный цикл сна/пробуждения этой короткой проверкой не подтверждаются.

Ожидание службы после загрузки Windows, 5 октября 2026

Пользователь сообщил, что после перезагрузки окно предложило переустановить приложение. У установленной службы обнаружен AUTO_START (DELAYED). Загрузка Windows началась в 20:36:56; журнал интерфейса содержит таймауты подключения к каналу с 20:37:27 до 20:39:08. В 20:39 появилась резервная копия повторной установки, а первое событие запущенной службы имеет время 20:39:24. На момент диагностики служба уже работает, ключ сохранён, статус подключения доступен. Эти данные подтверждают период недоступности канала после входа в Windows; автоматический запуск прежней версии без повторной установки в этом цикле отдельно не наблюдался.

Версия 1.0.1 убирает отложенный запуск службы. При отсутствии ответа интерфейс проверяет состояние в диспетчере служб и автоматически запускает остановленную службу. Установщик добавляет владельцу права START и QUERY_STATUS с сохранением остальных ACL; изменения конфигурации службы и остановка этим разрешением не предоставляются. Сообщение об установке появляется только при подтверждённом отсутствии регистрации службы (код Windows 1060). Ожидание запуска, отключённая служба и отказ доступа показываются отдельно. Обновление сначала закрывает старое окно, затем останавливает службу, чтобы интерфейс не запускал её во время замены файлов.

Сборка 1.0.1 и 16 групп тестов прошли. scripts/verify-startup-installed.ps1 -InstallUpdate обновляет программу с сохранением подписки, проверяет отсутствие отложенного старта, останавливает службу и запускает обычное окно без повышенных прав. Успешный результат требует повторного подключения с прежним числом локаций и HTTPS 204 через системный TUN. JSON и отрисовки проверки исключены из Git. Этот сценарий проверяет восстановление после остановки службы; реальную перезагрузку компьютера он не выполняет.

Повторное обследование BlancVPN и восстановления QVPN, 6 октября 2026

Проверка телефона выполнена через read-only ADB и локальные APK установленного приложения. VPN на телефоне не отключался, интерфейс не управлялся, журналы с сетевыми адресами и конфигурации пользователя не читались. Подтверждены com.blancvpn.app версии 1.8.7, код 189, активная foreground-служба XrayVpnService в отдельном процессе :xrayService, PID 15201. Снимок службы показывает startRequested=true, stopIfKilled=false, startCommandResult=1; последнее соответствует START_STICKY. Снимок не измеряет длительность непрерывной передачи данных и сам по себе не доказывает отсутствие прежних разрывов.

В нативной библиотеке APK найдена Go build metadata с зависимостью github.com/xtls/xray-core версии v1.260327.1-0.20260627131803-45cf2898ab12. Также присутствует отдельная libhev-socks5-tunnel.so. Это подтверждает использование Xray в телефонном компоненте, тогда как QVPN на Windows использует sing-box. Разные ядра и способы подключения виртуального сетевого интерфейса могут давать различия поведения, но обследование не устанавливает, что причиной жалобы является sing-box или что замена ядра сама по себе решит проблему.

Точечная проверка структуры и инструкций VPN-компонента APK подтверждает восстановление сохранённых параметров туннеля при перезапуске службы без новых Intent extras. Восстановление зависит от сохранённого состояния: приложение отдельно обрабатывает отсутствие конфигурации и состояние, при котором восстанавливать туннель не нужно. Повторная команда с уже принятым connection_id игнорируется. Обнаружен зарегистрированный Android network callback: при смене сети он ждёт подтверждённой доступности новой сети (NET_CAPABILITY_VALIDATED), затем вызывает обработчик восстановления. Обработчик пропускает восстановление после отзыва VPN-разрешения и в неподключённом состоянии, обновляет underlying network и передаёт восстановление текущему VPN-компоненту. Это проверенные ветви поведения; они не являются измерением фактического времени переподключения на телефоне.

В Flutter AOT найдены имена собственных компонентов проверки соединения, сохранения последнего успешного подключения, резервного выбора и сетевой проверки, а также параметры ограничения времени попытки и количества health-проверок. Эти имена указывают на отдельный управляющий слой поверх VPN-ядра. Точный алгоритм выбора серверов, значения таймаутов, интервалы и правила автоматического переключения из AOT не подтверждены. Строки о retry из сторонних платёжных и HTTP-библиотек не использованы как свидетельство алгоритма VPN. Закрытый код, ресурсы и native source BlancVPN в QVPN не перенесены.

В текущем исходном коде QVPN сохранён идентификатор последнего рабочего сервера; автоматический выбор удерживает допустимый рабочий сервер и перебирает ещё не отказавшие варианты. Ручной выбор остаётся фиксированным при отключённом автоматическом переключении. Отсутствующий TCP-замер отличается от явной ошибки измерения; если все TCP-замеры отрицательны, реальная VPN-попытка остаётся возможной. Восстановление учитывает изменения физического сетевого пути и сигнал пробуждения Windows; повторные команды подключения не запускают работающий туннель заново. Проверки доступности ограничены общим временем и используют несколько целей. Эти изменения реализуют соответствующие механизмы самостоятельно. Проверка исходников и регрессий должна дополняться отдельной проверкой установленного Windows-приложения; многочасовая стабильность и реальный сон/пробуждение из анализа APK не следуют.

Проверка Xray и свежести подписки

В отдельной проверке на физическом Wi-Fi с остановленным HAPP оба ядра не передали HTTPS через сохранённые параметры Цюриха. TCP до сохранённого сервера отвечал примерно за 65 мс: такой замер не подтверждает исправность VLESS/Reality. Затем новая подписка была получена в памяти без изменения установленных настроек. Для той же именованной локации изменились адрес сервера, SNI и shortId; UUID, публичный ключ, fingerprint и flow остались прежними.

С новыми параметрами официальный Xray 26.6.27 передал HTTPS 204: стандартная конфигурация — 272 мс после готовности локального прокси, без привязки интерфейса — 235 мс, с фрагментацией TLShello — 405 мс. Независимый curl через SOCKS подтвердил 204 во всех трёх случаях. Это проверка одного набора свежих параметров и трёх коротких попыток, а не измерение времени подключения телефона или полной Windows TUN-сессии. Фрагментация для этого соединения не потребовалась.

Обнаружена ошибка прежнего управляющего слоя: автоматическое обновление подписки начиналось только после успешного соединения, поэтому неработающий кэш не обновлялся во время восстановления. Новый запрос подключения и запуск службы со старым кэшем получают параметры до запуска TUN, не блокируя команды интерфейса. Время такого обновления ограничено пятью секундами; после ошибки связи повторные автоматические запросы ограничены одной попыткой в минуту. Выбор географической локации сохраняется при смене параметров и ID.

Установленная версия 1.0.3

Установленная служба совпала по SHA-256 с собранным payload. Проверены рабочий Xray, Windows TUN и HTTPS 204 через системный маршрут. Повторные команды и прямые TCP-замеры не перезапустили соединение; обновление параметров подписки сохранило Цюрих. Аварийное завершение Xray восстановилось за 6,13 с, процесса TUN — за 8,22 с, службы — за 15,32 с без оставшихся дочерних процессов. В объединённом испытании первый снимок последующего наблюдения не совпал с ожидаемым; исходный отчёт не сохранил различавшиеся поля, и установить причину этого снимка не удалось. Проверочный скрипт дополнен проверкой принадлежности процесса текущей службе и записью сравниваемых полей.

После обычного подключения пользователя к Цюриху проведено отдельное наблюдение установленной версии: 300,74 с, 58 снимков состояния, 10 успешных системных HTTPS-проверок, 0 перезапусков процессов и 0 смен сервера. На завершение проверки VPN оставлен подключённым. Время этого обычного подключения по событиям службы — примерно 3–4 с. Регрессионные проверки исходников: 49 групп, 49 успешно. Многосуточная работа и реальный сон/пробуждение в этой проверке не испытаны.