[Xposed][For Devs] How to dynamically declare permissions for a target app without altering its manifest and changing its signature

Search This thread

Mino260806

Member
Oct 10, 2020
44
22
Huawei Y5 (2017)
Redmi Note 10S
Have you ever tried to extend your favorite app with new features using Xposed, but were shocked halfway that your hooked app doesn't declare a permission in AndroidManifest ? And then you spent infinite hours on the internet trying to solve this frustrating problem, you decided to use services and an external intent, but you found out that it was not convenient, and finally you gave up...

So you are like me, who wasted hours looking for a solution, until I figured out how to do it myself. Here's a snippet to save time for future Xposed enthusiasts. Put this code snippet in handleLoadPackage
Java:
// Hook will only patch System Framework
if (!lpparam.packageName.equals("android")) return;
String targetPkgName = "com.example.app"; // Replace this with the target app package name
String[] newPermissions = new String[] { // Put the new permissions here
    "android.permission.INTERNET",
    "android.permission.ACCESS_NETWORK_STATE"
    };
String grantPermissionsMethod = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    grantPermissionsMethod = "restorePermissionState";
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
        XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS NEWER THAN ANDROID 12");
    }
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
    grantPermissionsMethod = "grantPermissions";
}
else {
    grantPermissionsMethod = "grantPermissionsLPw";
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)  {
        XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS PRIOR TO JELLYBEAN");
    }
}
XposedBridge.hookAllMethods(XposedHelpers.findClass("com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader),
                            grantPermissionsMethod, new XC_MethodHook() {
                                @Override
                                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                                    // on Android R and above, param.args[0] is an instance of android.content.pm.parsing.ParsingPackageImpl
                                    // on Android Q and older, param.args[0] is an instance of android.content.pm.PackageParser$Package
                                    // However, they both declare the same fields we need, so no need to check for class type
                                    String pkgName = (String) XposedHelpers.getObjectField(param.args[0], "packageName");
                                    XposedBridge.log("Package  " + pkgName + " is requesting permissions");
                                    if (pkgName.equals(targetPkgName)) {
                                        List<String> permissions = (List<String>) XposedHelpers.getObjectField(param.args[0], "requestedPermissions");
                                        for (String newPermission: newPermissions) {
                                            if (!permissions.contains(newPermission)) {
                                                permissions.add(newPermission);
                                                XposedBridge.log("Added " + newPermission + " permission to " + pkgName);
                                            }
                                        }
                                    }
                                }
                            });

Notes:
  • You must check System Framework in LSposed Manager
  • A reboot is required after adding the target permissions
  • You still need to prompt the user to accept sensitive permissions (ie android.permission.READ_CONTACTS), even if you have added them using this method
 

serajr

Recognized Developer / Recognized Themer
Apr 21, 2011
5,060
18,678
São Paulo - SP
Wow, thx. Great for the install permissions!
I wrote a class to grant install and runtime/sensitive permissions (without prompting users).

Android 12 implementation:
Java:
public class Grant_Package_Permissions {
   
