Changes for accessibility and content provider
- Remove the Settings Provider hook and switch to the ContentProvider$Transport hook - Fix the accessibility manager hook
This commit is contained in:
parent
67f8126bf4
commit
0dfd499476
8 changed files with 116 additions and 162 deletions
|
|
@ -14,7 +14,7 @@ class AccessibilityPreset : BasePreset(NAME) {
|
|||
),
|
||||
ReplacementItem(
|
||||
name = Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
|
||||
value = null,
|
||||
value = "",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ class DeveloperOptionsPreset : BasePreset("dev_options") {
|
|||
ReplacementItem(
|
||||
name = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
|
||||
value = "0",
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import icu.nullptr.hidemyapplist.common.JsonConfig
|
|||
import icu.nullptr.hidemyapplist.common.Utils
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.AccessibilityHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.ActivityHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.ContentProviderHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.IFrameworkHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.PlatformCompatHook
|
||||
import icu.nullptr.hidemyapplist.xposed.hook.PmsHookTarget28
|
||||
|
|
@ -154,6 +155,7 @@ class HMAService(val pms: IPackageManager) : IHMAService.Stub() {
|
|||
frameworkHooks.add(ActivityHook(this))
|
||||
frameworkHooks.add(PmsPackageEventsHook(this))
|
||||
frameworkHooks.add(AccessibilityHook(this))
|
||||
frameworkHooks.add(ContentProviderHook(this))
|
||||
|
||||
frameworkHooks.forEach(IFrameworkHook::load)
|
||||
logI(TAG, "Hooks installed")
|
||||
|
|
|
|||
|
|
@ -1,148 +0,0 @@
|
|||
package icu.nullptr.hidemyapplist.xposed
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import com.github.kyuubiran.ezxhelper.utils.findMethod
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import de.robv.android.xposed.IXposedHookLoadPackage
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage
|
||||
import icu.nullptr.hidemyapplist.common.JsonConfig
|
||||
import icu.nullptr.hidemyapplist.common.SettingsPresets
|
||||
import icu.nullptr.hidemyapplist.common.Utils
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
|
||||
import org.frknkrc44.hma_oss.common.BuildConfig
|
||||
import java.io.File
|
||||
|
||||
class HMASettingsProviderService : IXposedHookLoadPackage {
|
||||
companion object {
|
||||
private const val TAG = "SettingsProviderHook"
|
||||
}
|
||||
|
||||
init {
|
||||
searchDataDir()
|
||||
}
|
||||
|
||||
private lateinit var dataDir: String
|
||||
private lateinit var configFile: File
|
||||
|
||||
private val configLock = Any()
|
||||
private var configLastModified: Long = 0
|
||||
|
||||
var config = JsonConfig().apply { detailLog = true }
|
||||
private set
|
||||
|
||||
private fun searchDataDir() {
|
||||
File("/data/system").list()?.forEach {
|
||||
if (it.startsWith("hide_my_applist")) {
|
||||
if (!this::dataDir.isInitialized) {
|
||||
val newDir = File("/data/misc/$it")
|
||||
File("/data/system/$it").renameTo(newDir)
|
||||
dataDir = newDir.path
|
||||
} else {
|
||||
File("/data/system/$it").deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
File("/data/misc").list()?.forEach {
|
||||
if (it.startsWith("hide_my_applist")) {
|
||||
if (!this::dataDir.isInitialized) {
|
||||
dataDir = "/data/misc/$it"
|
||||
} else if (dataDir != "/data/misc/$it") {
|
||||
File("/data/misc/$it").deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!this::dataDir.isInitialized) {
|
||||
dataDir = "/data/misc/hide_my_applist_" + Utils.generateRandomString(16)
|
||||
}
|
||||
|
||||
File("$dataDir/log").mkdirs()
|
||||
configFile = File("$dataDir/config.json")
|
||||
|
||||
logI(TAG, "Data dir: $dataDir")
|
||||
}
|
||||
|
||||
private fun loadConfig() {
|
||||
if (!configFile.exists()) {
|
||||
logI(TAG, "Config file not found")
|
||||
return
|
||||
}
|
||||
|
||||
if (configLastModified == configFile.lastModified()) {
|
||||
return
|
||||
}
|
||||
|
||||
configLastModified = configFile.lastModified()
|
||||
|
||||
val loading = runCatching {
|
||||
val json = configFile.readText()
|
||||
JsonConfig.parse(json)
|
||||
}.getOrElse {
|
||||
logE(TAG, "Failed to parse config.json", it)
|
||||
return
|
||||
}
|
||||
if (loading.configVersion != BuildConfig.CONFIG_VERSION) {
|
||||
logW(TAG, "Config version mismatch, need to reload")
|
||||
return
|
||||
}
|
||||
config = loading
|
||||
logI(TAG, "Config loaded")
|
||||
}
|
||||
|
||||
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
|
||||
logI(TAG, "Load hook")
|
||||
|
||||
findMethod(
|
||||
"com.android.providers.settings.SettingsProvider",
|
||||
lpparam.classLoader,
|
||||
) {
|
||||
name == "call"
|
||||
}.hookBefore { param ->
|
||||
loadConfig()
|
||||
|
||||
val contentProvider = param.thisObject as ContentProvider
|
||||
val method = param.args[0] as String?
|
||||
val name = param.args[1] as String?
|
||||
|
||||
val caller = contentProvider.callingPackage
|
||||
if (!config.scope.containsKey(caller)) {
|
||||
// logD(TAG, "Not enabled app: $caller")
|
||||
return@hookBefore
|
||||
}
|
||||
|
||||
logD(TAG, "@call caller: $caller, method: $method, name: $name")
|
||||
|
||||
when (method) {
|
||||
"GET_global", "GET_secure", "GET_system" -> {
|
||||
val replacement = getSpoofedSetting(caller, name)
|
||||
if (replacement != null) {
|
||||
logD(TAG, "@getSettings returned replacement for $caller: ${replacement.value}")
|
||||
param.result = Bundle().apply {
|
||||
putString(Settings.NameValueTable.VALUE, replacement.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpoofedSetting(caller: String?, name: String?): ReplacementItem? {
|
||||
synchronized(configLock) {
|
||||
if (caller == null || name == null) return null
|
||||
|
||||
val presets = config.scope[caller]?.applySettingsPresets
|
||||
if (presets?.isEmpty() ?: true) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (presetName in presets) {
|
||||
val preset = SettingsPresets.instance.getPresetByName(presetName)
|
||||
val value = preset?.getSpoofedValue(name)
|
||||
if (value != null) return value
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
package icu.nullptr.hidemyapplist.xposed
|
||||
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import com.github.kyuubiran.ezxhelper.utils.findField
|
||||
import icu.nullptr.hidemyapplist.common.Constants
|
||||
import icu.nullptr.hidemyapplist.common.Utils
|
||||
|
||||
class Utils4Xposed {
|
||||
companion object {
|
||||
|
|
@ -12,5 +15,13 @@ class Utils4Xposed {
|
|||
}.get(packageSettings) as? String
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
fun getCallingApps(service: HMAService): Array<String> {
|
||||
val callingUid = Binder.getCallingUid()
|
||||
if (callingUid == Constants.UID_SYSTEM) return arrayOf()
|
||||
return Utils.binderLocalScope {
|
||||
service.pms.getPackagesForUid(callingUid)
|
||||
} ?: arrayOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,6 @@ class XposedEntry : IXposedHookZygoteInit, IXposedHookLoadPackage {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (lpparam.packageName == "com.android.providers.settings") {
|
||||
HMASettingsProviderService().handleLoadPackage(lpparam)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
package icu.nullptr.hidemyapplist.xposed.hook
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo
|
||||
import android.app.ActivityThread
|
||||
import com.github.kyuubiran.ezxhelper.utils.findMethod
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.AccessibilityPreset
|
||||
import icu.nullptr.hidemyapplist.xposed.HMAService
|
||||
import icu.nullptr.hidemyapplist.xposed.Utils4Xposed
|
||||
import icu.nullptr.hidemyapplist.xposed.logE
|
||||
import icu.nullptr.hidemyapplist.xposed.logI
|
||||
|
||||
// Big credits: https://github.com/Nitsuya/DoNotTryAccessibility/blob/main/app/src/main/java/io/github/nitsuya/donottryaccessibility/hook/AndroidFrameworkHooker.kt
|
||||
class AccessibilityHook(private val service: HMAService) : IFrameworkHook {
|
||||
companion object {
|
||||
private const val TAG = "AccessibilityHook"
|
||||
private const val ACCESSIBILITY_SERVICE = "com.android.server.accessibility.AccessibilityManagerService"
|
||||
}
|
||||
|
||||
private val hookList = mutableSetOf<XC_MethodHook.Unhook>()
|
||||
|
|
@ -20,24 +22,39 @@ class AccessibilityHook(private val service: HMAService) : IFrameworkHook {
|
|||
override fun load() {
|
||||
logI(TAG, "Load hook")
|
||||
|
||||
hookList += findMethod(
|
||||
"com.android.server.accessibility.AccessibilityManagerService"
|
||||
) {
|
||||
hookList += findMethod(ACCESSIBILITY_SERVICE) {
|
||||
name == "getInstalledAccessibilityServiceList"
|
||||
}.hookBefore { param -> hookedMethod(param) }
|
||||
|
||||
hookList += findMethod(
|
||||
"com.android.server.accessibility.AccessibilityManagerService"
|
||||
) {
|
||||
hookList += findMethod(ACCESSIBILITY_SERVICE) {
|
||||
name == "getEnabledAccessibilityServiceList"
|
||||
}.hookBefore { param -> hookedMethod(param) }
|
||||
|
||||
hookList += findMethod(ACCESSIBILITY_SERVICE) {
|
||||
name == "addClient"
|
||||
}.hookBefore { param ->
|
||||
val callingApps = Utils4Xposed.getCallingApps(service)
|
||||
if (callingApps.isEmpty()) return@hookBefore
|
||||
|
||||
for (caller in callingApps) {
|
||||
if (service.getEnabledSettingsPresets(caller).contains(AccessibilityPreset.NAME)) {
|
||||
param.result = 0L
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hookedMethod(param: XC_MethodHook.MethodHookParam) {
|
||||
try {
|
||||
val caller = ActivityThread.currentActivityThread().application.packageName
|
||||
if (service.getEnabledSettingsPresets(caller).contains(AccessibilityPreset.NAME)) {
|
||||
param.result = java.util.ArrayList<AccessibilityServiceInfo>()
|
||||
val callingApps = Utils4Xposed.getCallingApps(service)
|
||||
if (callingApps.isEmpty()) return
|
||||
|
||||
for (caller in callingApps) {
|
||||
if (service.getEnabledSettingsPresets(caller).contains(AccessibilityPreset.NAME)) {
|
||||
param.result = java.util.ArrayList<AccessibilityServiceInfo>()
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logE(TAG, "Fatal error occurred, ignore hooks", e)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
package icu.nullptr.hidemyapplist.xposed.hook
|
||||
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import com.github.kyuubiran.ezxhelper.utils.findMethod
|
||||
import com.github.kyuubiran.ezxhelper.utils.hookBefore
|
||||
import de.robv.android.xposed.XC_MethodHook
|
||||
import icu.nullptr.hidemyapplist.common.SettingsPresets
|
||||
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
|
||||
import icu.nullptr.hidemyapplist.xposed.HMAService
|
||||
import icu.nullptr.hidemyapplist.xposed.Utils4Xposed
|
||||
import icu.nullptr.hidemyapplist.xposed.logD
|
||||
|
||||
class ContentProviderHook(private val service: HMAService): IFrameworkHook {
|
||||
companion object {
|
||||
private const val TAG = "ContentProviderHook"
|
||||
}
|
||||
|
||||
private var hook: XC_MethodHook.Unhook? = null
|
||||
|
||||
// Credit: https://github.com/Nitsuya/DoNotTryAccessibility/blob/main/app/src/main/java/io/github/nitsuya/donottryaccessibility/hook/AndroidFrameworkHooker.kt
|
||||
override fun load() {
|
||||
hook = findMethod(
|
||||
"android.content.ContentProvider\$Transport"
|
||||
) {
|
||||
name == "call"
|
||||
}.hookBefore { param ->
|
||||
val callingApps = Utils4Xposed.getCallingApps(service)
|
||||
if (callingApps.isEmpty()) return@hookBefore
|
||||
|
||||
val method = param.args[2] as String?
|
||||
val name = param.args[3] as String?
|
||||
|
||||
for (caller in callingApps) {
|
||||
logD(TAG, "@call caller: $caller, method: $method, name: $name")
|
||||
|
||||
when (method) {
|
||||
"GET_global", "GET_secure", "GET_system" -> {
|
||||
val replacement = getSpoofedSetting(caller, name)
|
||||
if (replacement != null) {
|
||||
logD(TAG, "@getSettings returned replacement for $caller: ${replacement.value}")
|
||||
param.result = Bundle().apply {
|
||||
putString(Settings.NameValueTable.VALUE, replacement.value)
|
||||
putInt("_generation_index", -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpoofedSetting(caller: String?, name: String?): ReplacementItem? {
|
||||
if (caller == null || name == null) return null
|
||||
|
||||
val presets = service.getEnabledSettingsPresets(caller)
|
||||
if (presets.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (presetName in presets) {
|
||||
val preset = SettingsPresets.instance.getPresetByName(presetName)
|
||||
val value = preset?.getSpoofedValue(name)
|
||||
if (value != null) return value
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun unload() {
|
||||
hook?.unhook()
|
||||
hook = null
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue