Commit graph

14 commits

Author SHA1 Message Date
okhsunrog
c059c64582 fix(zygisk): cover libc socket-interface binds 2026-07-13 21:57:05 +03:00
okhsunrog
bca81b80e7 fix: block socket binds to hidden VPN interfaces 2026-07-13 20:22:51 +03:00
okhsunrog
a29f26be7d refactor: separate activation and config responsibilities 2026-07-10 11:31:38 +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
5cbdc54e4f
Drop dead proc diagnostics, fix misaligned reads, align parser parity (#206)
* fix(native): drop dead proc diagnostics, fix misaligned reads, parser parity

Three native-correctness fixes from the codebase audit.

Diagnostics: /proc/net/{tcp,tcp6,udp,udp6,fib_trie} expose the VPN only as a
hex local address, never as an interface name, so the name-matching probe could
only ever report a green "no VPN entries" regardless of an actual leak — and
from inside the targeted (self-hidden) process there is no reference tunnel
address to decode and compare. Remove those five checks rather than keep a
meaningless always-pass; the address-leak vector on these files is covered by
zygisk's socket-table filters. /proc/net/{route,ipv6_route,if_inet6,dev}, which
do carry interface names, are unchanged.

UB: SIOCGIFCONF and the netlink dumps reinterpret the kernel's bytes as ifreq /
nlmsghdr / rtattr over plain [u8; N] buffers (align 1), so forming those refs is
undefined behaviour. Back the three buffers with an 8-byte-aligned wrapper so
every such reference is well-formed.

Protocol parity: the Rust parse_header skipped a non-ASCII first significant
line and searched on, while C (kmod/KPM) and Kotlin (LSPosed) reject the whole
payload — same wire bytes, different accept decision across the mutually
exclusive backends. Distinguish "blank/comment" (skip) from "significant but
non-ASCII" (reject) so all three agree. Clarify the §4.2 corner in the spec and
add header-position golden vectors (cfg + kind) that all three parsers now pass.

* style: rustfmt protocol parse_header
2026-06-30 11:30:11 +03:00
okhsunrog
52b137b78e Add backend-specific native hook selection 2026-06-29 19:24:03 +03:00
okhsunrog
552762a246 Add LSPosed interception stats 2026-06-29 02:43:23 +03:00
okhsunrog
bccdc324e7 docs(kpm): refresh runtime activation notes 2026-06-28 19:32:56 +03:00
okhsunrog
0f829e52bd fix(kpm): support APatch supercall activation 2026-06-28 19:28:12 +03:00
okhsunrog
c593c6f9ea docs: storage & activation architecture (target design)
Capture the storage-layer design we converged on, above the wire protocol:

- docs/storage.md (new) — the authority. One JSON canonical
  (/data/system/vpnhide_config.json) as the single source of truth; the text
  protocol stays the runtime IPC for the native backends only; a Rust activator
  (workspace: protocol lib + zygisk cdylib + activator lib with three thin
  src/bin/{kmod,kpm,zygisk}.rs) projects JSON -> wire, each native module shipping
  only its own bin. LSPosed self-reads the canonical (it's an APK+hook, not a
  module, and the Java layer must work standalone), resolving uids via
  packages.list to avoid PM-hook re-entry. Stats stay kmod/KPM-only; zygisk
  per-hook stats deferred (no daemon-free cross-process aggregation). The APatch
  superkey gets an opt-in flag persisting it root-only at /data/adb/vpnhide/superkey
  (DE storage, boot-readable) to unlock APatch boot activation. SELinux layout,
  the file-count collapse, and migration are spelled out.

- protocol.md / state.md — pointers to storage.md and notes on the statements it
  supersedes (LSPosed reading the wire, APatch-can't-boot, the storage model). The
  frozen v1 wire format is unchanged.

- CLAUDE.md — index storage.md + protocol.md.

This is the target design; the current branch's code is the interim
protocol-on-every-backend step it converges toward.
2026-06-28 17:23:17 +03:00
okhsunrog
16dfc70c5a docs + cleanup: legacy protocol references and node names
Finish the migration: docs/state.md, docs/protocol.md, detection-vectors.md and
the kmod/lsposed READMEs now describe the folded /proc/vpnhide_ctl node and the
`vpnhide 1 config` wire on every channel; the kmod ABI section documents
config-in / status+stats-out. The KPM exit drops the dead remove_proc_entry
calls (it has no /proc node — its channel is ctl0). The shared header notes that
the decimal vpnhide_parse_target_uids is the legacy load-args/host-test path,
not the protocol channel. Changelog fragment added.

Refs #23.
2026-06-28 14:54:35 +03:00
okhsunrog
6984136848 docs(protocol): freeze version 1 — resolve OPEN-1/2/4/7
- OPEN-1: 0x prefix mandatory (parse anchor, no optional-spelling drift).
- OPEN-2: positional-after-keyword (the keyword self-describes; no key=value).
- OPEN-4: one folded .ko node renamed /proc/vpnhide_ctl (write=config,
  read=status+stats; debug folds in too so /proc/vpnhide_debug goes away).
  Renamed for semantics, NOT stealth — the node is 0600 and the threat model
  is an unprivileged app (root-level detection is explicitly out of scope).
- OPEN-7: in-band '# vpnhide v1 ...' comment header on reads (free; an agent
  learns the grammar + replace-whole semantics from one cat).

All seven OPENs resolved; version 1 is frozen.
2026-06-28 03:32:04 +03:00
okhsunrog
9aec3d48f1 docs(protocol): backend-selection model + resolve OPEN-3/6
Fold in the architecture decisions from the design discussion:

- §1.5 backend selection: one always-on Java (LSPosed) layer + exactly one
  active native backend, priority kmod>KPM>Zygisk, app picks from status and
  idles the rest; kernel guard (conflicting_backend) as the deadlock backstop.
- OPEN-3 resolved: u64 cumulative-since-load counters (non-destructive reads,
  app computes deltas); count is u64, uid/hookmask/hook_id are u32 (§4.4).
- OPEN-6 resolved: debug folded into the config snapshot (drops per-backend
  debug files + the /proc/vpnhide_debug node).
- §4.3: UID is the key on every channel including Zygisk (one grammar; Zygisk
  matches getuid()), package->UID resolution is the producer's job.
- §7.4 KPM distribution: thin flashable module; keyless KPatch-Next does it all
  in the boot script (recommended path), APatch needs the app + session-only
  superkey (boot can only flag awaiting_superkey).
- §9: resident root hub/daemon reconsidered (vs vpnhide_next) and re-rejected;
  reasoning recorded so it is not re-litigated.
2026-06-28 03:23:34 +03:00
okhsunrog
d190ea39ea docs: add control & stats protocol spec (arbiter)
Bring PROTOCOL.md into docs/ as the wire-format arbiter for the
control-protocol work. Updated from the design draft with what device
testing resolved:

- OPEN-5 resolved on-device: the KernelPatch CLI forwards out_msg to
  stdout (subcommand is 'kpm ctl0', not 'control'); data exits only via
  out_msg, the long return is for short codes. CLI binary must match the
  runtime (c02 vs d05); APatch is superkey-based, KPatch-Next d05 keyless.
- New kind = status (backend health + errors, §4.3) with a codegen'd error
  enum (§5.1), driven by the .ko<->KPM mutual-exclusion finding (§1.2):
  running both kernel backends at once hard-froze a device, so each must
  detect the other and refuse with status.error = conflicting_backend.
2026-06-28 02:07:41 +03:00