Commit graph

10 commits

Author SHA1 Message Date
Danila Gornushko
5902c0f86c
Upgrade diagnostic captures (#227)
Some checks are pending
CI / kmod-activator (push) Blocked by required conditions
CI / kmod (android12-5.10) (push) Blocked by required conditions
CI / kmod (android13-5.10) (push) Blocked by required conditions
CI / kmod (android13-5.15) (push) Blocked by required conditions
CI / kmod (android14-5.15) (push) Blocked by required conditions
CI / kmod (android14-6.1) (push) Blocked by required conditions
CI / kmod (android15-6.6) (push) Blocked by required conditions
CI / kmod (android16-6.12) (push) Blocked by required conditions
CI / kmod-qemu (android12-5.10) (push) Blocked by required conditions
CI / kmod-qemu (android13-5.10) (push) Blocked by required conditions
CI / kmod-qemu (android13-5.15) (push) Blocked by required conditions
CI / kmod-qemu (android14-5.15) (push) Blocked by required conditions
CI / kmod-qemu (android14-6.1) (push) Blocked by required conditions
CI / kmod-qemu (android15-6.6) (push) Blocked by required conditions
CI / kmod-qemu (android16-6.12) (push) Blocked by required conditions
CI / kpm-qemu (android12-5.10) (push) Blocked by required conditions
CI / kpm-qemu (android13-5.10) (push) Blocked by required conditions
CI / kpm-qemu (android13-5.15) (push) Blocked by required conditions
CI / kpm-qemu (android14-5.15) (push) Blocked by required conditions
CI / kpm-qemu (android14-6.1) (push) Blocked by required conditions
CI / kpm-qemu (android15-6.6) (push) Blocked by required conditions
CI / kpm-qemu (android16-6.12) (push) Blocked by required conditions
CI / kpm-qemu-legacy (4.14) (push) Blocked by required conditions
CI / kpm-qemu-legacy (4.19) (push) Blocked by required conditions
CI / kpm-qemu-legacy (5.4) (push) Blocked by required conditions
CI / kpm (push) Blocked by required conditions
CI / zygisk (push) Blocked by required conditions
CI / lsposed (push) Blocked by required conditions
CI / portshide (push) Blocked by required conditions
CI / release (push) Blocked by required conditions
* feat: upgrade diagnostic captures

Collect richer debug and full-logcat bundles with device, backend, hook, portshide, and kernel partition evidence.

Add an explicit kernel image export for maintainer-requested kernel analysis.

* fix: harden diagnostic capture sessions

* fix(lsposed): harden diagnostic export error paths from review

Address correctness findings from a review of the diagnostic-capture upgrade,
all validated on a Pixel 4a via the agent bridge (kernel/debug/logcat exports).

- Kernel image export: wrap exportKernelImagesZip so a zip/IO failure returns
  null (and cleans the parts dir) instead of escaping and stranding the export
  button disabled; add try/finally at the DiagnosticsScreen call site. Cap the
  per-partition dd (skip unknown/oversized partitions, bound with count=) so a
  mislabelled or huge partition can't fill /data.
- HookDiagnostics: drive counter deltas off an explicit "baseline captured"
  flag instead of baselineCounters.isEmpty(), so a fresh boot with an empty
  baseline no longer reports every delta as n/a. Add HookDiagnosticsTest.
- DebugShellSnapshot: raise the 20s snapshot timeout to 60s and preserve+flag a
  timeout-truncated section instead of silently dropping it and every later one.
- LogcatRecorder: delete the intermediate raw logcat file after zipping and on
  failed start, so repeated captures stop leaking multi-MB files into cache.
- DebugExport: rethrow CancellationException before the generic catch so
  navigating away mid-capture cancels cleanly instead of logging a false error.
- DebugCaptureLogging: when the on-disk canonical config is present but
  unparseable, refuse the targets-snapshot rebuild (which would reset settings
  and clear autoHiddenPackages); fall back to an app-process-only toggle.
- Dashboard: don't warn "ports rules not active" for a module the user disabled
  via their manager (new ports_disabled probe, mirrors the activator's guard).
- activator write_atomic: per-process temp name so a boot-time and app-triggered
  activation can't corrupt each other's load_status.
- Drop the wasted multi-user pm enumeration from the counter baseline command.
2026-07-02 16:25:53 +03:00
Danila Gornushko
1582efa067
docs: refresh installation/backend docs + 1.0.0 screenshots (#224)
* docs: refresh installation and backend docs

* docs: fix review findings and add KPM to update-json

Cross-checked the docs refresh against the code and corrected:
- protocol.md: config parser is vpnhide_parse_config (not the legacy
  vpnhide_parse_target_uids); path is kmod/shared/vpnhide_logic.h; drop the
  unverified 0x2026 KPatch-Next marker (activator drives it via the CLI).
- zygisk/README: module declares Zygisk API v2 (not v5); add the recv hook
  row and the if<N> renamed-netdev match.
- README (en/ru): live-check counts are 13 native / 12 Java; Diagnostics is
  under Settings, not a tab; mention the Statistics tab; note KPM beta parity.
- detection-vectors: KPM host-route helper is kpm_is_public_host_route{4,6};
  restore CONFIG_KPROBES and the Zygisk qualifiers; codegen renders four targets.
- storage.md: legacy files are no longer user-managed (migration inputs), not
  'gone'. CLAUDE.md: activator has four bins (kmod/kpm/zygisk/ports).
- development/ROADMAP/kmod/lsposed/portshide READMEs + AGENTS.md: assorted
  accuracy fixes (KPM needs a KernelPatch runtime, per-KMI kmod zip name, etc.).
- CONTRIBUTING.md: clang-format uses scripts/clang-format-c.sh (covers KPM src).
- portshide customize.sh: 'Hiding -> Ports' (renamed tab).
- update-json.sh: emit update-kpm.json so KPM gets Magisk/KSU update checks.

* docs: correct diagnostics check count (25, not 26 vectors)

* docs: refresh README screenshots for the 1.0.0 UI

Replace the v0.5.3-era screenshots (old per-tab Protection UI) with a
new 1.0.0 set: VPN-hidden dashboard, the unified Hiding list with
J/N/A/P, the Statistics tab + per-hook breakdown, per-app hook selection,
diagnostics, settings, and the community screen. Extra shots
(statistics-apps, settings-advanced, diagnostics-java, settings-developer)
are included for the release notes.

* docs: fix the how-it-works layer breakdown

The old 'Layer 3 — additional app-level controls' bucket was wrong:
interface hiding is what Layers 1+2 already do, app-hiding is done by the
LSPosed/Java layer (system_server PackageManager hooks), and ports is a
separate module. Fold app-hiding into Layer 1 (LSPosed) and make Layer 3
the standalone portshide module.

* docs: conceptual-coherence pass on the 5 core docs

Second review pass, this time for architectural coherence rather than
code-facts. Fixes:
- README (en/ru): 'What vpnhide hides' stopped reifying interface hiding
  as a standalone 'layer'/'role' and now ties the three hiding goals to
  the four J/N/A/P roles (interface = Java + Native, toggled independently)
- README (ru): add the KPM-beta hook-parity caveat (parity with the EN side)
- protocol.md: app-hiding is a normal lsposed mask bit, not a separate
  'package/observer records' entry in the §6 profile table
- detection-vectors.md: a complete install is two components (native + lsposed),
  not 'two roles'; name both lsposed jobs
- storage.md: scope the text protocol to the config wire (LSPosed reuses the
  format for its state file); 'by function' not 'by role'; note the Apps role
  shares the same resolver; heading no longer files ports under 'Native layer'
2026-07-02 00:03:17 +03:00
Danila Gornushko
09e02bf625
kmod host-route: cover the legacy kernels (4.14/4.19/5.4), document the 32-bit SIOCGIFCONF gap (#211)
* feat(kpm): extend the public host-route concealment to the legacy kernels

I had disabled the host-route check on the non-GKI kernels (4.14/4.19/5.4) on the
mistaken belief they weren't QEMU-tested — but kpm-qemu-legacy covers exactly
those, and they ARE the kernels the KPM exists to serve. Enable it properly:

- IPv4 now works on every supported kernel. On <5.6 (5.4/4.19/4.14) fib_dump_info
  passes the __be32 dst and dst_len as args 6/7 instead of a fib_rt_info*, so
  kpm_is_public_host_route4 reads them from the right place per ABI. (The __be32
  value in the arg register stores to network-order bytes, so the same
  vpnhide_is_public_ipv4 applies.)
- IPv6 now works on 5.4 and 4.19 too: their fib6_info head matches 5.10, so
  fib6_dst is at +64 (derived from the AOSP/vanilla sources, QEMU-validated on
  the legacy images). 4.14 stays disabled — it uses rt6_info, whose
  cacheline-aligned rt6i_dst has no hand-derivable offset.
- Harness: test hostroute4 on every kernel and hostroute6 on every kernel except
  4.14 (init-kpm.sh gate by uname -r).

Validated A/B in QEMU locally on 5.4, 4.19, 4.14 (legacy images, cortex-a57) and
re-checked 5.10 GKI for no regression on the refactored 5.6+ path.

* feat(kpm): also hide the public IPv6 host-route on 4.14 (rt6_info path)

Completes host-route coverage across every supported kernel. 4.14 predates
fib6_info — rt6_fill_node takes a struct rt6_info*, and the route's destination
is rt6_info.rt6i_dst (a rt6key), not fib6_info.fib6_dst. Add a per-kver rt6_dst
offset and read the rt6key there on the rt6_via_dst path.

The offset is derivable, not guessed: struct dst_entry is 160 bytes (its __pads
keep the size constant regardless of CONFIG_XFRM/CLASSID), the rt6_info prefix
runs to +220, and rt6i_dst is ____cacheline_aligned_in_smp, so it rounds up to
+256 (same for a 64- or 128-byte cacheline). QEMU-validated A/B on the legacy
4.14 image (hostroute6 now hidden for the target, 18/18, no panic), and the
harness host-route gate is dropped — every KPM kver now covers v4 + v6.

* docs(roadmap): record the 32-bit compat SIOCGIFCONF gap as low-priority

* docs(changelog): note the legacy-kernel KPM host-route coverage
2026-06-30 11:32:26 +03:00
Danila Gornushko
211e59ed97
fix(lsposed): clarify KPM-awaiting-superkey and soften no-targets dashboard message (#197)
Add a dashboard warning when KPM is installed under APatch but dormant
because no superkey is saved (runtime=apatch, detail=awaiting_superkey for
the current boot) — it previously read as inactive with no reason. Demote
the no-targets message from error to info, since a fresh, not-yet-configured
install isn't broken. Drop the drifted I1/I2/W2/W4/W5 comment prefixes (the
err/warn/info call already conveys severity) and note the open permissive-
SELinux severity/coverage questions in the roadmap.
2026-06-30 00:44:52 +03:00
okhsunrog
8916d88ab1 docs(roadmap): record that Zygisk per-app stats are intentionally unsupported
Zygisk hooks run inside each target app's own sandbox and can't export counters
anywhere VPN Hide can read without either leaving a self-detectable artifact in
the target's sandbox or adding a world-writable + sepolicy surface — both
degrade stealth for the least-preferred, soon-to-be-retired native backend. KPM
covers the same vectors invisibly and already reports counters, so that's the
path for users who want native statistics.
2026-06-29 22:33:57 +03:00
okhsunrog
034efefbf9 feat(lsposed): per-app probe statistics with detection-method breakdown
Reshape the Statistics screen from per-backend × per-(uid,hook) counter rows
into a per-app view: each app that has triggered a hook is one card showing its
total probe count and a breakdown by detection method. The 25 raw hooks fold
into a small user-facing taxonomy (routes, interfaces, ioctl, policy rules,
NetworkCapabilities, LinkProperties, NetworkInfo, network handle,
ConnectivityService, package enumeration) tagged by surface (Java API / native /
packages). Counts are aggregated across every active backend (Java + the one
installed native backend, including Zygisk's libc hooks).

Scoped to target apps only — the apps the hooks already count. Monitoring of
non-target apps to discover new probers is deferred (docs/ROADMAP.md).

- ProbeStats.kt: DetectionMethod / MethodSurface taxonomy + buildAppProbeStats
  aggregator (pure, unit-tested).
- StatisticsScreen: per-app cards with method chips; hero shows probes / apps /
  methods / active backends.
- Drop the now-unused per-row strings; EN + RU method/surface labels.
2026-06-29 20:40:29 +03:00
okhsunrog
00135ddf8b Add detection-vector coverage map and fix README coverage table
Consolidate "which app probe is hidden by which component" into a single
authoritative doc, and correct the README coverage table, which marked the
netlink path as SELinux-blocked and predated the issue #86 fix.

- Add docs/detection-vectors.md: threat model, the kmod/zygisk/lsposed/SELinux
  layers, a per-vector coverage matrix (interface enum, routes, socket procfs,
  framework Java APIs, package visibility), importance ranking, known gaps, and
  where each layer is defined in code
- Fix README.md / README.en.md table: netlink RTM_GETLINK/GETADDR/GETROUTE are
  not SELinux-restricted; RTM_GETROUTE is now covered by kmod and zygisk;
  RTM_GETADDR/ipv6_route/tcp coverage corrected; rewrite the conclusion to
  explain the netlink path and per-device SELinux variance
- Note that SELinux policy differs across devices, ROMs, and Android versions,
  so the layers never rely on it
- Cross-link the new doc from CLAUDE.md, ROADMAP.md, and state.md
2026-06-26 13:11:57 +03:00
okhsunrog
a4513c95e8 fix(kmod): harden route hooks (drop unreliable rt_fill_info, add IPv6 host-route hint)
Two refinements on top of the netlink route-dump hooks:

1. Drop the rt_fill_info hook. It is a static, directly-called function,
   so the compiler may ignore AAPCS64 and place its args in arbitrary
   registers. Verified in QEMU on a no-LTO android12-5.10 build: regs[3]
   held table_id (254), not the struct rtable* the source signature
   places there — a fixed regs[N] read is build-dependent guesswork that
   differs between LTO device builds and no-LTO builds. The vector is also
   low value: IPv4 route enumeration (RTM_GETROUTE + NLM_F_DUMP, what
   detection apps use) goes through the global, ABI-stable fib_dump_info
   hook; single lookups respect the caller's routing, which is physical
   under the recommended split-tunnel setup. ROADMAP documents the
   rtnl_unicast-based alternative if single-lookup hiding is ever wanted.

2. Extend rt6_fill_node with an IPv6 analogue of the physical host-route
   hint: a public /128 route pinned to a physical interface leaks the VPN
   server's IPv6 even when the tun interface is hidden.

Verified: all 7 GKI KMIs (5.10..6.12) compile; route hooks filter VPN
routes for target UIDs on no-LTO 5.10 in QEMU and on a Pixel 8 Pro
(android14-6.1), no panics.
2026-06-24 06:58:10 +03:00
okhsunrog
adc2291949 fix(lsposed): avoid null active network fallback 2026-06-23 23:06:33 +03:00
okhsunrog
68596cfd04 docs: add roadmap 2026-06-23 22:55:15 +03:00