Commit graph

15 commits

Author SHA1 Message Date
okhsunrog
02015d45e6 feat(zygisk): add best-effort filesystem path hiding 2026-08-13 22:53:05 +03:00
okhsunrog
fce99fc62b docs: separate boot activation from wire protocol 2026-08-13 17:28:16 +03:00
okhsunrog
62ebc2c6b5 refactor(hooks): model optional kernel backend owners 2026-08-13 15:57:56 +03:00
okhsunrog
833eebea3c feat(kmod): add optional filesystem path hiding 2026-08-12 00:34:53 +03:00
okhsunrog
c72119daed fix(zygisk): cover libc socket-interface binds 2026-08-09 21:55:44 +03:00
okhsunrog
40f9c938b5 fix: block socket binds to hidden VPN interfaces 2026-08-09 21:55:44 +03:00
okhsunrog
909dddb2f6 fix: hide additional tunnel interface names 2026-07-13 23:14:31 +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
22d9f62ce0 kpm: speak the control/stats protocol over ctl0
Dispatch the KernelPatch ctl0 supercall on the §4 header kind:
- config  -> vpnhide_parse_config: per-uid hookmask + debug (replaces the
  uid-only target set; each hook now gated by its bit, enabling per-hook
  control). The decimal load-args path stays for headless QEMU bring-up and
  enables the full kernel mask.
- status  -> vpnhide_format_status: backend id, kver, installed-hook mask,
  dominant error (ok / partial_hooks), via out_msg + clamp_to_line.
- stats   -> format wired; per-(uid,hook) counters are a follow-up (TODO).

Add backend ids (kmod/kpm/zygisk/lsposed, protocol §4.3) to the registry so
the status emitter and the app share one source — codegen now renders them
to C/Rust/Kotlin alongside the hook ids and error codes.
2026-06-28 11:53:30 +03:00
okhsunrog
e85f7cb401 codegen: hook-id registry (protocol §5 single source of truth)
data/hooks.toml is the global hook id space + status error codes shared by
the control/stats protocol: bit N of a config mask == hook id N == the id in
a stats line. scripts/codegen-hooks.py renders the matching id enums,
per-backend 'own' masks, and error codes for every language that touches the
protocol (kmod/KPM C, zygisk Rust, lsposed-native Rust, app Kotlin), so the
numbering can never diverge between backends. Wired into the CI drift-check
beside the iface-list codegen.

