Files
QVPN/research/happ-leanrea.md
Курнат Андрей 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

18 KiB
Raw Permalink Blame History

Happ 4.5.4: статическое исследование Windows-клиента, 24 октября 2026

Исследована копия C:\Program Files\FlyFrogLLC\Happ\Happ.exe в C:\Tools\QVPN-private\happ-review-20261024\Happ.exe. SHA-256 оригинала и копии совпал: 010A05548865A85DF250337E0C14CC137248B96C9A2446E4F2EFEAEB4455176A. Размер — 21 260 712 байт. PE x64, ImageBase 0x140000000; таблица COFF-символов отсутствует. Использованы PE-импорты Qt, встроенные строки, шаблоны JSON, unwind-границы функций и дизассемблирование. Настройки пользователя, базы, текущие журналы и секреты здесь не читались. Приложения, службы и сетевые настройки не изменялись.

Видно означает наличие данных или соответствующей ветви машинного кода. Вывод означает интерпретацию этих данных. Статическое исследование не подтверждает выполнение ветви в текущем соединении. Имена C++ ниже восстановлены из встроенных диагностических строк, а не из отладочных символов.

Собственный TUN Xray

Видно: встроенное имя coreConfigs::TConfigBuilder::injectXrayTunInbound находится по файловому смещению 0x46c210, VA 0x14046d010. Соответствующая функция ограничена unwind-таблицей: VA 0x1400ba700–0x1400bdad8. Это отдельный генератор native TUN Xray, наряду с поддержкой сторонних TUN-процессов.

Параметр/ветвь Доказательство
tag=tun-in, protocol=tun строки: файл 0x46c114, 0x45b2cc; присваивание protocol: VA 0x1400bb56f–0x1400bb5b0
Default нового TUN: mtu=1500 VA 0x1400bb0d5: mov edx,0x5dc, затем QJsonValue(int); ключ mtu: файл 0x46c13c
userLevel=8 VA 0x1400bb132–0x1400bb171
desc=Happ строки Happ: файл 0x458198, desc: 0x46c140; присваивание VA 0x1400bb18f–0x1400bb1d0
IPv4 gateway 172.19.0.1/30 строка: файл 0x46c120; создание массива VA 0x1400ba7c8–0x1400ba7f8
DNS виртуального интерфейса 172.19.0.2 строка: файл 0x46c150; массив DNS и присваивание VA 0x1400bb24d–0x1400bb2c1
IPv4 default route 0.0.0.0/0 строка: файл 0x46c160; массив autoSystemRoutingTable: VA 0x1400bb30b–0x1400bb3c8
IPv6 gateway и default route добавляются условно fdfe:dcba:9877::1/126: файл 0x46bd90; ::/0: файл 0x46c16c; ветви VA 0x1400ba81a–0x1400ba844, 0x1400bb357–0x1400bb384
При наличии sniffing у SOCKS он копируется для TUN и получает routeOnly=true чтение sniffing: VA 0x1400ba9ec–0x1400baadc; bool true и ключ routeOnly: VA 0x1400bab4c–0x1400bab5b, ключ: файл 0x46c130
DNS-трафик отправляется в dns-out конструкция routing rule с port 53, network tcp,udp, outboundTag dns-out: VA 0x1400bc330–0x1400bc41b; строки: файл 0x46c0e8, 0x46c2e8

Видно: IPv6 по умолчанию выключен в этом генераторе. Фабрика XrayTunOptions VA 0x1400cfbe0–0x1400cfe2e вызывает getter VA 0x140153860; затем сравнивает результат с 1 и сохраняет boolean в нулевой байт options (0x1400cfc43–0x1400cfc51). Getter читает ключ Preferences/TunnelSettings/preferredIPType (файл 0x4baae0, VA 0x1404bb8e0) с default QVariant(int 0) (VA 0x140153883–0x1401538ce). Вторая возможность включить IPv6: одна из строк списка Preferences/AdvancedSettings/excludedRoutes содержит :. Ключ списка находится по файлу 0x4bb078, VA 0x1404bbe78; getter VA 0x14014fc90; проверка QString::contains(':') — VA 0x1401c60e0–0x1401c60eb, обход списка — VA 0x1400ba78b–0x1400ba7bf.

Текущее значение preferredIPType пользователя этим исследованием не установлено. Нельзя переносить статический default на активное соединение. Отличие от безусловного dual-stack TUN требует отдельного сопоставления активных настроек. Разница MTU с конфигурацией QVPN 1500 не обнаружена.

Выбор физического адаптера

