zoukankan      html  css  js  c++  java
  • Protect Broadcast 保护广播

    一、android:sharedUserId="android.uid.system"

    1. 系统中所有使用android.uid.system作为共享UID的APK,都会首先在manifest节点中增加 android:sharedUserId="android.uid.system",然后在Android.mk中增加 LOCAL_CERTIFICATE := platform.如ZsDeskclock,:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.journeyui.deskclock"
        android:sharedUserId="android.uid.system"
        android:versionCode="410"
        android:versionName="4.1.0">
        ...
    </manifest>
    

    下面所说的系统应用也是指这些shareUID是system的应用。   

          2. 在开机PMS初始化的时候,将该name为“android.uid.system”的uid归为1000的system用户ID;

    PackageManagerService.java
    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
                ...
                mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                   ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
                ...
    }
    
    Process.java
    /**
    * Defines the UID/GID under which system code runs.
    */
    public static final int SYSTEM_UID = 1000;
    

    二、AMS对系统应用发出的广播进行安全检查

    当应用或者组件发送广播时,在广播发送的必经之路上,AMS对系统应用发送的广播进行了检查:

    1. 判断是否是系统uid,

    ActivityServiceManager.java
    final int broadcastIntentLocked(){
    final boolean isCallerSystem;
    switch (UserHandle.getAppId(callingUid)) {
        case Process.ROOT_UID:
        case Process.SYSTEM_UID:
        case Process.PHONE_UID:
        case Process.BLUETOOTH_UID:
        case Process.NFC_UID:
            isCallerSystem = true;
     break;
     default:
            isCallerSystem = (callerApp != null) && callerApp.persistent;
     break;
    }
    }
    

    2. 如果是SYSTEM_UID,便对该广播进行安全检查:

    if (isCallerSystem) {
           checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                            isProtectedBroadcast, receivers);
    }
    

    3. 在checkBroadcastFromSystem()函数中进行安全检查:

    private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
                String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
            final String action = intent.getAction();
            if (isProtectedBroadcast  //A
                    || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
                    || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
                    || Intent.ACTION_MEDIA_BUTTON.equals(action)
                    || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
                    || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
                    || Intent.ACTION_MASTER_CLEAR.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
                    || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
                    || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
                    || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)) {
                // Broadcast is either protected, or it's a public action that
                // we've relaxed, so it's fine for system internals to send.
                return;
            }
    
            // This broadcast may be a problem... but there are often system components that
            // want to send an internal broadcast to themselves, which is annoying to have to
            // explicitly list each action as a protected broadcast, so we will check for that
            // one safe case and allow it: an explicit broadcast, only being received by something
            // that has protected itself.
            if (receivers != null && receivers.size() > 0
                    && (intent.getPackage() != null || intent.getComponent() != null)) {
                boolean allProtected = true;
                for (int i = receivers.size()-1; i >= 0; i--) {
                    Object target = receivers.get(i);
                    if (target instanceof ResolveInfo) {
                        ResolveInfo ri = (ResolveInfo)target;
                        if (ri.activityInfo.exported && ri.activityInfo.permission == null) {
                            allProtected = false;
                            break;
                        }
                    } else {
                        BroadcastFilter bf = (BroadcastFilter)target;
                        if (bf.requiredPermission == null) {
                            allProtected = false;
                            break;
                        }
                    }
                }
                if (allProtected) {
                    // All safe!
                    return;
                }
            }
            //C
            // The vast majority of broadcasts sent from system internals
            // should be protected to avoid security holes, so yell loudly
            // to ensure we examine these cases.
            if (callerApp != null) {
                Log.wtf(TAG, "Sending non-protected broadcast " + action
                                + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
                        new Throwable());
            } else {
                Log.wtf(TAG, "Sending non-protected broadcast " + action
                                + " from system uid " + UserHandle.formatUid(callingUid)
                                + " pkg " + callerPackage,
                        new Throwable());
            }
        }
    

    1. framework中声明的保护广播和一些放开的公共广播:

    isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
    

    isProtectedBroadcast为true则代表该广播在Framework/base/core/res/AndroidManifest.xml中有声明为保护广播,这样的广播只能由系统发出。如果是phone进程的,一般在Teleservice下的AndroidManifest.xml中声明保护广播。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="android" coreApp="true" android:sharedUserId="android.uid.system"
        android:sharedUserLabel="@string/android_system_label">
        <!-- ================================================ -->
        <!-- Special broadcasts that only the system can send -->
        <!-- ================================================ -->
        <eat-comment />
    
        <protected-broadcast android:name="android.net.tether.CONNECTEDSTA_CHANGE" />
        <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.PRE_BOOT_COMPLETED" />
    
        ...
    </manifest>
    

    另外一些公共的action,虽然没有保护但是系统允许在系统内部发送;如Intent.ACTION_CLOSE_SYSTEM_DIALOGS,Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS等;

    2. 未在Framework中声明保护广播或者非放开的公共广播

    B处的大意是经常有系统组件想发送内部广播给自己,如果必须明确列出每个动作作为受保护的广播是很烦人的,因此在这里进行安全检查,如果符合安全条件及可以正常发送;如果不满足安全条件,则就会走到C处,打印wtf log。wtf的意思是What a Terrible Failure: Report an exception that should never happen.
    虽然检查不安全但是系统还是会允许发送该广播,logcat中会出现这样的提醒log:

    Sending non-protected broadcast notif_in_use_cancel from system 7747:com.journeyui.deskclock/1000
                                                                         pkg com.journeyui.deskclock
    

    3. 未给广播加保护影响

    虽然广播正常发送了,不影响广播的作用,但是这样的使用是不安全的,系统组件自定义的广播可能会被恶意软件接收或者发送,导致系统不稳定。

    且在这个log打印同时会在系统的dropbox下新生成一个wtf的log文件,发多少条这样的广播,就生成多少个这样的文件。而dropbox中默认最多只有1000个文件,再多了就会冲掉旧的文件。Dropbox是我们用于分析ANR tombstone等问题的重要log来源,因此会严重影响稳定性同事对该类问题的统计和解决。

    为了提高系统的安全性且避免这样的log,系统应用组件当在使用自发自收的广播时,要尽可能使用明确的广播,及指定接收的包名或者组件名,且对广播发送和接收加权限保护。同时这也使我们使用广播更加规范。

    三、规范举例:

    如果该广播是an explicit broadcast,且该receiver的android:exported为false,或者ri.activityInfo.permission!=null,及该receiver加了权限保护,系统则认为这个广播时做过保护了的,予以正常发送,便不会打这个log,是个规范使用的广播。

    1. 动态广播,系统应用可以使用本地广播进行操作是可以满足检查安全的需求。
    2. 静态广播:
      a.如果是系统独立应用的广播,可以在应用的AndroidManifest.xml里声明为保护广播就可以了。不过注意验证的时候,需要使用adb push到system/app或者system/priv-app/下再重启安卓验证;使用adb install -r后验证依然会报未保护提醒。

    b. 使用指定包名并且加权限保护 

     

    b1. 在Androidmanifest.xml里声明receiver的时候加上自定义的权限,如果是仅需应用内接收,可以将android:exported属性设置为false;

     

            <receiver android:name=".DemoReceiver"
                android:exported="false"
                android:permission="com.android.permission.RECV.XXX">
                <intent-filter android:priority="1000">
                    <action android:name="com.android.demo.test.XXX"/>
                </intent-filter>
            </receiver>

     

    b2. 发送广播的地方指定包名或者组件名

    Intent i = new Intent("com.android.demo.test.XXX");
    i.setPackage("com.zeusis.broadcasttest");
    sendBroadcast(i,"com.android.permission.RECV.XXX");
    
    具体加权限请参考 http://blog.csdn.net/javensun/article/details/7334230 

    注意指给广播加权限是不够的,在checkBroadcastFromSystem对未在framework中声明为保护广播的系统应用自定义广播进行安全检查的前提是这是一个explicit的广播。因此满足上面两步后才能真正消除警告的wtf log.

    c. 如果是非独立应用的系统组件,或者独立系统应用使用的公共的广播,推荐在framework/base/core/res/AndroidManifest.xml中有声明为保护广播。

    android exported属性:http://blog.csdn.net/watermusicyes/article/details/46460347
    广播权限限制:http://blog.csdn.net/javensun/article/details/7334230 
    本地广播使用:http://blog.csdn.net/lj2012sy/article/details/51688985

  • 相关阅读:
    RIO包 健壮的I/O函数代码
    Upgrading WebLogic Application Environments --官方文档
    JAVA的静态代理与动态代理比较--转载
    指向函数的指针--转
    c之指针与数组(1)
    weblogic 异常常见处理方法
    Redis: under the hood---转载
    A GDB Tutorial with Examples--转
    The best career advice I’ve received --转载
    redis 大数据插入
  • 原文地址:https://www.cnblogs.com/xiayexingkong/p/6769650.html
Copyright © 2011-2022 走看看