DroidGuard: Allow blocking of dump() for services when missing permission

Also request permission for the future
This commit is contained in:
Marvin W 2026-07-11 15:30:27 +02:00
parent 5560a94031
commit 90d4d8e9c9
No known key found for this signature in database
GPG key ID: 072E9235DB996F2A
3 changed files with 97 additions and 0 deletions

View file

@ -7,6 +7,8 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.DUMP"
tools:ignore="ProtectedPermissions" />
<application>
<service

View file

@ -23,6 +23,7 @@ import org.microg.gms.droidguard.PingData;
import org.microg.gms.droidguard.Request;
import org.microg.gms.droidguard.core.HardwareAttestationBlockingProvider;
import org.microg.gms.droidguard.core.SerialUnflaky;
import org.microg.gms.droidguard.core.ServiceCallProxy;
import java.util.Collections;
import java.util.concurrent.Executor;
@ -117,6 +118,8 @@ public class DroidGuardChimeraService extends TracingIntentService {
if (intent != null && intent.getAction() != null && intent.getAction().equals("com.google.android.gms.droidguard.service.START")) {
HardwareAttestationBlockingProvider.ensureEnabled(DroidGuardPreferences.isHardwareAttestationBlocked(this));
SerialUnflaky.INSTANCE.fetch();
ServiceCallProxy.INSTANCE.maySetBlockDumpForService(this, "SurfaceFlinger");
ServiceCallProxy.INSTANCE.maySetBlockDumpForService(this, "thermalservice");
return new DroidGuardServiceBroker(this);
}
return null;
@ -132,6 +135,8 @@ public class DroidGuardChimeraService extends TracingIntentService {
this.d = new ThreadPoolExecutor(1, 1, 0, TimeUnit.NANOSECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());
HardwareAttestationBlockingProvider.ensureEnabled(DroidGuardPreferences.isHardwareAttestationBlocked(this));
SerialUnflaky.INSTANCE.fetch();
ServiceCallProxy.INSTANCE.maySetBlockDumpForService(this, "SurfaceFlinger");
ServiceCallProxy.INSTANCE.maySetBlockDumpForService(this, "thermalservice");
super.onCreate();
}

View file

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2026 microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.droidguard.core
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.os.IBinder
import android.util.Log
import androidx.core.content.ContextCompat
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Proxy
private const val TAG = "DroidGuard"
/**
* Helper to block the call to dump() on services
*
* We may want to extend this later to allow for arbitrary service call interceptions
*/
@SuppressLint("PrivateApi")
object ServiceCallProxy {
private var serviceManagerClass: Class<*>? = null
private var getServiceMethod: Method? = null
private var activeServices: MutableMap<String, IBinder?>? = null
private val proxyEnabled: MutableMap<String, Boolean> = hashMapOf()
private val originalServices: MutableMap<String, IBinder> = hashMapOf()
init {
try {
serviceManagerClass = Class.forName("android.os.ServiceManager")
getServiceMethod =
serviceManagerClass!!.getDeclaredMethod("getService", String::class.java)
getServiceMethod!!.isAccessible = true
val cacheField: Field = serviceManagerClass!!.getDeclaredField("sCache")
cacheField.isAccessible = true
@Suppress("UNCHECKED_CAST")
activeServices = cacheField.get(null) as MutableMap<String, IBinder?>?
} catch (e: Exception) {
Log.w(TAG, "Error configuring DumpBlockingProxy", e)
}
}
fun maySetBlockDumpForService(context: Context, systemServiceName: String) {
val permissionResult =
ContextCompat.checkSelfPermission(context, android.Manifest.permission.DUMP)
if (permissionResult == PackageManager.PERMISSION_DENIED) {
setBlockDumpForService(systemServiceName, true)
} else if (permissionResult == PackageManager.PERMISSION_GRANTED) {
setBlockDumpForService(systemServiceName, false)
}
}
fun setBlockDumpForService(systemServiceName: String, enabled: Boolean) {
if (proxyEnabled.containsKey(systemServiceName) && proxyEnabled[systemServiceName] == enabled) return
if (getServiceMethod == null || activeServices == null) return
Log.d(TAG, "Configuring blocker for dump() on service $systemServiceName")
if (enabled) {
val originalService = try {
getServiceMethod!!.invoke(null, systemServiceName) as IBinder
} catch (e: Exception) {
Log.w(TAG, e)
return
}
val newService = Proxy.newProxyInstance(
IBinder::class.java.getClassLoader(),
arrayOf<Class<*>>(IBinder::class.java),
{ _, method, args ->
when (method.name) {
"dump" -> {
Log.d(TAG, "Blocking dump() call on $systemServiceName");
null
}
else -> method.invoke(originalService, args)
}
}) as IBinder
originalServices[systemServiceName] = originalService
activeServices!![systemServiceName] = newService
proxyEnabled[systemServiceName] = true
} else if (proxyEnabled[systemServiceName] == true) {
activeServices!![systemServiceName] = originalServices[systemServiceName]
proxyEnabled[systemServiceName] = false
}
}
}