    public static void hook(LoadPackageParam lpparam) {
       
        try {
           
            Class<?> PermissionManagerService = XposedHelpers.findClass(
                    "com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader);
            Class<?> AndroidPackage = XposedHelpers.findClass(
                    "com.android.server.pm.parsing.pkg.AndroidPackage", lpparam.classLoader);
            Class<?> PermissionCallback = XposedHelpers.findClass(
                    "com.android.server.pm.permission.PermissionManagerService$PermissionCallback", lpparam.classLoader);

            // PermissionManagerService - restorePermissionState
            XposedHelpers.findAndHookMethod(PermissionManagerService, "restorePermissionState",
                    AndroidPackage, boolean.class, String.class, PermissionCallback, int.class, new XC_MethodHook() {

                @SuppressWarnings("unchecked")
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {

                    // params
                    Object pkg = param.args[0];
                    int filterUserId = (int) param.args[4];

                    // obtém os campos
                    Object mState = XposedHelpers.getObjectField(param.thisObject, "mState");
                    Object mRegistry = XposedHelpers.getObjectField(param.thisObject, "mRegistry");
                    Object mPackageManagerInt = XposedHelpers.getObjectField(param.thisObject, "mPackageManagerInt");

                    // Continua ?
                    String packageName = (String) XposedHelpers.callMethod(pkg, "getPackageName");
                    Object ps = XposedHelpers.callMethod(mPackageManagerInt, "getPackageSetting", packageName);
                    if (ps == null)
                        return;

                    int[] getAllUserIds = (int[]) XposedHelpers.callMethod(param.thisObject, "getAllUserIds");
                    int userHandle_USER_ALL = XposedHelpers.getStaticIntField(Class.forName("android.os.UserHandle"), "USER_ALL");
                    final int[] userIds = filterUserId == userHandle_USER_ALL ? getAllUserIds : new int[]{filterUserId};

                    for (int userId : userIds) {

                        List<String> requestedPermissions;
                        Object userState = XposedHelpers.callMethod(mState, "getOrCreateUserState", userId);
                        int appId = (int) XposedHelpers.callMethod(ps, "getAppId");
                        Object uidState = XposedHelpers.callMethod(userState, "getOrCreateUidState", appId);

                        // package 1
                        if (packageName.equals("PACKAGE_1")) {

                            requestedPermissions = (List<String>) XposedHelpers.callMethod(pkg, "getRequestedPermissions");
                            grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
                                    Manifest.permission.RECORD_AUDIO);
                            grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
                                    Manifest.permission.MODIFY_AUDIO_SETTINGS);

                        }
                       
                        // package 2
                        if (packageName.equals("PACKAGE_2")) {

                            requestedPermissions = (List<String>) XposedHelpers.callMethod(pkg, "getRequestedPermissions");
                            grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
                                    Manifest.permission.READ_CONTACTS);

                        }
                    }
                }
            });

        } catch (Exception e) {
           
            XposedBridge.log(e);
           
        }
    }

    private static void grantInstallOrRuntimePermission(List<String> requestedPermissions, Object uidState,
                                        Object registry, String permission) {

        if (!requestedPermissions.contains(permission))
            XposedHelpers.callMethod(uidState, "grantPermission",
                    XposedHelpers.callMethod(registry, "getPermission", permission));

    }
}
 

Top Liked Posts

  • There are no posts matching your filters.
  • 2
    Have you ever tried to extend your favorite app with new features using Xposed, but were shocked halfway that your hooked app doesn't declare a permission in AndroidManifest ? And then you spent infinite hours on the internet trying to solve this frustrating problem, you decided to use services and an external intent, but you found out that it was not convenient, and finally you gave up...

    So you are like me, who wasted hours looking for a solution, until I figured out how to do it myself. Here's a snippet to save time for future Xposed enthusiasts. Put this code snippet in handleLoadPackage
    Java:
    // Hook will only patch System Framework
    if (!lpparam.packageName.equals("android")) return;
    String targetPkgName = "com.example.app"; // Replace this with the target app package name
    String[] newPermissions = new String[] { // Put the new permissions here
        "android.permission.INTERNET",
        "android.permission.ACCESS_NETWORK_STATE"
        };
    String grantPermissionsMethod = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        grantPermissionsMethod = "restorePermissionState";
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
            XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS NEWER THAN ANDROID 12");
        }
    } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
        grantPermissionsMethod = "grantPermissions";
    }
    else {
        grantPermissionsMethod = "grantPermissionsLPw";
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)  {
            XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS PRIOR TO JELLYBEAN");
        }
    }
    XposedBridge.hookAllMethods(XposedHelpers.findClass("com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader),
                                grantPermissionsMethod, new XC_MethodHook() {
                                    @Override
                                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                                        // on Android R and above, param.args[0] is an instance of android.content.pm.parsing.ParsingPackageImpl
                                        // on Android Q and older, param.args[0] is an instance of android.content.pm.PackageParser$Package
                                        // However, they both declare the same fields we need, so no need to check for class type
                                        String pkgName = (String) XposedHelpers.getObjectField(param.args[0], "packageName");
                                        XposedBridge.log("Package  " + pkgName + " is requesting permissions");
                                        if (pkgName.equals(targetPkgName)) {
                                            List<String> permissions = (List<String>) XposedHelpers.getObjectField(param.args[0], "requestedPermissions");
                                            for (String newPermission: newPermissions) {
                                                if (!permissions.contains(newPermission)) {
                                                    permissions.add(newPermission);
                                                    XposedBridge.log("Added " + newPermission + " permission to " + pkgName);
                                                }
                                            }
                                        }
                                    }
                                });

    Notes:
    • You must check System Framework in LSposed Manager
    • A reboot is required after adding the target permissions
    • You still need to prompt the user to accept sensitive permissions (ie android.permission.READ_CONTACTS), even if you have added them using this method