Видно: при создании native TUN вызывается helper VA 0x1401d4570 (вызов 0x1400bb44a). Его строки сообщают о поиске интерфейса default route; он использует GetAdaptersAddresses для получения имени адаптера. Непустое имя присваивается autoOutboundsInterface; при неуспехе остаётся значение auto и выдаётся предупреждение (строка: файл 0x46c1a0; ветвь VA 0x1400bb43c–0x1400bb546; ключ: файл 0x46c2a8).

Вывод: генератор не выбирает просто первый перечисленный адаптер. Это не доказывает автоматическое переключение на другой физический адаптер после запуска: native TUN получает конкретное имя, когда resolver успешен. Алгоритм повторной генерации при смене сети здесь не исследован.

DNS: шаблоны, а не доказательство активной политики

Шаблон в Happ.exe Статически видимое содержимое
файл 0x4682a0, VA 0x1404690a0 DNS hosts и queryStrategy параметризованы; servers содержит один https://1.1.1.1/dns-query
файл 0x468320, VA 0x140469120 другой шаблон: UDP DNS 1.1.1.1 и 8.8.8.8, port 53; подмена domain:googleapis.cn на googleapis.com
файл 0x468470 несколько параметризованных DNS-серверов и domain-scoped правила
файлы 0x4685a0, 0x4686e0 варианты параметризованных серверов, domain-scoped правил и port/дополнительных параметров
файл 0x468800 FakeDNS pool 198.18.0.0/15, poolSize 10000

Вывод: Happ поддерживает разные DNS-режимы. Утверждение, что именно резервный DNS обеспечивает работоспособность активного Happ, этими шаблонами не подтверждено. В одном из штатных шаблонов upstream единственный, как и в прежней конфигурации QVPN. Точный выбор шаблона и активные значения требуют отдельной runtime-проверки.

Mux, UDP443 и Reality

Шаблон/поле Доказательство и граница вывода
Mux выключен файл 0x468880, VA 0x140469680: enabled false, concurrency -1, xudpConcurrency 8, xudpProxyUDP443 пустая строка
Mux включён и настраивается файл 0x4688f0, VA 0x1404696f0: enabled true, остальные три поля параметризованы
Поля GUI/настроек useMuxing, muxingTcpConcurrency, muxingXudpConcurrency, muxingHandleQUICMode: файл 0x444ce8–0x444d1d
TCP Reality файл 0x469240, VA 0x14046a040: network tcp, header.type none; параметры fingerprint, publicKey, serverName, shortId, show, spiderX и security
Другие Reality-транспорты gRPC Reality: файл 0x469810; остальные шаблоны не устанавливают выбор активного узла

Видно: настройки Mux/QUIC конфигурируемы. Не установлено: активный UDP443 policy, включение Mux в рабочей сессии, автоматическое изменение flow. Нельзя объяснять превосходство Happ «включённым Mux» по наличию шаблона. Синтаксические варианты network tcp/raw и Reality publicKey/password сами по себе не доказывают разницу поведения совместимого ядра.

Разрешение доменного адреса сервера

Встроенные диагностические строки показывают отдельный TServerAddressResolver: параллельные DoH A/AAAA (файл 0x463400), проверку нескольких IP через временные VPN-ядра и proxy ping (файлы 0x463930, 0x463988, 0x463a40), принятие первого рабочего ответа (0x463780) и возврат к исходному домену, если IP не ответили (0x463868). Это сильное свидетельство возможностей соответствующего компонента; его полный алгоритм не восстановлен. Применимость к текущим локациям с IP literal не подтверждена.

Практический результат для QVPN

Статически подтверждены другой путь TUN (собственный Xray), условное IPv6, выбор default-route адаптера, маршрутизация DNS через отдельный outbound и сохранение исходной цели при sniffing. Не подтверждены активная DNS-резервируемость, включённый Mux, специальные значения UDP443 или фрагментация как причины работоспособности Happ. Замена всего TUN требует отдельной проверки очистки Windows-маршрутов, DNS, geo/routing и аварийного завершения; одно наличие генератора не доказывает безопасную взаимозаменяемость.

Воспроизводимые локальные материалы: pe-inventory.txt, shipped-strings.jsonl, configuration-strings.json, disassembly.txt, disassembly-annotated.txt, inject-xray-tun.dis.txt, xray-tun-options.dis.txt, default-route-adapter.dis.txt и скрипты извлечения в каталоге копии. Никакие ресурсы или закрытый программный код Happ в исходники QVPN не переносились.

