[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-1837":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":16,"stars30d":17,"stars90d":14,"forks30d":14,"starsTrendScore":18,"compositeScore":19,"rankGlobal":9,"rankLanguage":9,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":9,"pushedAt":9,"updatedAt":25,"readmeContent":26,"aiSummary":27,"trendingCount":14,"starSnapshotCount":14,"syncStatus":28,"lastSyncTime":29,"discoverSource":30},1837,"ByeByeVPN","pwnnex\u002FByeByeVPN","pwnnex","Full TSPU\u002FDPI\u002FVPN detectability scanner. Scan any IP as a censor sees it.",null,"C++",358,17,1,0,12,25,80,36,3.77,"MIT License",false,"main",true,[],"2026-06-12 02:00:33","# ByeByeVPN\n\nКлиентский сканер детектируемости VPN \u002F DPI \u002F Reality \u002F ТСПУ. Одна\nстатическая `byebyevpn.exe` под Windows (работает через Wine на Linux\nи macOS), без прав администратора, без DLL-зависимостей.\n\n```\n ____             ____           __     ______  _   _\n| __ ) _   _  ___| __ ) _   _  __\\ \\   \u002F \u002F  _ \\| \\ | |\n|  _ \\| | | |\u002F _ \\  _ \\| | | |\u002F _ \\ \\ \u002F \u002F| |_) |  \\| |\n| |_) | |_| |  __\u002F |_) | |_| |  __\u002F\\ V \u002F |  __\u002F| |\\  |\n|____\u002F \\__, |\\___|____\u002F \\__, |\\___| \\_\u002F  |_|   |_| \\_|\n       |___\u002F            |___\u002F\n   Full TSPU\u002FDPI\u002FVPN detectability scanner   v2.7.0\n```\n\n**Languages:** [English](#english) · [Русский](#русский) · [简体中文](README.zh-CN.md) · [فارسی](README.fa.md)\n\n**Discussion \u002F report issues:**\n[ntc.party\u002Ft\u002Fbyebyevpn\u002F24325](https:\u002F\u002Fntc.party\u002Ft\u002Fbyebyevpn\u002F24325) ·\n[GitHub Issues](https:\u002F\u002Fgithub.com\u002Fpwnnex\u002FByeByeVPN\u002Fissues)\n\n\u003Ca href=\"https:\u002F\u002Fnowpayments.io\u002Fdonation\u002Fbyebyevpn\" target=\"_blank\" rel=\"noreferrer noopener\">\n    \u003Cimg src=\"https:\u002F\u002Fnowpayments.io\u002Fimages\u002Fembeds\u002Fdonation-button-black.svg\" alt=\"Crypto donation button by NOWPayments\">\n\u003C\u002Fa>\n\n---\n\n## English\n\n### Purpose\n\nGiven an IP or hostname, run the full Russian OCR методика (§5-10) plus\nmodern 2026 tunnel fingerprints against it from an external vantage\npoint. Output: a detection score, the identified stack, and what a\nTSPU-class classifier would decide. No VPN connection to the target\nis needed - the scanner looks at the destination as a third-party\nobserver, the way an ISP or DPI middlebox sees it.\n\n### Prerequisites (read before scanning)\n\n> **Disable any active VPN \u002F Zapret \u002F proxy on the host running the\n> scanner before you start.** the scanner uses the host's TCP\u002FIP and\n> TLS stack to emit probes. if your host routes through a TUN \u002F\n> sing-box \u002F Zapret \u002F GoodbyeDPI \u002F proxifier, the wire-level signature\n> you measure on the target is **your local stack reflected**, not the\n> target's. specifically:\n>\n> - latency anchors and RTT to anchors will be wrong, breaking SNITCH.\n> - ClientHello bytes may be rewritten by Zapret-style fragmentors,\n>   breaking JA4 calculation.\n> - GeoIP queries will resolve YOUR exit IP, not the lookups you want.\n> - the `local` mode (`byebyevpn local`) is the one place where active\n>   VPN matters in reverse: there the goal IS to inspect your own\n>   adapters, but you still want to scan a remote target with the VPN\n>   off.\n>\n> turn the VPN\u002Fproxy off, run the scan, then re-enable. on Windows:\n> kill the v2rayN\u002Fsing-box\u002FZapret process and confirm\n> `Get-NetAdapter` shows no active TUN\u002FWireGuard\u002FWintun\u002FTAP-style\n> interfaces.\n\n### Pipeline\n\n| # | Module                          | What it does                                                            |\n|---|---------------------------------|-------------------------------------------------------------------------|\n| 1  | DNS resolve                     | A + AAAA, IPv4 preferred                                                |\n| 2  | GeoIP aggregation               | 5 HTTPS-only providers in parallel, ASN + flags                         |\n| 3a | TCP port scan                   | Connect-scan 1-65535 (default) or 205 curated ports, 500 threads        |\n| 3b | TCP stack fingerprint           | Handshake distribution + SIO_TCP_INFO peer window\u002FMSS + closed-port behavior, no admin |\n| 4  | UDP probes                      | WireGuard \u002F AmneziaWG \u002F Hysteria2 handshakes                            |\n| 4b | AmneziaWG S1 deep-probe (v2.6.0)| Junk-prefix size sweep on :51820, recovers the configured S1 parameter  |\n| 5  | Service fingerprint + CT        | SSH, HTTP, TLS + SNI consistency, SOCKS5, CONNECT, Shadowsocks, crt.sh, proxy-header leak |\n| 5b | uTLS dual-probe + JA4 + JA4S    | Two ClientHellos per TLS port (byte-accurate Chrome 131 vs openssl-default), JA4 \u002F JA4S extracted from raw CH\u002FSH bytes, JA4S classified against a backend-stack table |\n| 6  | J3 \u002F TSPU active probing        | 8 probes per TLS port (Reality discriminator)                           |\n| 7  | SNITCH + traceroute + SSTP      | RTT vs GeoIP (methodika §10.1), ICMP hop-count, Microsoft SSTP          |\n| 8  | Verdict + TSPU emulation        | Score 0-100, stack identification, 3-tier TSPU ruling, hardening advice |\n\n### UDP handshakes\n\nv2.6.0 narrowed the UDP set to the modern signature-less tunnels.\nthe legacy OpenVPN \u002F IKEv2 \u002F L2TP \u002F TUIC \u002F plain-QUIC \u002F DNS probes were\nremoved: those protocols carry fixed-port \u002F fixed-header signatures any\nDPI already catches, so probing for them cost scan time without adding\ndetection value for this niche.\n\n| Port       | Protocol           | Payload                                               |\n|------------|--------------------|-------------------------------------------------------|\n| 51820      | WireGuard          | 148-byte MessageInitiation, randomized body           |\n| 51820      | AmneziaWG Sx=8     | Delta-probe: vanilla WG rejected, Sx=8 prefix accepted |\n| 55555      | AmneziaWG Sx=8     | 8-byte junk prefix + WG init                          |\n| 51820      | AmneziaWG S1 sweep | 12-step junk-prefix size sweep, recovers configured S1 |\n| 36712      | Hysteria2          | QUIC v1 Initial, random DCID                          |\n| 443        | Hysteria2          | QUIC v1 Initial on :443                               |\n\n### J3 probes\n\nEight probe types fired at every TLS-capable port:\n\n1. Empty TCP connect (no bytes)\n2. `GET \u002F` with a real Host header\n3. `CONNECT example.com:443`\n4. Plausible OpenSSH banner\n5. 512 random bytes (via `RAND_bytes`)\n6. TLS ClientHello with random `.invalid` SNI\n7. Absolute-URI proxy-style `GET`\n8. `0xFF × 128`\n\nReality \u002F XTLS silently drops all 8; regular HTTP returns 400\u002F403. The\npattern is the diagnostic signal.\n\n### Verdict scale\n\n| Score  | Label          | Meaning                                           |\n|--------|----------------|---------------------------------------------------|\n| 85-100 | `CLEAN`        | Looks like a regular web server                   |\n| 70-84  | `NOISY`        | Suspicious artefacts, not necessarily VPN        |\n| 50-69  | `SUSPICIOUS`   | Multiple red flags                                |\n| \u003C 50   | `OBVIOUSLY VPN`| Trivially detected - obfuscation \u002F stack change needed |\n\n### TSPU emulation\n\n| Tier | Verdict          | Meaning                                                 |\n|------|------------------|---------------------------------------------------------|\n| A≥1  | `IMMEDIATE BLOCK`| Named protocol signature - SYN\u002Fhandshake dropped        |\n| B≥2  | `BLOCK` (cumul.) | ≥2 soft anomalies - classifier trips block threshold    |\n| B=1  | `THROTTLE \u002F QoS` | 1 soft anomaly - flagged for monitoring \u002F rate-limit    |\n| 0    | `PASS \u002F ALLOW`   | No signatures                                           |\n\n### On-the-wire posture\n\nThe tool does not impersonate a browser. Every outbound HTTP request\n(to IP-intel services, to the target during HTTP-over-TLS audit, to\ncrt.sh) goes out with zero tool-specific headers.\n\nFor `http_get()` - the one used against IP-intel services and\ncrt.sh - the request is byte-wise:\n\n```\nGET \u002Fpath HTTP\u002F1.1\nHost: \u003Chost>\n```\n\nNo `User-Agent`, no `Accept`, no `Accept-Language`, no\n`Accept-Encoding`, no `Sec-Fetch-*`, no `Upgrade-Insecure-Requests`.\nThe endpoints all accept a bare GET - the same way `curl -sS\nhttps:\u002F\u002Fipwho.is\u002F8.8.8.8` works without any flags. WinHTTP still\ntransparently decompresses gzip'd responses server-side, but we\ndon't advertise it.\n\nFor `https_probe()` - the target HTTP-over-TLS audit - headers are\nalso minimal (`Host`, `Accept: *\u002F*`, `Connection: close`).\n\nEarlier versions (v2.5 - v2.5.4) emitted a Chrome-131 header block\nintended to look \"browser-like\". That was itself a unique static\nfingerprint and has been dropped (see\n[issue #5](https:\u002F\u002Fgithub.com\u002Fpwnnex\u002FByeByeVPN\u002Fissues\u002F5)).\n\nFor protocol probes (UDP handshakes, TLS ClientHello, ICMP) every\nfield that a real client would randomize is filled via OpenSSL\n`RAND_bytes`: WireGuard MessageInitiation body, AmneziaWG junk prefix +\nWG body, Hysteria2 QUIC DCID, TLS ClientRandom, invalid-SNI prefix.\n\nICMP traceroute payload is the standard Windows `ping.exe` pattern\n(`abcdefghi...`, 32 bytes) - byte-identical to what any Windows box\nemits.\n\nThe uTLS dual-probe (v2.6.0) sends two different ClientHellos per TLS\nport. The \"chrome\" side is a byte-accurate Chrome 131 ClientHello built\nby hand (`src\u002Fscan\u002Fchrome_ch.cpp`): GREASE at the spec positions, the\nChrome extension set in order, a GREASE-prefixed x25519 key_share, the\npadding extension. It is exactly the bytes Chrome sends, not a tweaked\nOpenSSL context, so it carries no tool-identifying bytes - a uTLS\nfingerprint check sees Chrome. The \"openssl\" side is the default\nOpenSSL ClientHello, same as the existing `tls_probe`. SNI is the\ntarget's hostname in both. ClientHello \u002F ServerHello bytes stay\nin-process; JA4 \u002F JA4S hashes are computed locally and never\ntransmitted. The \"chrome accepted, openssl rejected\" split is the\nuTLS-enforcement signal.\n\nThe TCP stack fingerprint (3b) does not change anything on the wire.\nIt runs 6 sequential `SOCK_STREAM` connects to an already-known open\nport, calls `WSAIoctl(SIO_TCP_INFO_v0)` on the local handle, and (if\nasked) tries one connect to a closed port. No raw socket, no admin,\nno extra packet shapes.\n\n### Audit\n\nGrep the modular source tree for tool-identifying strings. Since\nv2.5.8 the source is split across `src\u002F**\u002F*.cpp` and `src\u002F**\u002F*.h`\nunder `src\u002Fcommon`, `src\u002Fnet`, `src\u002Fscan`, `src\u002Flocal`, `src\u002Fapp`,\nplus `src\u002Fmain.cpp`. Expected matches: only the `--help` printf in\n`src\u002Fapp\u002Fcli.cpp` (cap is 3 to leave headroom).\n\n```\n$ grep -rnE 'ByeByeVPN|BYEBYEVPN|BBVPN|BBV|pwnnex' \\\n    src --include='*.cpp' --include='*.h'\nsrc\u002Fapp\u002Fcli.cpp:27:    printf(\"ByeByeVPN — full TSPU\u002FDPI\u002FVPN ...\n```\n\nNone of these reach a socket. The CI workflow\n(`.github\u002Fworkflows\u002Frelease.yml`) fails the build if any additional\nmatch appears, and re-runs the grep across the full tree on every tag.\n\n### Install\n\nWindows: download `byebyevpn-v2.7.0-win64.zip` from\n[Releases](..\u002F..\u002Freleases), extract, run `byebyevpn.exe` - either\ndouble-click for the interactive menu, or pass an IP\u002Fhostname from\nthe terminal.\n\nRuntime: Windows 10 1803+ \u002F 11 \u002F Server 2019+. No admin, no DLLs, no\n.NET, no VC++ Redistributable. Internet access for GeoIP and\nCT-log lookups.\n\nLinux \u002F macOS: run through Wine. Everything except `local`\n(host-side adapter enumeration) works identically.\n\nVerify what you downloaded: every release ships a SHA256 in the\nrelease notes, a CycloneDX SBOM (`byebyevpn-sbom.json`), and -\nwhen the project signing key is configured - matching `.minisig`\nsignatures. See `BUILD.md` for the verify recipe.\n\n### CLI\n\n```bash\nbyebyevpn                        # interactive menu\nbyebyevpn \u003Chost>                 # full scan\nbyebyevpn scan 1.2.3.4           # same, explicit\nbyebyevpn \u003Chost> --json          # full scan, JSON on stdout (v2.6.0)\nbyebyevpn ports my.server.ru     # tcp scan only\nbyebyevpn udp my.server.ru       # udp probes only\nbyebyevpn tls my.server.ru 443   # tls + sni consistency\nbyebyevpn j3 my.server.ru 443    # j3 active probing\nbyebyevpn geoip 8.8.8.8          # geoip aggregation\nbyebyevpn snitch my.server.ru    # rtt vs geo (methodika §10.1)\nbyebyevpn trace my.server.ru     # icmp hop-count\nbyebyevpn local                  # scan this machine\n```\n\nA completed full scan exits with the verdict tier so wrapper scripts\ncan branch without parsing output (v2.6.0):\n\n```\n0  CLEAN (score >= 85)        2  SUSPICIOUS (50-69)\n1  NOISY (70-84)              3  OBVIOUSLY-VPN (\u003C 50)\n64 usage error (no target)\n```\n\nHostnames are resolved via `getaddrinfo`; IPv4 is always preferred,\nand the chosen IP is printed in phase [1\u002F8]. On IPv4-only links (RU \u002F\nCIS consumer internet) this avoids the happy-eyeballs AAAA trap where\nan unreachable v6 silently burns every timeout.\n\n### Port scan modes\n\n```\n--full                    all ports 1-65535 (default)\n--fast                    205 curated VPN \u002F proxy \u002F TLS \u002F admin ports\n--range 8000-9000 ports   port range\n--ports 80,443,8443       explicit list\n```\n\n### Tuning\n\n```\n--threads N       parallel TCP connects      (default 500)\n--tcp-to MS       TCP connect timeout         (default 800)\n--udp-to MS       UDP recv timeout            (default 900)\n--no-color        disable ANSI colors\n-v \u002F --verbose    verbose output\n```\n\n### Stealth \u002F privacy\n\n```\n--stealth         --no-geoip + --no-ct + --udp-jitter, AND adds\n                  inter-probe timing jitter across J3 \u002F SNI consistency \u002F\n                  uTLS dual-probe \u002F AmneziaWG sweep (v2.7.0)\n--no-geoip        skip all HTTPS GeoIP lookups\n--no-ct           skip crt.sh CT-log query\n--udp-jitter      50-300ms random delay between UDP probes\n--j3-subset N     send a random N-probe subset (1..7) of the eight J3\n                  probes per port instead of all eight (v2.7.0)\n--passive         minimal-probe mode: SKIPS J3, uTLS dual-probe, SNI\n                  consistency loop and AmneziaWG S1 sweep entirely. one\n                  base TLS handshake + GeoIP + CT-log + traceroute +\n                  SNITCH only. fewest scanner-shaped patterns on the\n                  wire (v2.7.0)\n```\n\nAll default off. Default scan emits the same bytes v2.6.0 emitted.\n\nAnti-fingerprint context: v2.7.0 randomizes the J3 probe order with a\nCSPRNG-backed Fisher-Yates per scan, so the fixed `empty -> GET ->\nCONNECT -> SSH -> rand -> tls-invalid -> abs-URI -> 0xff` sequence is\nno longer on the wire. The Chrome 131 ClientHello randomness also moved\nto `RAND_bytes` (away from `std::mt19937`).\n\n### Save scan output\n\n```\n--save            write the scan to \u003Ctarget>.md in the current directory\n--save \u003Cpath>     write the scan to \u003Cpath> (markdown-wrapped)\n```\n\nANSI colors are stripped from the file; terminal output is unchanged.\nThe file is wrapped in a markdown code block so it renders cleanly in\nany md viewer.\n\n### Build\n\nSee [BUILD.md](BUILD.md) for full instructions, OpenSSL provenance,\nand SHA256s. Short form:\n\n```bash\n# msys2 UCRT64\npacman -S --needed mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-make\ngit clone https:\u002F\u002Fgithub.com\u002Fpwnnex\u002FByeByeVPN.git && cd ByeByeVPN\nmake windows-static\n```\n\nRelease zips are produced by\n[`.github\u002Fworkflows\u002Frelease.yml`](.github\u002Fworkflows\u002Frelease.yml) from\na pinned msys2 image. SHA256 of the exe and zip are printed in each\nrelease's notes for verification.\n\n### Limitations\n\n- Connect-scan, not SYN-scan. Full TCP handshake seen by the target.\n- Cloudflare WARP \u002F CGNAT \u002F corporate proxies can ACK every port\n  with identical RTT. The tool detects this (>60 ports with RTT\n  variance \u003C80ms) and warns.\n- The uTLS dual-probe sends a byte-accurate Chrome 131 ClientHello\n  (so a strict uTLS-enforcing Reality server accepts it) but the\n  raw-socket path does not run the TLS 1.3 key schedule, so it has\n  no peer cert for the Chrome side - cert-steering detection still\n  relies on the openssl-side handshake. The main `tls_probe` JA3 is\n  still OpenSSL-default; this is noted in the output as an advisory.\n- QUIC probes are version-negotiation only - no derived-key\n  handshake. Enough to verify port liveness, not to fingerprint\n  the specific QUIC stack.\n- GeoIP providers disagree; `ipapi.is` flags any hosting IP as\n  \"VPN\". Score is built on behaviour, not on single-source tags.\n\n### License\n\nGPL-3.0-or-later. See [LICENSE](LICENSE) and [NOTICE](NOTICE).\n\nReleases up to v2.5.9 were MIT. From v2.6.0 onward the project is\nGPL-3.0-or-later. Old MIT releases keep their MIT grant; nothing is\nrevoked retroactively. The relicense was done by the sole copyright\nholder (every commit up to v2.6.0 was authored by pwnnex).\n\n---\n\n## Русский\n\n### Назначение\n\nПолучив IP или hostname, программа прогоняет полную методику\nРоскомнадзора (§5-10) + современные 2026 сигнатуры обфусцированных\nтуннелей против этой цели, работая как внешний наблюдатель. На выходе:\nscore детектируемости, определённый стек, и решение, которое принял\nбы ТСПУ-классификатор. Подключаться к VPN цели не нужно - сканер\nсмотрит на неё так же, как видит провайдер или DPI-middlebox.\n\n### Подготовка (читай до запуска)\n\n> **Перед запуском диагностики выключи на хосте любой активный\n> VPN \u002F Zapret \u002F GoodbyeDPI \u002F прокси.** сканер использует TCP\u002FIP и\n> TLS стек хоста чтобы слать пробы. если хост ходит через\n> TUN \u002F sing-box \u002F Zapret-фрагментатор \u002F Proxifier, на проводе ты\n> измеришь не цель, а **отражение собственного локального стека**:\n>\n> - latency-якоря и RTT поедут, SNITCH сломается.\n> - байты ClientHello могут перезаписаться Zapret-style фрагментатором,\n>   JA4 посчитается криво.\n> - GeoIP-запросы вернут твой exit-IP вместо нужного lookup'а.\n> - режим `local` (`byebyevpn local`) - единственное исключение, там\n>   как раз надо смотреть свои адаптеры. но даже в нём для скана\n>   удалённой цели VPN на хосте нужно отключить.\n>\n> алгоритм: вырубить VPN\u002Fпрокси, прогнать скан, потом включить\n> обратно. на Windows: убить процесс v2rayN\u002Fsing-box\u002FZapret и\n> убедиться через `Get-NetAdapter` что нет активных\n> TUN\u002FWireGuard\u002FWintun\u002FTAP интерфейсов.\n\n### Пайплайн\n\n| # | Модуль                          | Что делает                                                            |\n|---|---------------------------------|-----------------------------------------------------------------------|\n| 1  | DNS resolve                      | A + AAAA, приоритет IPv4                                              |\n| 2  | GeoIP aggregation                | 5 HTTPS-only провайдеров параллельно, ASN + флаги                     |\n| 3a | TCP port scan                    | Connect-scan 1-65535 (дефолт) или 205 curated, 500 потоков            |\n| 3b | TCP stack fingerprint            | Распределение handshake-времени + SIO_TCP_INFO peer window\u002FMSS + поведение на закрытом порту, без админа |\n| 4  | UDP probes                       | Handshake'и WireGuard \u002F AmneziaWG \u002F Hysteria2                          |\n| 4b | AmneziaWG S1 deep-probe (v2.6.0) | Sweep размера junk-prefix на :51820, восстанавливает настроенный S1    |\n| 5  | Service fingerprint + CT         | SSH, HTTP, TLS + SNI consistency, SOCKS5, CONNECT, Shadowsocks, crt.sh, proxy-headers |\n| 5b | uTLS dual-probe + JA4 + JA4S     | По два ClientHello на TLS-порт (байт-в-байт Chrome 131 vs openssl-default), JA4 \u002F JA4S из захваченных байт CH\u002FSH, JA4S классифицируется по таблице стеков |\n| 6  | J3 \u002F ТСПУ active probing         | 8 probe'ов на каждый TLS-порт (Reality discriminator)                 |\n| 7  | SNITCH + traceroute + SSTP       | RTT vs GeoIP (§10.1), ICMP hop-count, Microsoft SSTP                  |\n| 8  | Verdict + эмуляция ТСПУ          | Score 0-100, определение стека, 3-tier вердикт ТСПУ, hardening        |\n\n### UDP handshake'и\n\nv2.6.0 сузил UDP-набор до современных signature-less туннелей. legacy\nпробы OpenVPN \u002F IKEv2 \u002F L2TP \u002F TUIC \u002F plain-QUIC \u002F DNS убраны: эти\nпротоколы несут fixed-port \u002F fixed-header сигнатуры, которые любой DPI\nи так ловит, так что probe'инг по ним съедал время скана не давая\nдетект-ценности для этой ниши.\n\n| Порт      | Протокол           | Payload                                              |\n|-----------|--------------------|------------------------------------------------------|\n| 51820     | WireGuard          | 148-байтный MessageInitiation, рандомное тело        |\n| 51820     | AmneziaWG Sx=8     | Двойная проба: vanilla WG отвергается, Sx=8 принят   |\n| 55555     | AmneziaWG Sx=8     | 8-байт junk-prefix + WG init                         |\n| 51820     | AmneziaWG S1 sweep | Sweep из 12 размеров junk-prefix, восстанавливает S1 |\n| 36712     | Hysteria2          | QUIC v1 Initial, рандомный DCID                      |\n| 443       | Hysteria2          | QUIC v1 Initial на :443                              |\n\n### J3 probe'ы\n\nВосемь probe-типов на каждый TLS-порт:\n\n1. Пустой TCP (ничего не шлём)\n2. `GET \u002F` с реальным Host-заголовком\n3. `CONNECT example.com:443`\n4. Плаузабельный OpenSSH-баннер\n5. 512 байт `RAND_bytes`\n6. TLS ClientHello с рандомным `.invalid` SNI\n7. HTTP absolute-URI (proxy-style)\n8. `0xFF × 128`\n\nReality \u002F XTLS молча дропает все 8; обычный HTTP-сервер отвечает\n400\u002F403. Сам паттерн и есть сигнал.\n\n### Шкала verdict\n\n| Score  | Label           | Смысл                                                  |\n|--------|-----------------|--------------------------------------------------------|\n| 85-100 | `CLEAN`         | Выглядит как обычный веб-сервер                        |\n| 70-84  | `NOISY`         | Подозрительные артефакты, не обязательно VPN           |\n| 50-69  | `SUSPICIOUS`    | Несколько красных флагов                               |\n| \u003C 50   | `OBVIOUSLY VPN` | Палится сразу - нужна обфускация \u002F смена стека         |\n\n### Вердикт ТСПУ\n\n| Tier | Вердикт          | Что это значит                                          |\n|------|------------------|---------------------------------------------------------|\n| A≥1  | `IMMEDIATE BLOCK`| Named-протокол - SYN\u002Fhandshake дропается                |\n| B≥2  | `BLOCK` (cumul.) | ≥2 soft-аномалии - классификатор пересекает порог       |\n| B=1  | `THROTTLE \u002F QoS` | 1 soft-аномалия - флаг на мониторинг \u002F rate-limit       |\n| 0    | `PASS \u002F ALLOW`   | Нет сигнатур                                            |\n\n### Как тулза выглядит на проводе\n\nПрограмма не притворяется браузером. Каждый исходящий HTTP-запрос (к\nIP-intel сервисам, к target при HTTP-over-TLS аудите, к crt.sh)\nуходит **без** tool-specific заголовков.\n\nДля `http_get()` - функция которая ходит в IP-intel и crt.sh -\nзапрос побайтово выглядит так:\n\n```\nGET \u002Fpath HTTP\u002F1.1\nHost: \u003Chost>\n```\n\nНикаких `User-Agent`, `Accept`, `Accept-Language`, `Accept-Encoding`,\n`Sec-Fetch-*`, `Upgrade-Insecure-Requests`. Эти endpoint'ы принимают\nголый GET - так же как работает `curl -sS https:\u002F\u002Fipwho.is\u002F8.8.8.8`\nбез дополнительных флагов. WinHTTP всё равно прозрачно распакует\ngzip если сервер выберет его сам, но мы не анонсируем поддержку.\n\nДля `https_probe()` - аудит target'а через HTTP-over-TLS - хедеры\nтоже минимальные (`Host`, `Accept: *\u002F*`, `Connection: close`).\n\nПредыдущие версии (v2.5 - v2.5.4) отправляли блок заголовков \"как\nChrome 131\", чтобы \"выглядеть как браузер\". Это само по себе было\nуникальным статическим fingerprint'ом и удалено (см.\n[issue #5](https:\u002F\u002Fgithub.com\u002Fpwnnex\u002FByeByeVPN\u002Fissues\u002F5)).\n\nДля protocol-probe'ов (UDP handshake'и, TLS ClientHello, ICMP) каждое\nполе, которое реальный клиент рандомизирует, заполняется через\nOpenSSL `RAND_bytes`: тело WireGuard MessageInitiation, junk-prefix +\nWG-тело AmneziaWG, Hysteria2 QUIC DCID, TLS ClientRandom, префикс\ninvalid-SNI.\n\nICMP traceroute шлёт стандартный Windows `ping.exe` payload\n(`abcdefghi...`, 32 байта) - байт-в-байт то же, что и любой\nWindows-клиент.\n\n### Аудит\n\nГрепнуть исходник на tool-identifying строки. Ожидаются только три\nсовпадения, ни одно из которых не уходит в сеть:\n\n```\n$ grep -nE 'ByeByeVPN|BYEBYEVPN|BBVPN|BBV|pwnnex' src\u002Fbyebyevpn.cpp\n1:     \u002F\u002F ByeByeVPN - full VPN \u002F proxy \u002F Reality detectability analyzer\n...    \u002F\u002F коммент в http_get про scrub\n...    \u002F\u002F printf в --help\n```\n\nCI workflow (`.github\u002Fworkflows\u002Frelease.yml`) проваливает сборку при\nлюбом дополнительном совпадении.\n\n### Установка\n\nWindows: скачать `byebyevpn-v2.7.0-win64.zip` со страницы\n[Releases](..\u002F..\u002Freleases), распаковать, запустить `byebyevpn.exe`\n(двойной клик = интерактивное меню, либо IP\u002Fhostname из терминала).\n\nТребования: Windows 10 1803+ \u002F 11 \u002F Server 2019+. Прав администратора\nне нужно. DLL не нужно. Интернет - для GeoIP и CT-log.\n\nLinux \u002F macOS: через Wine. Всё кроме `local` (адаптеры хоста)\nработает идентично.\n\nПроверка скачанного: каждый релиз идёт с SHA256 в release notes,\nCycloneDX-SBOM (`byebyevpn-sbom.json`) и - когда выставлен ключ\nподписи проекта - с `.minisig` подписями. Рецепт верификации в\n`BUILD.md`.\n\n### CLI\n\n```bash\nbyebyevpn                        # интерактивное меню\nbyebyevpn \u003Chost>                 # полный скан\nbyebyevpn scan 1.2.3.4           # то же, явно\nbyebyevpn \u003Chost> --json          # полный скан, JSON в stdout (v2.6.0)\nbyebyevpn ports my.server.ru     # только tcp\nbyebyevpn udp my.server.ru       # только udp\nbyebyevpn tls my.server.ru 443   # TLS + SNI consistency\nbyebyevpn j3 my.server.ru 443    # J3 active probing\nbyebyevpn geoip 8.8.8.8          # GeoIP\nbyebyevpn snitch my.server.ru    # RTT vs geo (§10.1)\nbyebyevpn trace my.server.ru     # ICMP hop-count\nbyebyevpn local                  # сканировать свою машину\n```\n\nЗавершённый full scan выходит с кодом по уровню вердикта, чтобы\nобёртки могли ветвиться без парсинга вывода (v2.6.0):\n\n```\n0  CLEAN (score >= 85)        2  SUSPICIOUS (50-69)\n1  NOISY (70-84)              3  OBVIOUSLY-VPN (\u003C 50)\n64 ошибка использования (нет цели)\n```\n\nHostname резолвится через `getaddrinfo`; IPv4 выбирается всегда, а\nвыбранный IP печатается в фазе [1\u002F8]. На IPv4-only каналах (РФ \u002F СНГ)\nэто чинит баг happy-eyeballs, когда недоступный IPv6 тихо съедал\nвесь timeout.\n\n### Режимы TCP-скана\n\n```\n--full                    все порты 1-65535 (дефолт)\n--fast                    205 curated VPN \u002F proxy \u002F TLS \u002F admin\n--range 8000-9000 ports   диапазон\n--ports 80,443,8443       явный список\n```\n\n### Тюнинг\n\n```\n--threads N       параллельных TCP-connect'ов  (default 500)\n--tcp-to MS       TCP connect timeout           (default 800)\n--udp-to MS       UDP recv timeout              (default 900)\n--no-color        без ANSI-цветов\n-v \u002F --verbose    подробный вывод\n```\n\n### Stealth \u002F приватность\n\n```\n--stealth         --no-geoip + --no-ct + --udp-jitter, и плюс\n                  inter-probe timing jitter по J3 \u002F SNI consistency \u002F\n                  uTLS dual-probe \u002F AmneziaWG sweep (v2.7.0)\n--no-geoip        не дёргать HTTPS GeoIP-провайдеров\n--no-ct           не дёргать crt.sh\n--udp-jitter      50-300ms случайная задержка между UDP probe'ами\n--j3-subset N     отправить N (1..7) случайных проб из восьми J3 на порт\n                  вместо всех восьми (v2.7.0)\n--passive         минимальный профиль: ПРОПУСКАЕТ J3, uTLS dual-probe,\n                  SNI consistency и AmneziaWG sweep целиком. только\n                  один TLS handshake + GeoIP + CT + traceroute + SNITCH.\n                  меньше всего scanner-образных паттернов на проводе\n                  (v2.7.0)\n```\n\nВсе по умолчанию OFF. Дефолтный скан шлёт ровно то же что v2.6.0.\n\nАнти-фингерпринт контекст: v2.7.0 рандомизирует порядок J3-проб через\nCSPRNG-Fisher-Yates per scan, фиксированной последовательности `empty\n-> GET -> CONNECT -> SSH -> rand -> tls-invalid -> abs-URI -> 0xff` на\nпроводе больше нет. Рандом в Chrome 131 ClientHello тоже переехал на\n`RAND_bytes` (с `std::mt19937`).\n\n### Сохранение результата в файл\n\n```\n--save            записать скан в \u003Ctarget>.md в текущей папке\n--save \u003Cpath>     записать скан в \u003Cpath> (тоже как markdown)\n```\n\nANSI-цвета вырезаются при записи в файл; вывод в терминале не меняется.\nСодержимое обёрнуто в markdown code-block, так что файл нормально\nоткрывается в любом md-вьювере.\n\n### Сборка\n\nСмотрите [BUILD.md](BUILD.md) - полные инструкции, provenance OpenSSL,\nSHA256. Коротко:\n\n```bash\n# msys2 UCRT64\npacman -S --needed mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-make\ngit clone https:\u002F\u002Fgithub.com\u002Fpwnnex\u002FByeByeVPN.git && cd ByeByeVPN\nmake windows-static\n```\n\nРелизные zip собираются через\n[`.github\u002Fworkflows\u002Frelease.yml`](.github\u002Fworkflows\u002Frelease.yml) из\npinned msys2 образа. SHA256 exe и zip печатаются в release notes\nдля верификации.\n\n### Ограничения\n\n- Connect-scan, не SYN-scan. Target видит полный TCP handshake.\n- Cloudflare WARP \u002F CGNAT \u002F корпоративный proxy могут ACK'ать любой\n  порт с одинаковым RTT. Программа детектит это (>60 портов с\n  variance \u003C 80 мс) и выводит warning.\n- uTLS dual-probe шлёт байт-в-байт точный Chrome 131 ClientHello\n  (поэтому Reality с жёстким uTLS-enforcement его примет), но\n  raw-socket путь не гоняет TLS 1.3 key schedule, так что для\n  Chrome-стороны нет сертификата peer'а - детект cert-steering\n  всё ещё опирается на openssl-handshake. JA3 основного `tls_probe`\n  по-прежнему OpenSSL-default, отмечено в выводе как advisory.\n- QUIC probe - только version negotiation. Достаточно чтобы\n  проверить liveness порта, не достаточно чтобы идентифицировать\n  конкретный QUIC-стек.\n- GeoIP-провайдеры часто несогласны друг с другом; `ipapi.is` метит\n  любой hosting-IP как VPN. Score построен на поведении, а не на\n  single-source флагах.\n\n### Лицензия\n\nGPL-3.0-or-later. См. [LICENSE](LICENSE) и [NOTICE](NOTICE).\n\nРелизы до v2.5.9 включительно были под MIT. С v2.6.0 проект под\nGPL-3.0-or-later. Старые MIT-релизы сохраняют свою MIT-лицензию,\nретроактивно ничего не отзывается. Релиценз сделан единоличным\nправообладателем (все коммиты до v2.6.0 написаны pwnnex).\n","ByeByeVPN 是一个全面的 TSPU\u002FDPI\u002FVPN 检测扫描工具，能够从审查者的视角对任何 IP 地址进行扫描。项目核心功能包括使用多种现代隧道指纹技术来评估目标IP的可检测性，并输出检测评分、识别出的技术栈以及TSPU类分类器的决策结果。技术上，它以C++编写，提供了一个独立的`byebyevpn.exe`执行文件，无需管理员权限或额外的DLL依赖即可运行于Windows系统下（通过Wine兼容层支持Linux和macOS）。适用于需要了解特定网络服务是否能被深度包检测或其他形式的流量监控手段识别的情景中，比如安全研究、隐私保护等领域。",2,"2026-06-11 02:46:20","CREATED_QUERY"]