Android checkBroadcastFromSystem 源碼分析

  1. 受保護的廣播只能由System進程(參考isCallerSystem小節(jié))發(fā)送,否則會報錯
  2. System進程只能發(fā)送受保護的廣播,除非有下文說的特殊情況(參考Sending non-protected broadcast小節(jié)),否則會有wtf日志打印

checkBroadcastFromSystem

    private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
            String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {

        if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
            // Don't yell about broadcasts sent via shell
            return;
        }

        final String action = intent.getAction();
        if (isProtectedBroadcast
                || 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)
                || Intent.ACTION_FACTORY_RESET.equals(action)
                || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
                || TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
                || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
                || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
                || AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.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 (intent.getPackage() != null || intent.getComponent() != null) {
            if (receivers == null || receivers.size() == 0) {
                // Intent is explicit and there's no receivers.
                // This happens, e.g. , when a system component sends a broadcast to
                // its own runtime receiver, and there's no manifest receivers for it,
                // because this method is called twice for each broadcast,
                // for runtime receivers and manifest receivers and the later check would find
                // no receivers.
                return;
            }
            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;
            }
        }

        // 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());
        }
    }

絕大多數(shù)從系統(tǒng)內(nèi)部發(fā)送的廣播都應(yīng)該受到保護,以避免出現(xiàn)安全漏洞:

  1. 代碼首先檢查廣播的一些標(biāo)志。如果是通過 shell 發(fā)送的廣播,那么就不做檢查。
  2. 代碼檢查廣播的動作(action)。如果廣播的動作是一些特定的公共動作,或者是受保護的廣播,則認為是系統(tǒng)內(nèi)部的廣播,那么就不做檢查。
  3. 代碼檢查廣播的接收者。如果廣播是顯式的(通過指定包名或組件名),并且沒有接收者,那么就不做檢查。
  4. 代碼檢查廣播的接收者。如果廣播是顯式的(通過指定包名或組件名),并且有接收者,且該廣播規(guī)定了接收權(quán)限(靜態(tài)廣播的exported為true的時候加上了permission限制,動態(tài)廣播指定了requiredPermission),那么就不做檢查。
  5. 否則,就輸出wtf日志,認為該廣播是不受保護,即不安全的廣播,wtf日志包括了發(fā)送者的uid和packageName以及堆棧。

isProtectedBroadcast

這段代碼的目的是驗證受保護的廣播只能由系統(tǒng)代碼發(fā)送,并且系統(tǒng)代碼只發(fā)送受保護的廣播

        // Verify that protected broadcasts are only being sent by system code,
        // and that system code is only sending protected broadcasts.
        final boolean isProtectedBroadcast;
        try {
            isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception", e);
            return ActivityManager.BROADCAST_SUCCESS;
        }
        @Override
        public boolean isProtectedBroadcast(String actionName) {
            if (actionName != null) {
                // TODO: remove these terrible hacks
                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;
                }
            }
            // allow instant applications
            synchronized (mProtectedBroadcasts) {
                return mProtectedBroadcasts.contains(actionName);
            }
        }
    // Broadcast actions that are only available to the system.
    // 受保護的廣播只能給系統(tǒng)用
    @GuardedBy("mProtectedBroadcasts")
    final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
  1. 校驗actionName是否以某個字符串為開始。
  2. 校驗actionName是否存在于mProtectedBroadcasts數(shù)據(jù)結(jié)構(gòu)中。

如果滿足以上兩個條件,就認為是受保護的廣播。

isCallerSystem

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

每次調(diào)用checkBroadcastFromSystem進行check的前提是廣播發(fā)起者是System進程:

        final boolean isCallerSystem;
        switch (UserHandle.getAppId(callingUid)) {
            case ROOT_UID:
            case SYSTEM_UID:
            case PHONE_UID:
            case BLUETOOTH_UID:
            case NFC_UID:
            case SE_UID:
            case NETWORK_STACK_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) && callerApp.isPersistent();
                break;
        }

這里的System進程指的是有特殊UID或者是Persistent進程。也就是說只針對System進程發(fā)起的廣播進行校驗,System進程發(fā)起的廣播必須是受保護的廣播。

Sending non-protected broadcast

這是一個wtf的日志,如何不打印這個wtf呢?

  1. callapp不是System進程,參考isCallerSystem小節(jié)
  2. 該廣播是shell進程發(fā)的
  3. 該廣播是受保護的廣播,參考isProtectedBroadcast小節(jié),或者action比較特殊。
  4. 該廣播是顯示廣播,有明確具體的Package或者Component,且沒有廣播接收者
  5. 該廣播是顯示廣播,有明確具體的Package或者Component,有廣播接收者,此時廣播需要有明確的權(quán)限限制。

受保護的廣播只能由System進程發(fā)送

        // First line security check before anything else: stop non-system apps from
        // sending protected broadcasts.
        if (!isCallerSystem) {
            if (isProtectedBroadcast) {
                String msg = "Permission Denial: not allowed to send broadcast "
                        + action + " from pid="
                        + callingPid + ", uid=" + callingUid;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);

首先進行一線安全檢查:停止非系統(tǒng)應(yīng)用程序發(fā)送受保護的廣播。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容