Six unrelated drift fixes that accumulated since they were last
synced. Each is independent of the rest:
* README{.en,}.md — kmod claim "filters /proc/net/*" trimmed to
/proc/net/route. The other /proc/net files are SELinux-blocked
for untrusted apps and the coverage table already says so.
* kmod/README.md — hook table and architecture note updated from
dev_ifconf to sock_ioctl. dev_ifconf gets inlined by Clang LTO
on GKI 5.10 so the kretprobe silently never fires; sock_ioctl
has been the actual hook target since the vpnhide_kmod.c fix.
* zygisk/README.md — five inline hooks now, not four (recv was
added separately because bionic's recv tail-calls recvfrom).
Also clarified pre_app_specialize runs in the forked child, not
zygote, matching the lifecycle block in lib.rs.
* docs/development.md — JDK requirement matches CI image (17, not
21); document ANDROID_NDK_ROOT quirk for Gobley; CI lint list
expanded to match what ci.yml actually runs.
* docs/development.md + lsposed/README.md — explain Gobley (the
Gradle plugin pair that builds lsposed/native/ and bundles the
.so + UniFFI Kotlin bindings into the APK). Previously absent
from all *.md.
3.7 KiB
vpnhide -- LSPosed module + target picker app
Hooks writeToParcel() in system_server to strip VPN data before Binder serialization reaches target apps. Part of vpnhide.
The APK also serves as the target management UI for the entire vpnhide project — it writes targets for both kmod and zygisk modules.
Zero presence in the target app's process -- only "System Framework" is needed in the LSPosed scope.
What it hooks
writeToParcel() on three classes inside system_server:
| Class | Effect |
|---|---|
NetworkCapabilities |
VPN transport and capability flags stripped before serialization. Covers hasTransport(VPN), getAllNetworks() + VPN scan, getTransportInfo(). |
NetworkInfo |
VPN type rewritten to WIFI before serialization |
LinkProperties |
VPN interface name and routes stripped before serialization |
Uses a ThreadLocal save/restore pattern so the original values are preserved for non-target callers.
Per-UID filtering
Filtering is controlled by Binder.getCallingUid() -- only apps whose UID appears in the target list see the filtered view. System services, VPN clients, and everything else see real data.
Target management
Target UIDs are loaded from /data/system/vpnhide_uids.txt. A FileObserver (inotify) watches for changes and reloads the list immediately -- no reboot needed.
This file is written by:
- The VPN Hide app (this APK's target picker UI)
- The module's
service.shon boot
Target picker app
The APK includes a Compose UI for managing target apps across all vpnhide modules:
- Lists all installed apps with icons, names, and package names
- Text search filter
- System apps toggle (selected system apps always visible)
- Save writes to all target locations via
su:/data/adb/vpnhide_kmod/targets.txt(if kmod is installed)/data/adb/vpnhide_zygisk/targets.txt(if zygisk is installed)/data/adb/modules/vpnhide_zygisk/targets.txt(Magisk module dir copy)/proc/vpnhide_targets(kmod live update, no reboot needed)/data/system/vpnhide_uids.txt(system_server hooks, live reload via inotify)
Works on KernelSU, Magisk, and any other root solution.
Install
- Build the APK (
./gradlew assembleDebug). - Install:
adb install app/build/outputs/apk/debug/app-debug.apk. - Open LSPosed/Vector manager, go to Modules, enable VPN Hide.
- Add "System Framework" to the module's scope. No other apps should be in scope.
- Reboot.
- Open the VPN Hide app to manage target apps.
Combined use with kmod
For apps with aggressive anti-tamper SDKs, full VPN hiding requires covering both native and Java API paths without any hooks in the target app's process:
- kmod covers native:
ioctl,getifaddrs(netlink),/proc/net/route. - This module covers Java APIs:
NetworkCapabilities,NetworkInfo,LinkPropertiesviawriteToParcel()insystem_server.
Together they provide complete VPN hiding with zero footprint in the target process.
Debugging
adb logcat | grep VpnHide
Build
./gradlew assembleDebug
Requires JDK 17 or later. Output: app/build/outputs/apk/debug/app-debug.apk.
The build cross-compiles lsposed/native/ (Rust crate) for aarch64-linux-android via cargo-ndk and bundles the resulting libvpnhide_checks.so into the APK's jniLibs/, plus auto-generated UniFFI Kotlin bindings under package dev.okhsunrog.vpnhide.checks. Both steps are driven by Gobley Gradle plugins (dev.gobley.cargo + dev.gobley.uniffi) — no manual cargo invocation needed. See ../docs/development.md for the full prereq list.
License
MIT. See LICENSE.