Commit graph

24 commits

Author SHA1 Message Date
okhsunrog
c571e59afd fix: tighten /data/system/vpnhide_*.txt to 0640 root:system
The three files written under /data/system/ to coordinate state between
the LSPosed system_server hook and the app — vpnhide_uids.txt,
vpnhide_hidden_pkgs.txt, vpnhide_observer_uids.txt — were chmodded
0644 root:root. /data/system/ itself is mode 0775 system:system,
traversable by untrusted apps, so any "other"-readable file there is
both enumerable (`ls /data/system/`) and openable by name. Untrusted
apps could:

  cat /data/system/vpnhide_uids.txt          # all target UIDs
  cat /data/system/vpnhide_hidden_pkgs.txt   # the hide list
  cat /data/system/vpnhide_observer_uids.txt # observer UIDs

If the reader's own UID is in vpnhide_uids.txt, that's a positive
"vpnhide is filtering me right now" detection — strictly stronger than
the presence-of-marker fingerprint we already closed for
vpnhide_hook_active in PR #100.

Switch every write site to mode 0640 + chown root:system. system_server
runs as UID 1000 with `system` (GID 1000) in its supplementary groups,
so it still gets read via the group bit. Untrusted apps fall to the
"other" octet (now ---) and get EACCES on open.

Empirically verified on Pixel:
  before: 644 root:root → `cat` from untrusted shell succeeds
  after:  640 root:system → untrusted shell EACCES;
                            `su system -c cat` (uid=1000) reads fine,
                            mirroring what system_server sees

Boot-time service.sh in both kmod and zygisk modules also include an
idempotent migration block that re-stamps any pre-PR files left at
0644 by an older version on the next boot. Closes #36 in REVIEW.
2026-04-26 16:41:06 +03:00
okhsunrog
fccc0387a2 fix: use awk literal match instead of grep regex for pm-list parsing
Six places parsed `pm list packages -U` output with
`grep "^package:${pkg} "`, which treats `pkg` as a regex — dots in
package names cross-match, in theory mapping `com.x.y` to a
hypothetical `comXxXy` package. In practice Android won't let two
such packages coexist, so this has never bit anyone, but the fix is
free and unifies with the literal `awk '$1 == p'` pattern that
portshide/vpnhide_ports_apply.sh has been using all along.

Touched:
* kmod/module/service.sh, zygisk/module/service.sh — boot-time UID
  resolution for kmod and lsposed/zygisk targets.
* lsposed/.../{AppPickerScreen,AppHidingScreen,ShellUtils}.kt — three
  call-sites that build shell pipelines from Kotlin to resolve UIDs
  for /proc/vpnhide_targets, the system_server hook uids file, and
  the package-visibility observer uids file.
* lsposed/.../DashboardData.kt — the self-multi-profile detection
  that warns when vpnhide is installed in more than one profile.
2026-04-26 15:44:19 +03:00
okhsunrog
7f22875e6b chore: release v0.7.1 2026-04-21 16:45:04 +03:00
okhsunrog
4cf512c6ae chore: release v0.7.0 2026-04-20 02:54:26 +03:00
okhsunrog
16578a15d2 feat: target apps across all user profiles (work profile, etc.)
Android user profiles (work profile, MIUI Second Space, Private Space,
secondary users) each give the same package its own UID in the
namespace `<user>*100000 + <app_id>`. Previously every pkg→UID
resolver used plain `pm list packages -U`, which only emits UIDs for
the primary user, so the work-profile copy of Telegram kept seeing
the VPN even though the user had marked Telegram as a target.

Switch every resolver to `pm list packages -U --user all`. The pm
output format for multi-profile apps is comma-separated on one line:

    package:com.android.chrome uid:10187,1010187

Each call site now splits on `,` and emits one UID per line so every
profile's copy is individually matched by the hooks. No UI changes —
"mark Telegram as a target" just now means "in every profile it's
installed in".

Resolvers touched (all places found by an audit, no duplicates left):

  Shell (boot-time):
    kmod/module/service.sh
    zygisk/module/service.sh
    portshide/module/vpnhide_ports_apply.sh

  Kotlin (save-time via suExec):
    AppPickerScreen.kt       — buildUidResolver
    AppHidingScreen.kt       — buildHidingUidResolver
    ShellUtils.kt            — ensureSelfInTargets
    TargetsCache.kt          — PM_LIST batch script + parser

