
45.03 MB
Android 10.0+
55
arm64-v8a
Verified safeScanned with ClamAV, APKiD, and Quark-Engine. No threats detected.
What's New
Added
Dashboard now splits issues into Errors (red, block protection) and Warnings (amber, setup is suboptimal but working). Four new warnings: kernel supports kmod but only Zygisk is installed; kmod and Zygisk both active simultaneously (means you have to remember per-app Z-off for banking / payment apps that detect Zygisk); debug logging left on; SELinux in Permissive mode (exposes six detection vectors that VPN Hide relies on the kernel to block).
Debug logging toggle in Diagnostics: off by default — VPN Hide, LSPosed hooks (VpnHide-NC/NI/LP and the package-visibility filter), and zygisk keep logcat near-silent. Start recording and Collect debug log automatically enable verbose logging for the duration of the capture and restore it afterwards, so the toggle is only needed if you want logs emitted continuously outside a capture. Errors always pass through so hook-install failures remain visible.
Diagnose wrong-variant kmod installs: Dashboard surfaces when the installed kernel module is built for a different GKI variant than your kernel (e.g. android13-5.10 installed on android14-6.1), when your kernel is missing kretprobes, or has no compatible kmod variant at all — and points to the right zip. The same boot-time insmod status is bundled into the Collect debug log zip on the Diagnostics screen for bug reports.
Expand Russian-apps filter: Pribyvalka 63, RosDomofon, Drivee, Setka, Twinby, GoodLine (4pda user feedback).
Expand Russian-apps filter: Youla, Delivery Club, SDEK, Russian Post, Dom.ru, ConsultantPlus, etc. — local retailers, pharmacies, food chains and services.
Multi-profile support: VPN Hide now targets every instance of a selected app across user profiles (work profile, MIUI Second Space, Private Space, secondary users). Previously hooks only matched the app in your primary profile — a work-profile Ozon would still see the VPN. Package-to-UID resolution at boot + at Save time now uses 'pm list packages -U --user all' and writes every UID to targets, so all profiles are covered automatically without any UI changes. Fixes the work-profile request in issue #15.
Changed
Changelog entries now live as per-PR Markdown fragments under changelog.d/ instead of a shared JSON section, and CHANGELOG.md is regenerated only at release time, so concurrent PRs no longer conflict on the changelog.
Dashboard no longer re-runs all checks (module detection, kprobes, SELinux, target counts, GitHub update check, etc.) on every tab switch. State is loaded once at startup and cached in RAM; the Refresh button in the top bar forces a reload. Update check is cached for 6 hours and re-runs when the app comes back to the foreground after sitting in the background longer than that — no background jobs, no notifications, just reactive to app lifecycle.
Help text on Protection screens (Tun / Apps / Ports) moved from a hard-to-discover ? icon in the top bar to always-visible collapsible cards at the top of each list. Users who read and understood the hints can collapse them — the state is remembered across app restarts.
Installed-app list is now loaded once at app start and cached in RAM, so switching between Protection tabs is instant instead of waiting for the icon+label load every time. Added a refresh button in the top bar to force a reload (also re-reads the per-screen target/observer files).
Diagnostics and Protection tabs now open instantly after the first load. Previously each tab switch re-ran all root-shell probes (module detection, target/observer files, pm lookups, 500ms of check functions), now those results live in process-lifetime caches. Diagnostics specifically: checks run once when VPN is first detected active, then results are fixed for the process — hooks don't change mid-session, so re-running is pointless. When VPN is off, both Dashboard and Diagnostics show a shared "turn on VPN, then Retry" banner. Log-capture tools (debug logging toggle, logcat recorder, debug-zip export) are now always visible on Diagnostics, regardless of VPN state.
Diagnostics tab opens instantly even on the first time you tap it. The cache that stores the protection-check results now starts running in the background as soon as the app launches, instead of waiting for you to navigate to the tab — by the time you switch from Dashboard to Diagnostics, the results are usually already there.
Tun help accordion now spells out which layers need a target-app restart after Save: L (LSPosed) and K (kmod) apply immediately, Z (Zygisk) hooks are per-process so any just-toggled target with Z needs a force-stop + reopen to pick up the change.
Fixed
App Hiding: marking the same app as both H (Hidden) and O (Observer) caused it to crash on startup — the app would query its own PackageInfo, our system_server hook matched it as an observer and stripped its own package from the result, and the framework bailed. Roles are now mutually exclusive: toggling one clears the other, and existing H+O configs are migrated to O-only on first launch.
Dashboard now shows a consistent version string for all modules. Kernel-module, Zygisk and Ports module cards used to display the Magisk-style 'vX.Y.Z' from their module.prop, while the LSPosed hook module card showed the Android-style 'X.Y.Z' from the APK's versionName — on the same screen, for the same version number. The 'v' prefix is now stripped at parse time so every card reads 'X.Y.Z' (or 'X.Y.Z-N-gSHA' for dev builds).
Dev builds of the app no longer trigger a false 'module version mismatch' warning on the Dashboard. The check now strips the git-describe dev suffix (e.g. 0.6.2-14-g1f2205e vs module 0.6.2) before comparing.
Dev builds of the app now correctly receive "new version available" notifications. The comparison used to bail on the git-describe suffix (0.6.2-14-gSHA) and silently treat it as "no update", so testers running dev APKs never saw release prompts.
Protection toolbar: the filter icon indicator for "filter is applied" no longer blends into the topbar background on Material You palettes. Active state is now a FilledIconButton (primary / onPrimary pair, guaranteed contrast) instead of a plain icon tinted with primary on top of primaryContainer.
What's Changed
feat: debug logging toggle, off by default by @okhsunrog in #40
feat(lsposed): H+O mutex, inline help, issue severity split by @okhsunrog in #42
fix(lsposed): skip version-mismatch warning for dev builds by @okhsunrog in #47
feat(scripts): changelog as per-PR TOML fragments by @okhsunrog in #49
feat: diagnose wrong-variant kmod installs end-to-end by @okhsunrog in #38
fix(scripts): release-only CHANGELOG.md + Markdown fragments by @okhsunrog in #52
perf(lsposed): cache installed-app list at startup by @okhsunrog in #53
feat(lsposed): expand Russian-apps filter (4pda batch) by @okhsunrog in #50
perf(lsposed): cache Dashboard state + update check by @okhsunrog in #54
perf(lsposed): cache Diagnostics + Protection state for the session by @okhsunrog in #56
feat: target apps across all user profiles (work profile, etc.) by @okhsunrog in #57
docs(lsposed): spell out target-restart rules in Tun help accordion by @okhsunrog in #58
perf(lsposed): pre-warm DiagnosticsCache at app launch by @okhsunrog in #59
Description
Hide an active Android VPN connection from selected apps.
Why VPN Hide over alternatives?
Existing modules like NoVPNDetect and NoVPNDetect Enhanced only cover Java API detection and hook inside the target app's process via Xposed. This has two critical problems:
1. Invisible to anti-tamper? — any app with memory injection checks detects the Xposed hooks and refuses to work. The NoVPNDetect Enhanced author explicitly states: "The module will not work if the target app has LSPosed protection or memory injection checks. For example, MirPay, T-Bank."
2. No native coverage — apps using C/C++ code, cross-platform frameworks (Flutter, React Native), or direct syscalls can detect VPN through ioctl, getifaddrs, netlink sockets, and /proc/net/*. These vectors are completely missed by Java-only hooks.
VPN Hide solves both problems with a two-layer architecture:
Layer 1 — Java API (LSPosed module): hooks system_server, not the target app. NetworkCapabilities, NetworkInfo, and LinkProperties are filtered at the Binder level before data reaches the app's process. The app receives clean data over IPC — no injection into its process, nothing for anti-tamper to detect.
Layer 2 — Native (kmod or Zygisk): covers every native detection path:
- kmod (recommended) — kernel-level kretprobe hooks. Filters ioctl (SIOCGIFFLAGS, SIOCGIFNAME, SIOCGIFCONF), getifaddrs/netlink dumps (RTM_GETLINK, RTM_GETADDR), and /proc/net/* reads — all before the syscall returns to userspace. Zero in-process footprint. No library injection. Nothing to detect.
- Zygisk (alternative) — inline-hooks libc.so inside the app process. Same native coverage as kmod but runs in-process, so it's theoretically detectable by advanced anti-tamper. Use this if your kernel isn't supported by kmod.
The target app's process is completely untouched (with kmod + LSPosed) — no Xposed, no inline hooks, no modified memory regions. This makes VPN Hide work with MirPay, T-Bank, Alfa-Bank and other banking/government apps that actively detect and block Xposed-based modules.
Which modules do I need?
You always need the VPN Hide app (the main APK) plus one native module. The app's Dashboard will detect your device and recommend the right one:
- kmod (recommended) — fully out-of-process, invisible to anti-tamper. Requires a supported GKI kernel.
- Zygisk — use this if your kernel isn't supported by kmod.
See Install for step-by-step instructions.
Install
Download the latest release.
Step 1 — VPN Hide app + LSPosed
1. Install the VPN Hide APK as a regular app.
2. In LSPosed manager, enable the VPN Hide module and add System Framework to its scope.
3. Reboot (required — LSPosed hooks are injected into system_server at boot, so the module must be active before system_server starts).
4. Open the VPN Hide app and grant it root access (Magisk will prompt automatically; on KernelSU-Next, grant permission manually in the manager).
Step 2 — Native module
Open the VPN Hide app. The Dashboard tab will detect your device and kernel, and tell you exactly which native module to install:
- If your kernel is supported, it will recommend a specific kmod file (e.g. vpnhide-kmod-android14-6.1.zip).
- If not, it will recommend the Zygisk module (vpnhide-zygisk.zip).
Install the recommended module:
- kmod: via KernelSU-Next manager -> Modules -> Install from storage.
- Zygisk: via KernelSU-Next or Magisk manager -> Modules.
Reboot after installing the native module.
Step 3 — Select target apps
Open the VPN Hide app -> Apps tab. Use the L / K / Z toggles to control which protection layers apply to each app (LSPosed, Kernel module, Zygisk), or tap the row to toggle all layers at once. Tap Save.
After changing targets, force-stop and restart the affected apps — hooks take effect on the next app launch.
Note: some apps detect Zygisk hooks. For those apps, keep Z disabled and rely on kmod + LSPosed.
Verify
The app has a built-in diagnostics system that catches most setup problems automatically.
Dashboard (runs on every app launch):
- Module status for all three layers (installed, active, version, target count).
- LSPosed configuration validation — reads the LSPosed database to verify that VPN Hide is enabled, System Framework is in scope, and no extra apps are scoped (a common misconfiguration).
- Version mismatch detection — compares installed module versions with the running app version and tells you exactly what to update.
- Native module recommendation — detects your kernel and maps it to the right kmod artifact, or recommends Zygisk if unsupported.
- Live protection check (when VPN is active) — runs 16 native checks and 5 Java API checks to verify that VPN is actually hidden.
Any issues found are shown as actionable cards with specific instructions.
Diagnostics tab — detailed per-check breakdown with individual PASS/FAIL results for all 26 detection vectors. Useful for troubleshooting when the Dashboard shows partial protection.
Split tunneling
Works correctly with split-tunnel VPN configurations. Only the apps in the target list are affected.
Detection apps that compare device-reported public IP against external checkers require split tunneling — the detection app's traffic must exit through the carrier, not the tunnel.
Threat model
VPN Hide hides an active VPN from specific apps. It is NOT designed for:
- Hiding root or custom ROM presence.
- Bypassing Play Integrity.
- Fooling server-side detection (DNS leakage, IP blocklists, latency/TLS fingerprinting).
Known limitations
- kmod requires a GKI kernel with CONFIG_KPROBES=y (standard on Android 12+ devices).
- LSPosed requires LSPosed, LSPosed-Next, or Vector.
- Zygisk is arm64 only.
- Direct svc #0 syscalls bypass Zygisk's libc hooks — that's what kmod is for.
- Server-side detection is unfixable client-side — use split tunneling.
License
Copyright © 2025 okhsunrog
MIT License
Rate this app
Ratings & reviews
No reviews yet.