protected-broadcast 的一些细节

https://blog.csdn.net/u013553529/article/details/78409382


protected-broadcast 的一些细节

★ 1. 引言

注:本文中提及的“广播(Broadcast)”,“广播事件”和“Action”的意思大致相同。发送广播(sendBroadcast)也是发送一个指定的action给BroadcastReceiver。在本文中不严格区分“广播”和“Action”,除非有地方特别说明。

对于 android 系统应用来说,运用protected-broadcast是基本的安全要求。 
顾名思义,protected-broadcast是保护广播事件(Action)不被滥用的。相对的,如果一个action是不受protected-broadcast保护,并且使用此action的<receiver>组件(称之为MyReceiver1)没有system或者signature权限保护的话,这时任何app都可以发送此action的广播给MyReceiver1

对于 Android 系统应用来说,用protected-broadcast保护action不被滥用,可以不用声明system或者signature权限去保护receiver。当然,从安全的角度来说,给每一个组件设置适当的system或者signature权限是更好的。

你如果看过 Android 源码,一定在frameworks\base\core\res中的AndroidManifest.xml文件中看到过这样的代码:

    <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
    <protected-broadcast android:name="android.intent.action.SCREEN_ON" />
    <protected-broadcast android:name="android.intent.action.USER_PRESENT" />
    <protected-broadcast android:name="android.intent.action.TIME_SET" />
    <protected-broadcast android:name="android.intent.action.TIME_TICK" />
    <protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />
    <protected-broadcast android:name="android.intent.action.DATE_CHANGED" />
    <protected-broadcast android:name="android.intent.action.PRE_BOOT_COMPLETED" />
    <protected-broadcast android:name="android.intent.action.BOOT_COMPLETED" />
    <protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" />
    <protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" />
(略)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

本文就是针对protected-broadcast做一些细节方面的介绍,包括:

  • (1)protected-broadcast的适用范围:哪些应用可以使用这个特性?有系统签名的非系统app,能够使用这个特性吗?

  • (2)Android 系统是如何阻止第三方app发送被 protected-broadcast 保护的广播(Action)的?

  • (3)protected-broadcast 是对Activity(startActivity)、Receiver(sendBroadcast)、Service(startService)都有效吗?

  • (4)如何判断一个action是否是受保护的?

接下来一一说明。

★ 2. protected-broadcast的适用范围:哪些应用可以使用这个特性?有系统签名的非系统app,能够使用这个特性吗?

对于这个问题,我们可以从PackageParser.java入手,从PackageParser解析apk里的AndroidManifest.xml开始。

PackageParser.java 在 frameworks\base\core\java\android\content\pm目录中。

PackageParser.java中定义的常量TAG_PROTECTED_BROADCAST

private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
  • 1

PackageParser的内部类Package中的变量protectedBroadcasts

public ArrayList<String> protectedBroadcasts;
  • 1

解析 apk 中的 AndroidManifest.xml 的代码在 PackageParser.java 中的parseBaseApkCommon()

   private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
            IOException {
            //(略)
            } else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {
                // 解析<protected-broadcast>标签
                // 获取 AndroidManifestProtectedBroadcast 的属性值
                // 这里 res 是 Resources res, sa 是 TypedArray sa。
                sa = res.obtainAttributes(parser,
                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);

                // getNonResourceString()确保protected broadcast 的name是从xml中得到的,而不能是引用strings.xml中的字符串。
                // protected broadcast 的name在编译之后不能改变,除非重新编译。
                // AndroidManifestProtectedBroadcast_name是属性值的索引,其值为0。
                // name 为获取到的受保护的action名。
                String name = sa.getNonResourceString(
                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);

                sa.recycle();

                if (name != null && (flags & PARSE_IS_SYSTEM) != 0) {
                    // PARSE_IS_SYSTEM表明只在解析系统app时,protected broadcast才会有效
                    // pkg 是内部类 Package 的实例
                    if (pkg.protectedBroadcasts == null) {
                        pkg.protectedBroadcasts = new ArrayList<String>();
                    }
                    if (!pkg.protectedBroadcasts.contains(name)) {
                        // 所有的受保护的action都放到protectedBroadcasts
                        pkg.protectedBroadcasts.add(name.intern());
                    }
                }

                // 此标签<protected-broadcast>解析完毕,跳到END_TAG:'/>' 或者 '</protected-broadcast>'
                XmlUtils.skipCurrentTag(parser);
            }
            略
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

