[Android Framework]客户定制接口如何绕开系统权限管控

前言:

在公司中做Framework的定制也已经两年多了,陆陆续续的也改了不少的系统服务,替客户制定过不少api接口。之前关于一些需要系统级权限的接口也都是通过将apk进行系统签名的方式来实现的,虽然这种做法也并无不妥,但是仔细考虑了一下应该还是有其他方式可以让apk与系统的耦合度变的更低的。(大不了就是在其他机器上调用不了这个接口了)


网上找了一下也早就已经有人对权限机制和安全机制做过一些分析,但是都不太涉及到具体的调用链,而且一般都是添加新的权限限制而不是绕开原有限制。

浅析Android权限机制(一) —— Android的权限机制

Android安全机制(2) Android Permission权限控制机制


好了,接下来就是一些系统Framework的接口调用追踪了,只想看解决方案的话可以直接拉到最后。

调用流程(MT8735_M平台):

这里我们就挑一个具体的案例来分析吧,以蓝牙作为例子,apk如果需要管理蓝牙连接,就必须声明蓝牙相关的permission,那么这个permission的检查又是在哪里进行的呢。

BluetoothManagerService.java

    public boolean enable() {
        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
            (!checkIfCallerIsForegroundUser())) {
            Log.w(TAG,"enable(): not allowed for non-active and non system user");
            return false;
        }

        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                "Need BLUETOOTH ADMIN permission");
        if (DBG) {
            Log.d(TAG,"enable():  mBluetooth =" + mBluetooth +
                    " mBinding = " + mBinding);
        }

从这个enable函数可以看到,在执行enable函数开启蓝牙时,会先检查是否是系统进程,如果不是系统进程的话就需要在前台运行。

然后就通过Context(aondroid.content.Context)类的enforceCallingOrSelfPermission函数来检查相应的权限,这里的权限是一个常量,具体的定义是

private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;

现在我们转到Context.java这个类里去看看:

    /**
     * Determine whether the calling process of an IPC or you have been
     * granted a particular permission.  This is the same as
     * {@link #checkCallingPermission}, except it grants your own permissions
     * if you are not currently processing an IPC.  Use with care!
     *
     * @param permission The name of the permission being checked.
     *
     * @return {@link PackageManager#PERMISSION_GRANTED} if the calling
     * pid/uid is allowed that permission, or
     * {@link PackageManager#PERMISSION_DENIED} if it is not.
     *
     * @see PackageManager#checkPermission(String, String)
     * @see #checkPermission
     * @see #checkCallingPermission
     */
    @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)")
    @PackageManager.PermissionResult
    public abstract int checkCallingOrSelfPermission(@NonNull String permission);

这里的注释已经写的很详细了(谷歌aosp里的源码注释还是挺多的,很多比较难懂的地方都会有详细的注释),简单解释一下就是检查你的应用有没有声明这个权限,或者应用运行的uid(通过AndroidManifest.xml里的sharedUserId定义)是否有相应的权限。

这是一个抽象方法,那么一定会有一个实现这个方法的类,一般来说aosp里的命名都比较规范,这种实现一般都是xxxImpl.java或者xxxService.java之类的,不过一般我会直接用git grep去确认。这里的具体实现是在ContextImpl.java(framework/base/core/java/android/app/ContextImpl.java)中:

    @Override
    public int checkCallingOrSelfPermission(String permission) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        return checkPermission(permission, Binder.getCallingPid(),
                Binder.getCallingUid());
    }

可以看到这里是调用了checkPermission这个函数,如果把代码稍微浏览一遍的话会发现,context中检查权限的代码,包括checkxxxPermission或者enforcexxxPermission最后都是调到了这个 checkPermission的函数。

这里也贴一下enforce的调用吧,帮大家省点找代码的时间:

    private void enforce(
            String permission, int resultOfCheck,
            boolean selfToo, int uid, String message) {
        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException(
                    (message != null ? (message + ": ") : "") +
                    (selfToo
                     ? "Neither user " + uid + " nor current process has "
                     : "uid " + uid + " does not have ") +
                    permission +
                    ".");
        }
    }

    @Override
    public void enforcePermission(
            String permission, int pid, int uid, String message) {
        enforce(permission,
                checkPermission(permission, pid, uid),
                false,
                uid,
                message);
    }

可以看到这里最后是否抛SecurityException完全取决于checkPermission这个函数的返回值,我们来看看这个函数是怎么写的:

    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        try {
            return ActivityManagerNative.getDefault().checkPermission(
                    permission, pid, uid);
        } catch (RemoteException e) {
            return PackageManager.PERMISSION_DENIED;
        }
    }

注意这里有个try catch的block块,catch的这个RemoteException一般是用在ipc通信中的,所以说这里已经涉及到了跨进程的调用!

