一. 引言
1.1 保活概述
什么是保活?保活就是在用户主动杀进程,或者系统基于当前内存不足状态而触发清理进程后,该进程设法让自己免于被杀的命运或者被杀后能立刻重生的手段。
保活是”应用的蜜罐,系统的肿瘤“,应用高保活率给自己赢得在线时长,甚至做各种应用想做而用户不期望的行为,给系统带来的是不必要的耗电,以及系统额外的性能负担。
保活方案一直就层出不穷,APP开发们不断地绞尽脑汁让自己的应用能存活得时间更长, 主要思路有两个:
- 提升进程优先级,降低被杀概率
- 比如监听SCREEN_ON/OFF广播 启动一像素的透明Activity
- 启动空通知,提升fg-service
- 进程被杀后,重新拉起进程
- 监听系统或者第3方广播拉起进程。目前安全中心/Whetstone已拦截
- Native fork进程相互监听,监听到父进程被杀,则通过am命令启动进程。force-stop会杀整个进程组,几乎很难生效
1.2 保活案例
这是在2017年发现一款办公协作应用,在安全中心关闭TIM自启动功能的情况, 一键清理、强力清理等各大招都无法彻底杀掉TIM,系统的自启动拦截都没能阻止TIM的永生,这引起了我强烈的兴趣。
二. 初步分析
2.1 初识TIM
执行命令adb shell ps | grep tencent.tim
,可见TIM共有4个进程, 其父进程都是Zygote
root@gityuan:/ # ps | grep tencent.tim u0_a146 27965 551 1230992 43964 SyS_epoll_ 00f6df4bf0 S com.tencent.tim:Daemon u0_a146 27996 551 1252492 54032 SyS_epoll_ 00f6df4bf0 S com.tencent.tim:MSF u0_a146 28364 551 1348616 89204 SyS_epoll_ 00f6df4bf0 S com.tencent.tim:mail u0_a146 31587 551 1406128 147976 SyS_epoll_ 00f6df4bf0 S com.tencent.tim
2.2 一键清理看现象,排查初步怀疑
以下是对TIM执行一键清理后的日志:
12-21 21:12:20.265 1053 1075 I am_kill : [0,4892,com.tencent.tim:Daemon,5,stop com.tencent.tim: from pid 4617] 12-21 21:12:20.272 1053 1075 I am_kill : [0,5276,com.tencent.tim:mail,2,stop com.tencent.tim: from pid 4617] 12-21 21:12:20.305 1053 1075 I am_kill : [0,4928,com.tencent.tim,2,stop com.tencent.tim: from pid 4617] 12-21 21:12:20.330 1053 1075 I am_kill : [0,4910,com.tencent.tim:MSF,0,stop com.tencent.tim: from pid 4617] 12-21 21:13:59.920 1053 1466 I am_proc_start: [0,5487,10146,com.tencent.tim:MSF,service,com.tencent.tim/com.tencent.mobileqq.app.DaemonMsfService] 12-21 21:13:59.984 1053 1604 I am_proc_start: [0,5516,10146,com.tencent.tim,content provider,com.tencent.tim/com.tencent.mqq.shared_file_accessor.ContentProviderImpl]
Force-stop是系统提供的杀进程最为彻底的方式,详见文章Android进程绝杀技–forceStop。从日志可以发现一键清理后TIM的4个进程全部都已被Force-stop。但进程com.tencent.tim:MSF立刻就被DaemonMsfService服务启动过程而拉起。
问题1:
安全中心已配置了禁止TIM的自启动, 并且安全中心和系统都有对进程自启动以及级联启动的严格限制,为何会有漏网之鱼?
怀疑1: 是否安全中心自启动没能有效限制,以及微信/QQ跟TIM有所级联,比如com.tencent.mobileqq.app.DaemonMsfService服务名中以com.tencent.mobileqq(QQ的包名)开头,经过dumpsys以及反复验证后排除了这种可能性,如下:
12-21 21:12:20.266 1053 1075 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.tencent.tim uid : 10146 12-21 21:12:20.291 1053 1075 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.tencent.tim uid : 10146 12-21 21:12:20.323 1053 1075 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.tencent.tim uid : 10146 12-21 21:12:20.323 1053 1075 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.tencent.tim uid : 10146 12-21 21:12:20.331 1053 1075 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.tencent.tim uid : 10146 12-21 21:12:20.332 1053 1075 I AutoStartManagerService: MIUILOG- Reject RestartService packageName :com.tencent.tim uid : 10146
怀疑2: 是否在TIM进程被杀后, 收到BinderDied后的死亡回调过程中将Service再次拉起,这个情况也很快就被排除, 因为force-stop这种冷面强力杀手, 并不会等到死亡回调再去清理进程相关信息,而是直接连根拔起,并不会走到AMS的死亡回调。
怀疑3: TIM设置了alarm机制,在callApp为空符合特征, 但经过分析这里就是普通的startService, 非startServiceInPackage(), 也排除了这种可能性
//启动DaemonAssistService时,callApp为空,只有通过PendingIntent方式才可能出现这种情况 12-21 21:56:54.653 3181 3195 I am_start_service: [-1,NULL,10146,com.tencent.tim:Daemon,com.tencent.tim/com.tencent.mobileqq.app.DaemonAssistService,{cmp=com.tencent.tim/com.tencent.mobileqq.app.DaemonAssistService}] 12-21 21:56:56.666 3181 3827 I am_start_service: [-1,NULL,10146,com.tencent.tim:MSF,com.tencent.tim/com.tencent.mobileqq.app.DaemonMsfService,{cmp=com.tencent.tim/com.tencent.mobileqq.app.DaemonMsfService}]
既然排除以上3种可能,直接上断点来看看吧
2.3 Android Studio断点分析
一上断点就发现了意外的一幕:
问题2:
startService()的callingPid怎么可能等于0?
2.3.1 分析callingPid=0
为什么说上面是意外的一幕呢?这需要对binder底层原理有一定深入理解,才能看出一些端倪,那就是此处callingPid=0是不合理逻辑的。很多人可能不太理解为何就不合乎逻辑, 这要从Binder原理说起, startService()这个Binder call是属于同步binder调用, 对于binder调用过程,只有异步Binder调用的情况下callingPid=0才会为空, 因为不需要reply应答数据给发送binder请求的那一端。 但如果是同步的,则必须要给出callingPid,否则无法将应答数据回传给发送方。 这是由Binder Driver所决定的,见如下Binder Driver核心代码:
(1)Binder发起端:根据当前ONE_WAY来决定是否设置from线程
- 文章2300
- 用户1336
- 访客10730680
爱是你与一切之间的桥梁。
wordpress采集器——Wp-AutoPost精简无限制版
linux文件夹打包命令
ndk神奇问题之non-numeric second argument to `wordlist' function: '8.7z'
XEON E5无法使用系统评分解决方法
win10利用winsat disk测试磁盘读写速度
C++ 11新语法获取系统盘符
Android Studio使用Opencv2.4.9进行NDK开发
10年后,Android应用程序仍然比iOS应用程序差
Notepad++如何代码格式化——NppAStyle
iMessage for Android是我们需要的但不是我们想要的
二进制加壳原理与实现
仙剑奇侠传3d回合-PC端辅助
adb logcat 命令行用法