Verified on a Pixel 4a with a managed profile (user 10):
  - Chrome toggled in LZ on primary → both 10187 and 1010187 land in
    /data/system/vpnhide_uids.txt.
  - Primary-only apps (Ozon, etc.) still resolve to a single UID.
  - ensureSelfInTargets correctly adds both UIDs when vpnhide is
    installed across profiles.
2026-04-20 01:27:11 +03:00
okhsunrog
b09f05ce60 feat: diagnose wrong-variant kmod installs end-to-end
Users routinely installed the wrong GKI variant of the kmod zip and
saw no signal beyond "installed, inactive". This adds a full chain
from build to diagnostics so the wrong-variant case is both obvious
to the user and fully captured in bug reports.

Why each piece exists:

- CI stamps `gkiVariant=<kmi>` into each variant's `module.prop`
  so the app can identify what was installed without guessing.
- `post-fs-data.sh` records `/data/adb/vpnhide_kmod/load_status`
  (boot_id, uname -r, gki_variant, insmod exit+stderr, kprobes,
  root manager) and `load_dmesg` at every boot — this survives
  reboots and is the only record of insmod failures by the time
  the user opens the app.
- Dashboard reads both, always computes the kernel-based
  recommendation, and emits targeted issues: wrong-variant,
  unknown-variant (pre-stamp zip that also failed to load),
  kmod-on-unsupported-kernel, kprobes-missing, or generic
  load-failed with the captured stderr.
- Diagnostics screen adds a "Kmod load trace" card so bug
  reports can come in as a screenshot, and the debug zip
  includes load_status + load_dmesg for deeper analysis.

Also aligns `lsposed/native/Cargo.lock` with Cargo.toml (0.6.1
→ 0.6.2) — a real stale-lock fix surfaced by the gradle build.
2026-04-19 20:41:30 +03:00
okhsunrog
b85673c81c chore: release v0.6.2 2026-04-17 16:24:53 +03:00
okhsunrog
00ba398f36 fix(service.sh): wait for PackageManager to index user apps before resolving UIDs
`pm list packages` starts responding to IPC very early in boot but
returns only system packages for several more seconds. service.sh's
previous `pm list packages >/dev/null && break` loop exited as soon as
PM was alive — before user-installed packages (including the vpnhide
app itself and any chosen targets) were indexed. The subsequent
`pm list packages -U | grep "^package:$pkg "` returned nothing, so
/data/system/vpnhide_uids.txt was written empty, and the LSPosed hook
in system_server cached an empty target set for the session. Result:
all Java-level filtering silently disabled until the next reboot where
we got lucky on timing.

Gate the boot wait on our own package being visible in the list (with
a 60s budget instead of 30s). That guarantees PM has moved past the
system-only snapshot before we read target names.