(flags & PARSE_IS_SYSTEM) != 0这个判断可知,可以使用<protected-broadcast>标签的app只能是系统app如果不是系统app,那么此标签将被忽略

哪些app是系统app呢? 
有两类: 
* 一类是uid为 android.uid.systemandroid.uid.phoneandroid.uid.logandroid.uid.nfcandroid.uid.bluetoothandroid.uid.shell的app是系统app。

  • 另一类是指定目录中的app是系统app,“指定目录”包括/vendor/overlay/system/framework/system/priv-app/system/app/vendor/app/oem/app

解析这两类apk的时候,用到了PARSE_IS_SYSTEM标志位。

关于系统app的更多细节,可以参考 《Android 权限的一些细节》 。

替换系统app(replace)或者升级系统app(upgrade)时,也用到了PARSE_IS_SYSTEM标志位,如果系统app在新版本中增加了<protected-broadcast>也会被处理。

如果安装一个跟系统签名一样的app,不是替换系统app,也不是升级系统app的话,就不会用到PARSE_IS_SYSTEM标志来解析apk,所以在这种情况下,有系统签名但不是系统app,不能使用<protected-broadcast>特性

★ 3. Android 系统是如何阻止第三方app发送protected-broadcast保护的广播(Action)的?

分两个部分:

  • 第一部分:受保护的广播是如何保存到PackageManagerService(简称为PMS)的,即protected-broadcast从系统app到PMS。

  • 第二部分:什么时候检查广播(action)是不是受保护的。

♦ 3.1 受保护的广播是如何保存到PMS的

PackageManagerService.java

Android 7.1.1中大致的流程如下:

-> PackageManagerService()构造方法
-> scanDirTracedLI()
-> scanDirLI(File dir, ...) {// 为了与Android 8.0代码做对比,这里列出一些细节
    final File[] files = dir.listFiles();
    for (File file : files) {
        scanPackageTracedLI(); // 这里是串行处理,目录dir中的apk是一个接一个解析的
    }
}
-> scanPackageTracedLI((File scanFile, ...)
-> scanPackageLI(File scanFile, ...)
    -> pp.parsePackage(scanFile, parseFlags);
-> scanPackageLI(PackageParser.Package pkg, File scanFile, ...)
-> scanPackageInternalLI()
-> scanPackageLI(PackageParser.Package pkg, final int policyFlags, ...)
-> scanPackageDirtyLI() {
   if (pkg.protectedBroadcasts != null) {
        N = pkg.protectedBroadcasts.size();
        for (i=0; i<N; i++) {
            mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Android 8.0中大致的流程如下:

-> PackageManagerService()构造方法
-> scanDirTracedLI()
-> scanDirLI(File dir, ...) {// 这里采用了并行处理,7.1.1中是串行处理的
    final File[] files = dir.listFiles();
    ParallelPackageParser parallelPackageParser = new ParallelPackageParser()
    for (File file : files) {
        // 将解析apk的任务交给多个线程来处理
        parallelPackageParser.submit(file, parseFlags);
        fileCount++;
    }
    for (; fileCount > 0; fileCount--) {
        // take()取出解析apk的结果,如果没有解析完,则等待
        // 哪个apk先解析完,就先处理哪个apk(scanPackageLI())
        ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
        Throwable throwable = parseResult.throwable;
        if (throwable == null) {// 解析apk无异常
            scanPackageLI(parseResult.pkg, parseResult.scanFile, ...);
        }
    }
    parallelPackageParser.close();
}
-> scanPackageLI(PackageParser.Package pkg, File scanFile, ...)
-> scanPackageInternalLI()
-> scanPackageLI(File scanFile, int parseFlags, ...)
    -> parsePackage() // 这又解析了一次?不知道为什么,没有用之前解析好的(useCaches为false)
-> scanPackageLI(PackageParser.Package pkg, final int policyFlags, ...)
-> scanPackageDirtyLI()
-> commitPackageSettings() {
    if (pkg.protectedBroadcasts != null) {
        N = pkg.protectedBroadcasts.size();
        for (i=0; i<N; i++) {
            mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

<protected-broadcast>保存到PMS后,PMS提供了一个接口isProtectedBroadcast()供其他应用调用,目前只是AMS在调用。

从下面的代码中可以看到,mProtectedBroadcasts中的action是受保护的,除此之外,某些action名字是受保护的或者说是被禁止的,第三方app不能乱发,例如,以android.net.netmon.lingerExpired开头的action。

    @Override
    public boolean isProtectedBroadcast(String actionName) {
        synchronized (mPackages) {
            if (mProtectedBroadcasts.contains(actionName)) {
                return true;
            } else if (actionName != null) {
                if (actionName.startsWith("android.net.netmon.lingerExpired")
                        || actionName.startsWith("com.android.server.sip.SipWakeupTimer")
                        || actionName.startsWith("com.android.internal.telephony.data-reconnect")
                        || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
                    return true;
                }
            }
        }
        return false;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

调用PMS的isProtectedBroadcast()是通过IPackageManager接口。

IPackageManager.aidl

    boolean isProtectedBroadcast(String actionName);
  • 1
  • 2
  • 3

♦ 3.2 检查广播(action)是不是受保护的

这是在 ActivityManagerService (简称AMS)中执行的,最终调用的是PMS的接口isProtectedBroadcast()

ActivityManagerService.java 
大致的调用过程如下:

-> App中调用sendBroadcast()
-> sendBroadcast() @ ContextImpl
-> broadcastIntent() @ ActivityManagerService
-> broadcastIntentLocked() {
    final String action = intent.getAction();
    isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
    if (!isCallerSystem) {// 如果调用者不是系统app,即调用sendBroadcast()的不是系统app
        if (isProtectedBroadcast) {//action是被保护的广播,那么将抛出异常
            String msg = "Permission Denial: not allowed to send broadcast "
                    + action + " from pid="
                    + callingPid + ", uid=" + callingUid;
            throw new SecurityException(msg);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

由上面可知,如果调用sendBroadcast()的不是系统app,并且广播action是受保护的,那么将抛出SecurityException异常。

★ 4. protected-broadcast是对Activity(startActivity)、Receiver(sendBroadcast)、Service(startService)都有效吗?

从上面的分析来看,不是都有效。 
而且从名字protected-broadcast直观的来看(猜),也是只保护广播action的,不保护 startActivity 和 startService 的action。 
都是action,待遇怎么差那么多呢?

★ 5. 如何判断一个action是否是受保护的?

如果对apk进行渗透测试,经常会遇到Receiver组件暴露的问题,但是Receiver接收的action是否受保护呢?这里提供一种方法来判断action是不是受保护的。代码如下:

package com.galian.mylib;

import android.content.pm.IPackageManager;
import android.os.RemoteException;
import android.util.Log;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Utils {

    private static final String TAG = "Utils";
    public static boolean isProtectedBroatcast(String action) {
        Class<?> cls = null;
        Method method = null;
        boolean ret = false;

        try {
            cls = Class.forName("android.app.ActivityThread");
            method = cls.getMethod("getPackageManager", null);
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            IPackageManager iPackageManager = (IPackageManager) method.invoke(cls, null);
            ret = iPackageManager.isProtectedBroadcast(action);
            Log.d(TAG, "action: " + action + " is " + (ret?"protected.":" not protected."));
        } catch ...
        //(catch 代码块略)
        return ret;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

要想使上面的代码编译通过,还需要framework生成的jar包framework-classes-full-debug.jar。 
在 build.gradle中引用 framework-classes-full-debug.jar 包:

dependencies {
    //compile fileTree(dir: 'libs', include: ['*.jar']) // 一定要注释掉,否则会发生method数超过65536的问题
    provided files('libs/framework-classes-full-debug.jar')
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5

我用Android 8.0 代码编译生成的 framework-classes-full-debug.jar,放到了github,地址为:https://github.com/galian123/Samples/blob/master/mylib/libs/framework-classes-full-debug.jar。 
整个工程也传到了github,地址:https://github.com/galian123/Samples


猜你喜欢

转载自blog.csdn.net/thinkinwm/article/details/80725402
今日推荐