先看看ActivityManagerNative.java(framework/base/core/java/android/app/ActivityManagerNative)里的geetDefault函数,这是一个static函数:

    /**
     * Retrieve the system's default/global activity manager.
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

sDefault是一个单例的对象,用来获取系统的ams(ActivityManagerService)

    private static final Singleton gDefault = new Singleton() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

看到这里也就不难看出,这是通过了aidl的方式获取到了系统定义的IActivityManager接口,这个接口的实现则是ActivityManagerService(frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java),至于是怎么调用到ams里的这里就不详细写了,大致的流程应该可以百(gu)度(ge)得到,如果没人写过详细流程的话后面会开一篇新的blog来写一下。

    /**
     * As the only public entry point for permissions checking, this method
     * can enforce the semantic that requesting a check on a null global
     * permission is automatically denied.  (Internally a null permission
     * string is used when calling {@link #checkComponentPermission} in cases
     * when only uid-based security is needed.)
     *
     * This can be called with or without the global lock held.
     */
    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            /// M: Add log for debugging AMS permissions @{
            if (DEBUG_PERMISSION) {
                Slog.d(TAG, "checkPermission: permission == null, " + pid + ", " + uid);
            }
            /// M: Add log for debugging AMS permissions @}
            return PackageManager.PERMISSION_DENIED;
        }
        return checkComponentPermission(permission, pid, uid, -1, true);
    }

从注释可以看到,这是提供给外部的唯一检查方法,如果检查的权限为空的话直接就返回deny了,然后就跳转到了checkComponentPermission这个函数:

    /**
     * This can be called with or without the global lock held.
     */
    int checkComponentPermission(String permission, int pid, int uid,
            int owningUid, boolean exported) {
        if (pid == MY_PID) {
            /// M: Add log for debugging AMS permissions @{
            if (DEBUG_PERMISSION) {
                Slog.d(TAG, "checkComponentPermission: " + permission + ", " + pid + ", " + uid +
                    ", "  + owningUid + ", " + exported);
            }
            /// M: Add log for debugging AMS permissions @}
            return PackageManager.PERMISSION_GRANTED;
        }

        /// M: Add log for debugging AMS permissions @{
        if (DEBUG_PERMISSION) {
            Slog.d(TAG, "checkComponentPermission: " + permission + ", " + pid + ", " + uid +
                ", "  + owningUid + ", " + exported + ", " + UserHandle.getAppId(uid) + ", " +
                UserHandle.isIsolated(uid) + ", " + UserHandle.isSameApp(uid, owningUid));
        }
        /// M: Add log for debugging AMS permissions @}

        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported);
    }

这里先检查了一下pid是不是自己,如果是的话就授予权限,不是自己的话就调用ActivityManager去获取组件的权限:

ActivityManager.java(framework/base/core/java/android/app/ActivityManager.java)

    /** @hide */
    public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        // Root, system server get to do everything.
        final int appId = UserHandle.getAppId(uid);
        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // Isolated processes don't get any permissions.
        if (UserHandle.isIsolated(uid)) {
            return PackageManager.PERMISSION_DENIED;
        }
        // If there is a uid that owns whatever is being accessed, it has
        // blanket access to it regardless of the permissions it requires.
        if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If the target is not exported, then nobody else can get to it.
        if (!exported) {
            /*
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
                    here);
            */
            return PackageManager.PERMISSION_DENIED;
        }
        if (permission == null) {
            return PackageManager.PERMISSION_GRANTED;
        }
        try {
	    return AppGlobals.getPackageManager()
		    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            // Should never happen, but if it does... deny!
            Slog.e(TAG, "PackageManager is dead?!?", e);
        }
        return PackageManager.PERMISSION_DENIED;
    }

可以看到前面有一堆关于uid的判断,然后最后通过AppGlobals.getPackageManager()拿到了pms对权限进行判断。

AppGlobals.java(framework/base/core/java/android/app/AppGlobals.java)

    /**
     * Return the raw interface to the package manager.
     * @return The package manager.
     */
    public static IPackageManager getPackageManager() {
        return ActivityThread.getPackageManager();
    }

ActivityThread.java(framework/base/core/java/android/app/ActivityThread.java)

    public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
    }

这里进行了第二次跨进程调用,通过pms的checkUidPermission函数来确认权限,最终就是在这个地方来实现权限规避的,实现的原理是在判断是否拥有权限时,同时检查自定义的权限,如果有授予自定义权限就返回granted

    @Override
    public int checkUidPermission(String permName, int uid) {
		Log.d(TAG, "checkUidPermission, permission=" + permName + " uid=" + uid);// add by linqihan, for log
        final int userId = UserHandle.getUserId(uid);

        if (!sUserManager.exists(userId)) {
            return PackageManager.PERMISSION_DENIED;
        }

        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                final SettingBase ps = (SettingBase) obj;
                final PermissionsState permissionsState = ps.getPermissionsState();
                if (permissionsState.hasPermission(permName, userId)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
                // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
                if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
                        .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
				// add by linqihan, for api
				if (permissionsState.hasPermission("android.permission.TEST_API", userId)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
				// end linqihan
            } else {
                ArraySet perms = mSystemPermissions.get(uid);
                if (perms != null) {
                    if (perms.contains(permName)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                    if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
                            .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
					// add by linqihan, for api
					if (perms.contains("android.permission.TEST_API")) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
					// end linqihan
                }
            }
        }

        return PackageManager.PERMISSION_DENIED;
    }

解决方法:

1. 系统AndroidManifest.xml中声明自定义权限,表明系统中有这项权限(framework/base/core/res/AndroidManifest.xml)

2. platform.xml中声明该权限(framework/base/data/etc/platform.xml)

3. 修改pms中的checkUidPermission函数,添加相应的判断(上文最后一步调用)

4. 应用申请该权限,由于自定义的权限不在runtime permission的列表中,因此只要申请就会授予


这样就把Context中的enforcePermission检查规避了,但是系统中仍然还有其他权限限制,比如selinux,AppOpsService的限制等。

你可能感兴趣的:(Android,Framework)