Also add per-call diagnostic logs to the three writeToParcel hooks
(NC, NI, LP) — `VpnHide-NC`, `VpnHide-NI`, `VpnHide-LP` tags — so the
next "Java check fails with zygisk on / passes with zygisk off" style
report can be diagnosed from logs alone instead of a live instrumented
build. The per-call volume is modest compared to system_server's own
logging and the logs live inside the LSPosed bridge log.
2026-04-16 15:33:46 +03:00
okhsunrog
53aacaa870 chore: release v0.6.1 2026-04-16 00:24:58 +03:00
okhsunrog
a37b1b7cd8 fix(modules): add META-INF for Magisk versions before v28
Magisk before v28 requires META-INF/com/google/android/update-binary
+ updater-script in module zips to extract them; without these the
manager fails with an unpack error (issue #23). Magisk v28+ removed
this requirement, which is why the bug only shows up on older managers.

Added the standard Magisk template (same one already used by the
zygisk module) to portshide and kmod. CI's `(cd module && zip -qr)`
step picks up the new files automatically.
2026-04-16 00:23:05 +03:00
okhsunrog
a5c21f262c chore: release v0.6.0 2026-04-15 18:42:08 +03:00
okhsunrog
00a40db04c chore: bump version to v0.5.3 2026-04-15 00:49:37 +03:00
okhsunrog
8420965cd9 chore: bump version to v0.5.2 2026-04-14 17:03:50 +03:00
okhsunrog
ca9773d14f Fix hardcoded v0.1.0 in customize.sh — read version from module.prop 2026-04-14 16:56:57 +03:00
okhsunrog
0073ef7030 Fix false LSPosed warnings and improve target detection
- Detect LSPosed in all known module paths (zygisk_vector, zygisk_lsposed, lsposed)
- Skip LSPosed config warnings when hooks are already active at runtime
- Check all modules for empty targets, not just LSPosed
- Bump version to v0.5.1
2026-04-14 02:57:37 +03:00
okhsunrog
e5a85c5b7d chore: bump version to v0.5.0 2026-04-14 00:39:21 +03:00
okhsunrog
25e7d1d1be feat: add dashboard, per-component target lists, LSPosed status detection
Dashboard as new landing screen with module status cards (kmod, zygisk,
LSPosed), aggregated protection checks (native + Java API), and issue
alerts. Uses sealed types for type-safe state modeling (invalid states
are unrepresentable).

Three separate target lists: kmod, zygisk, and lsposed each have
independent targets.txt. Users can configure per-app which layers
protect it (L/K/Z chips in app picker). Service.sh scripts decoupled —
each resolves only its own component, with migration from unified lists.

HookEntry writes /data/system/vpnhide_hook_active with version and
boot_id so the app can detect if LSPosed hooks are active this boot.

VPN Hide app auto-adds itself to all target lists for self-diagnostics.
If just added, shows "restart needed" instead of stale check results.
App hides itself from the app picker and target counts.

Diagnostics tab no longer runs checks without VPN — shows banner only.
Removed "Run All" button (results are cached per process lifetime).

Splash screen follows system dark/light theme via Material3 DayNight.
2026-04-13 18:03:25 +03:00
okhsunrog
9ba7bfb127 refactor: drop WebUI and action.sh from kmod and zygisk modules
The VPN Hide app is now the sole UI for target management. WebUI was
KernelSU-Next-only and redundant since the app works on both KSU and
Magisk. Remove webroot/, action.sh, and all references across docs,
install scripts, module descriptions, and code comments.
2026-04-13 16:28:39 +03:00
okhsunrog
52d28bd743 chore: bump version to v0.4.2 2026-04-13 03:35:56 +03:00
okhsunrog
178deace6a fix: run update-version.sh for v0.4.1 2026-04-13 01:07:45 +03:00
okhsunrog
e12c58cace fix: shell injection guard, use named constants, bypass own hooks for /proc/self/maps
- WebUI: validate package names against [a-zA-Z0-9_.\-]+ before
  interpolating into shell commands (both kmod and zygisk copies)
- zygisk hooks.rs: use RTM_NEWLINK/RTM_NEWADDR from filter.rs instead
  of magic constants 16/20
- zygisk lib.rs: read /proc/self/maps via raw libc::open in
  scrub_shadowhook_maps to bypass our own hooked_openat
- kmod: add comment explaining why seq->buf access without seq->lock
  is safe in fib_route_ret (seq_read holds the mutex around ->show())
- kmod: add comment clarifying MODULE_LICENSE("GPL") vs MIT SPDX
2026-04-12 23:12:45 +03:00
okhsunrog
7b867c8b64 chore: add VERSION file and update-version.sh, bump to v0.4.0
Single VERSION file in repo root as the source of truth. The script
update-version.sh propagates it to all 5 locations: kmod module.prop,
zygisk module.prop, zygisk Cargo.toml, lsposed build.gradle.kts,
test-app build.gradle.kts. versionCode = major*10000 + minor*100 + patch.
2026-04-12 03:23:16 +03:00
okhsunrog
c52a6711ff fix: UID resolution in save commands used printf \n escapes instead of real newlines
The shell variable $UIDS accumulated UIDs with literal \n (two chars)
instead of actual newlines. printf "$UIDS" wrote garbage to
/proc/vpnhide_targets and vpnhide_uids.txt. The empty-targets case
used bare > redirect which never triggers the proc write handler.

Fix: accumulate UIDs with real newlines (like service.sh does), use
echo "$UIDS" for output, and echo (writes \n) for the empty case so
the kmod write handler fires and clears the UID list.

Affects: APK target picker, kmod WebUI, zygisk WebUI.
2026-04-12 03:14:59 +03:00
okhsunrog
12daca5c1a monorepo: combine vpnhide-zygisk, vpnhide (lsposed), and vpnhide-kmod
Unified repository for the complete Android VPN-hiding stack:
- zygisk/ — Rust Zygisk module (inline libc hooks via shadowhook)
- lsposed/ — Kotlin LSPosed module (Java API + system_server hooks)
- kmod/ — C kernel module (kretprobe hooks, invisible to anti-tamper)

CI workflows use path filters to build only the changed component.
2026-04-11 15:01:49 +03:00