The script runs under uv (PEP 723, '#!/usr/bin/env -S uv run' so plain
./scripts/codegen-hooks.py works), matching the repo's other tooling; no
external deps. Generated outputs are rustfmt- and ktlint-clean by
construction. Registry is append-only: ids are permanent so old stats keep
meaning and old readers skip unknown ids.
2026-06-28 02:21:28 +03:00
okhsunrog
9d8a54cfaa chore(codegen): drop unused suffix forms digits_optional / any
Both forms came in with the codegen split (#91) but no [[vpn]]
rule has ever used them — the only `suffix=` rules are `digits`
(`wlan` test vector + `if` from #93). The grammar surface paid
for itself in ~150 lines of dead C/Rust helpers + their tests.

Drop them from VALID_KINDS, the parser, the C/Rust/Kotlin
emitters, and the helper test cases. If a future rule needs
either form, reintroduce alongside the rule that needs it.

Re-ran the codegen; tests pass for all four targets.
2026-04-26 05:16:18 +03:00
okhsunrog
54242b1140 fix(filter): catch tunnels renamed to if<N> (issue #86)
Add a single TOML rule `prefix = "if", suffix = "digits"` to the shared
matcher. Renames using the kernel's default anonymous-netdev naming
(`ip link set tun0 name if33`) — the exact attack from issue #86 — now
get hidden by every component (kmod, zygisk, lsposed, lsposed-native).

The shape is intentionally narrow: `if` + 1+ ASCII digits only. `ifb<N>`
(intermediate-functional-block traffic shaping) has a letter after `if`
and is not matched.
2026-04-26 04:08:36 +03:00
okhsunrog
38f9b56f5b feat(codegen): grammar forms digits_optional / any + generated tests for all 4 langs
Some checks are pending
CI / kmod (android13-5.15) (push) Waiting to run
CI / kmod (android14-5.15) (push) Waiting to run
CI / kmod (android14-6.1) (push) Waiting to run
CI / kmod (android15-6.6) (push) Waiting to run
CI / kmod (android16-6.12) (push) Waiting to run
CI / kmod (android12-5.10) (push) Waiting to run
CI / kmod (android13-5.10) (push) Waiting to run
CI / setup (push) Waiting to run
CI / lint (push) Blocked by required conditions
CI / zygisk (push) Blocked by required conditions
CI / lsposed (push) Blocked by required conditions
CI / portshide (push) Waiting to run
CI / release (push) Blocked by required conditions
Two follow-ups to #90 in one PR:

1. Two new match forms in data/interfaces.toml grammar:
     suffix = "digits_optional"   prefix + 0+ ASCII digits
     suffix = "any"                prefix + 1+ any chars
   Needed by the upcoming whitelist (PR-B) for patterns like
   `seth_lte\d*` and `v4-.+`. Not used by any current [[vpn]] rule, but
   the helper functions are exercised by direct unit tests in the
   generated test modules so a bug would surface before whitelist lands.

2. [[test]] vectors in data/interfaces.toml that the codegen renders
   into per-language unit tests:
     - zygisk + lsposed/native: #[cfg(test)] mod tests inside the
       generated iface_lists.rs (run via `cargo test`)
     - lsposed/app: a separate IfaceListsGeneratedTest under
       src/test/kotlin (run via `:app:testDebugUnitTest`)
     - kmod: a userspace test driver test_iface_lists.c — the
       generated header now has __KERNEL__-guarded includes so the
       same matcher compiles against libc, and a new lint step builds
       and runs it via gcc.
   36 fixed vectors today; trivial to grow as new rules / corner cases
   come up. CI catches drift on the next push: any single matcher that
   disagrees with the toml fails its job.

No production behavior change — generated matches_vpn / vpnhide_iface_is_vpn
/ IfaceLists.isVpnIface bodies are byte-identical to before; only the
helper functions and test modules grew.
2026-04-25 21:18:29 +03:00
Danila Gornushko
daa98f3138
refactor: drive VPN-iface matching from a single TOML source of truth (#90)
The kernel module, zygisk, lsposed-native, and the LSPosed Kotlin module
each had their own hand-written list of VPN interface name prefixes,
and the four had drifted: kmod/zygisk/HookEntry knew utun/l2tp/gre
while lsposed-native and DiagnosticsScreen only knew tun/wg/ppp/tap/
ipsec/xfrm. So the self-test could PASS while the hooks were actually
hiding more interfaces.

Move the rules to data/interfaces.toml and render four matchers from it
via scripts/codegen-interfaces.py — one per language target. A new lint
job re-runs the codegen and fails if anything drifts.

The match grammar is intentionally tiny so each codegen target
implements it without depending on regex (kernel C can't):
  exact / prefix / prefix+digits / contains.

Side effect: native diagnostics now agree with the hooks, so the
self-test in DiagnosticsScreen will recognize utun*, l2tp*, gre* and
*vpn* substrings as VPN tunnels (previously it would silently PASS on
those). The /proc/net/route check also moved from raw substring to
whitespace-tokenized matching, which avoids matching VPN-prefix
substrings that show up by chance inside hex-encoded IP addresses.

Existing zygisk filter unit tests still pass unchanged — public API of
is_vpn_iface_bytes / is_vpn_iface_cstr is preserved, only the body now
delegates to the generated matches_vpn().

Cargo.lock files updated incidentally (synced with Cargo.toml versions
that were already 0.7.1 in the manifests).
2026-04-25 20:53:11 +03:00