Отдельная служба Windows

Исследована также копия happd.exe: SHA-256 BDF6857524C1DB459282C3C96CD9C77C0AF78DA557B793F1D739452559748D20, размер 3 699 624 байта. Видно: PE импортирует Windows IP Helper (GetIpForwardTable2, CreateIpForwardEntry2, DeleteIpForwardEntry2, SetIpInterfaceEntry) и WFP (FwpmEngineOpen0, FwpmFilterAdd0, FwpmFilterGetById0, FwpmFilterDeleteById0). Сами импорты не доказывают активность конкретного фильтра.

Встроенные сообщения по файловым смещениям 0x244A00, 0x247550, 0x247B70, 0x24D668, 0x24D822 описывают обнаружение потерянного после сна адаптера, установку DNS, 15-секундный допуск heartbeat после пробуждения, разрешающие фильтры интерфейса и проверку сохранности WFP-фильтров. Вывод: управление соединением Happ включает отдельную политику Windows, а общий Xray и одинаковый ключ не определяют поведение всей системы. Эти строки не являются измерением работоспособности каждого пути в текущем сеансе.

Изменения QVPN 1.0.5

  • SystemTunnelProbe использует Windows GetBestInterfaceEx и сверяет выбранный интерфейс для публичных контрольных IPv4/IPv6-адресов с индексом QVPN. Рабочий адаптер и HTTPS напрямую больше не могут сами по себе подтвердить VPN. Проверка выполняется в цикле службы; при потере маршрута статус подключения снимается, повторный отказ восстанавливает локальный туннель на прежнем сервере. Это проверка контрольных маршрутов, а не фильтрация всех обходных путей.
  • Subscription ограничивает одну полную HTTP-попытку 12 секундами, включая тело после ResponseHeadersRead. Более короткая отмена вызывающего кода действует раньше; прежний один повтор сохранён. Устранён случай, когда пришедшие заголовки оставляли фоновое обновление ждать тело бессрочно. Семантика подтверждена документацией Microsoft.
  • Результат проверки маршрутов SystemRoutesVerified доступен через существующий IPC и в диагностике интерфейса. Секреты подписки в него не входят.

Регрессии: 70 проверок успешно, включая обход IPv4/IPv6 при поднятом TUN, неизвестные ошибки Windows route lookup, IPv4-only сеть, зависшее тело подписки и отмену. Живые результаты установки, восстановления и контролируемой потери маршрута сохраняются отдельно в happ-comparison-verification.json, installed-stability-verification.json, system-route-verification.json.

24 октября установлена и проверена версия 1.0.5.0; подписка сохранена, 50 локаций. Подключение к Оулу заняло 3,67 с, системные DNS/HTTPS и сравнительная проверка внешнего выхода прошли. Принудительное завершение Xray, TUN-ядра и службы восстановилось на той же локации за 6,36, 11,24 и 9,07 с соответственно; дочерних процессов после сбоя службы не осталось.

Отдельно добавлен один временный маршрут 1.1.1.1/32 через физическую сеть при поднятом адаптере QVPN. Обход обнаружен за 1,07 с: SystemRoutesVerified=false, состояние connecting вместо ложного connected. Локальный туннель перезапущен за 12,8 с без смены сервера; после удаления тестового маршрута проверенное подключение вернулось за 0,61 с. Удаление именно добавленного маршрута подтверждено. В последующих 61,5 с наблюдения было 27 снимков состояния, 6 успешных системных HTTPS-проверок, 0 самопроизвольных перезапусков и 0 смен сервера. Независимый завершающий HTTPS-запрос вернул 204.

Установщик: dist/QVPN-Setup-1.0.5.exe, SHA-256 B2518BDC3E96CE5CEE905CEE2A44252CA6424CF8771845F17E5F5B5A42D3501F. Все 211 файлов payload совпали с манифестом. Первое обновление откатилось после отказа Windows переместить каталог программы; повторная установка завершилась успешно. Финальное состояние проверки: QVPN подключён к Оулу, служба Happ остановлена.

Транспортное ядро уже совпадало с Happ до этих изменений; SHA-256 текущих бинарников Xray повторно совпал 24 октября. Перенос native TUN требует отдельной реализации DNS-политики, совместимости RU/private-правил и очистки после аварии: Xray использует .dat, а текущий QVPN — .srs; принудительное завершение процесса не гарантирует вызов native Close(). Поэтому в этой версии изменены проверенные дефекты управления существующим туннелем. Многодневная работа и реальный сон/пробуждение этим исследованием не доказаны.