在公司中做Framework的定制也已经两年多了,陆陆续续的也改了不少的系统服务,替客户制定过不少api接口。之前关于一些需要系统级权限的接口也都是通过将apk进行系统签名的方式来实现的,虽然这种做法也并无不妥,但是仔细考虑了一下应该还是有其他方式可以让apk与系统的耦合度变的更低的。(大不了就是在其他机器上调用不了这个接口了)
网上找了一下也早就已经有人对权限机制和安全机制做过一些分析,但是都不太涉及到具体的调用链,而且一般都是添加新的权限限制而不是绕开原有限制。
Android安全机制(2) Android Permission权限控制机制
好了,接下来就是一些系统Framework的接口调用追踪了,只想看解决方案的话可以直接拉到最后。
这里我们就挑一个具体的案例来分析吧,以蓝牙作为例子,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的限制等。