Hide Veil itself from target apps by default

An installed hooking module is as much a tell as a root manager, but
Veil's own package wasn't in the hidden list, so any scoped app could
find it with getPackageInfo or the installed-package lists. It's now a
default alongside KernelSU and Magisk.

DEFAULT_HIDDEN_PACKAGES only applies while no list has been saved, so a
one-time migration adds Veil to an existing list the first time the
settings screen opens; a flag stops it from re-adding Veil after a
manual removal.

Discovery is unaffected: the hooked process resolves Veil's provider
through ActivityManager, not the hooked ApplicationPackageManager.
Verified on device (Android 16, Vector) with Veil Probe: getPackageInfo
for Veil now throws NameNotFoundException, the probe still registers,
and every other hooked path stays hidden.

Co-Authored-By: agent <agent@bdeshi.space>
This commit is contained in:
bdeshiandagent committed 2026-11-03 18:26:38 +06:00
1 parent 83cbb59ff8
commit 6dfb0d5779
4 files changed
+22 -5

No files matched your search

+1 -1
View File
@@ -35,7 +35,7 @@ Per app, per feature — anything not listed gets everything hidden by default.
| USB / wireless debugging | `adb_enabled` / `adb_wifi_enabled` |
| Developer options | Developer Options enabled state |
| Mock location | Legacy pre-Marshmallow flag only |
| Root / manager apps | Configurable package list (default: KernelSU, Magisk) |
| Root / manager apps | Package list (default: KernelSU, Magisk, Veil itself) |
| Build tags/type | Real build swapped for `release-keys` / `user` |
Doesn't cover native-level root/hook detection or remote attestation (Play Integrity,
@@ -3,10 +3,12 @@ package space.bdeshi.veil
data class FeatureInfo(val label: String, val description: String)
object Features {
const val SELF_PACKAGE = "space.bdeshi.veil"
const val PREFS_NAME = "veil_prefs"
const val KEY_PREFIX = "features_"
const val HIDDEN_PACKAGES_KEY = "hidden_packages"
const val DISCOVERED_PACKAGES_KEY = "discovered_packages"
const val SELF_HIDDEN_MIGRATED_KEY = "self_hidden_migrated"
const val PROVIDER_AUTHORITY = "space.bdeshi.veil.provider"
const val ACCESSIBILITY = "accessibility"
@@ -52,9 +54,13 @@ object Features {
),
)
// Veil itself belongs here too: an installed hooking module is as much a tell as a root
// manager. Hiding it from PackageManager doesn't affect discovery, which resolves Veil's
// provider through ActivityManager rather than the hooked ApplicationPackageManager.
val DEFAULT_HIDDEN_PACKAGES: Set<String> = setOf(
"me.weishu.kernelsu",
"com.topjohnwu.magisk",
SELF_PACKAGE,
)
fun keyFor(packageName: String): String = KEY_PREFIX + packageName
@@ -56,6 +56,7 @@ class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
prefs = getSharedPreferences(Features.PREFS_NAME, Context.MODE_PRIVATE)
migrateSelfIntoHiddenList()
val padding = dp(16)
val root = LinearLayout(this).apply {
@@ -203,6 +204,18 @@ class SettingsActivity : AppCompatActivity() {
return cardView to content
}
// A list saved before Veil joined DEFAULT_HIDDEN_PACKAGES never picks up the new default,
// so add it once. The flag keeps a later manual removal from being undone.
private fun migrateSelfIntoHiddenList() {
if (prefs.getBoolean(Features.SELF_HIDDEN_MIGRATED_KEY, false)) return
val editor = prefs.edit().putBoolean(Features.SELF_HIDDEN_MIGRATED_KEY, true)
if (prefs.contains(Features.HIDDEN_PACKAGES_KEY)) {
editor.putStringSet(Features.HIDDEN_PACKAGES_KEY, hiddenPackages() + Features.SELF_PACKAGE)
}
editor.apply()
fixPermissions()
}
private fun hiddenPackages(): Set<String> =
prefs.getStringSet(Features.HIDDEN_PACKAGES_KEY, Features.DEFAULT_HIDDEN_PACKAGES) ?: Features.DEFAULT_HIDDEN_PACKAGES
@@ -13,8 +13,6 @@ import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
private const val SELF_PACKAGE = "space.bdeshi.veil"
// Keyed by the Settings class each key actually lives on, so we only ever install hooks on
// the one class that could plausibly be asked about a given feature — never both.
private val SECURE_NULL_STRING_KEYS_BY_FEATURE = mapOf(
@@ -46,7 +44,7 @@ private fun keysForEnabledFeatures(
class VeilHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: LoadPackageParam) {
if (lpparam.packageName == SELF_PACKAGE) return
if (lpparam.packageName == Features.SELF_PACKAGE) return
reportScoped(lpparam)
@@ -114,7 +112,7 @@ class VeilHook : IXposedHookLoadPackage {
private fun openPrefs(): XSharedPreferences? {
return try {
XSharedPreferences(SELF_PACKAGE, Features.PREFS_NAME).apply { reload() }
XSharedPreferences(Features.SELF_PACKAGE, Features.PREFS_NAME).apply { reload() }
} catch (t: Throwable) {
null
}