引言
到目前为止,我们已经分析了OpenAtlas中插件的安装,卸载,更新,以及安装好插件之后组件类的加载过程,但是对于这些是如何引发的还不知道,比如,在宿主的一个Activit中调用startActivity()跳转到插件中的一个Activity,如何判断这个Activity在的插件是否已经安装,如果没有,需要在哪里判断并安装呢?
要回答这些问题,就需要对于Android中四大组件的启动过程非常熟悉(主要涉及到ActivityThread,ApplicationThread,H,Instrumentation,ActivityManagerNative,
ActivityManagerProxy,AMS等),之后Hook一些关键的对象,然后在关键的位置进行判断并安装插件。
1.startActivity()过程分析及Hook
startActivity()依据上下文可分为Activity.startActivity(),Application.startActivity()和Fragment.startActivity().这些情况都要考虑到,幸运的是,这些方法只是在开始时有区别,到了后面就一样了,所以先分析Activity.startActivity(),剩下的两个就简单了。
1.1 Activity.startActivity()分析
Activity.startActivity(Intent)如下:
|
|
由于options==null,故执行startActivityForResult(intent,-1);而startActivityForResult(Intent,int)如下:
|
|
在这里会调用Instrumentation对象的execStartActivity()方法;而Instrumentation的execStartActivity()方法如下:
|
|
注意到这里最终调用了ActivityManagerNative.getDefault().startActivity()方法,那么这个ActivityManagerNative.getDefault()到底是什么对象呢?
ActivityManagerNative#getDefault()方法如下:
|
|
而gDefault的定义如下:
|
|
注释中已经写了,b是一个BinderProxy对象,这是怎么来的呢?
1.2 BinderProxy对象的来源
看一下ServiceManager的getService()方法即可:
|
|
其中getIServiceManager()方法如下:
|
|
为了知道ServiceManagerNative.asInterface()的结果是什么,需要先看一下BinderInternal.getContextObject()的结果是什么.
|
|
这是一个native方法,它对应的C++方法在/frameworks/base/core/android_util_Binder.cpp中,如下:
|
|
而ProcessState::self()返回的是一个ProcessState对象:
|
|
ProcessState::getContextObject()方法如下:
|
|
其中的0就是ServiceManager对应的句柄,或者说ServiceManager的注册号。而getStrongProxyForHandle()函数如下:
|
|
这个方法比较简单,其实就是返回一个句柄(handle)为0的BpBinder对象,只是由于context manager(java层的ServiceManager在C++层对应的叫context manager)比较特殊,如果发现通过弱引用获取不到的话,需要通过一个空事件来保证context manager已经注册。
在回到android_os_BinderInternal_getContextObject()方法中,显然,在返回之前,做了一个包装,将这个BpBinder对象(C++对象)包装成一个java对象,方法如下:
|
|
- checkSubclass()是IBinder中的方法,默认返回false,而继承IBinder的BpBinder并没有重写这个方法,故val->checkSubclass()为false
- jobject object=(jobject)val->findObject(&gBinderProxyOffsets);是根据gBinderProxyOffsets这个结构体变量的地址查找之前创建的BinderProxy对象,假设我们这里是首次创建,则返回null
- object=env->NewObject(gBinderProxyOffsets.mClass,gBinderProxyOffsets.mConstructor);是调用gBinderProxyOffset这个结构体变量中的类和构造方法创建java对象,那么这个gBinderProxyOffsets是何时被赋值的呢?
答案就在android_util_Binder.cpp的int_register_android_os_BinderProxy(JNIEnv*env)中:
|
|
显然,在这里对gBinderProxyOffsets进行了赋值。而int_register_android_os_BinderProxy是在register_android_os_Binder()中调用的,register_android_os_Binder()又是在AndroidRuntime中调用的,AndroidRuntime是用于启动虚拟机的,这个不是本篇讨论的重点,所以不深入了,感兴趣的童鞋可以看我之前的博客,比如Java服务框架分析.
回到javaObjectForIBinder()方法中,可知此时先创建一个BinderProxy对象,然后将val.get()即handle为0的BpBinder对象作为BinderProxy对象的属性mObject保存,最后它返回的就是一个BinderProxy对象.
再回到getIServiceManager()方法中,此时它就等价于:
|
|
并且该BinderProxy对象中含有handle为0的BpBinder对象(保存在BinderProxy中的mObject中).
而ServiceManagerNative.asInterface()方法如下:
|
|
由于BinderProxy的queryLocalInterface()方法返回null,故最后返回的是一个ServiceManagerProxy对象,该对象的mRemote中保存着BinderProxy对象,而BinderProxy对象中又保存着一个handle为0的BpBinder对象。
所以getIServiceManager().getService(name)其实是调用ServiceManagerProxy对象的getService()方法,最终会返回一个BinderProxy对象,并且该BinderProxy对象的mObject为一个BpBinder对象,该BpBinder对象中的handle值为ActivityManagerService的句柄。由于这个涉及到Java层Service的注册,篇幅较长,感兴趣的可以看我的这篇博客:Java服务框架分析.
1.3 ActivityManagerNative.asInterface()的结果
再回到ActivityManagerNative的asInterface()方法中,显然,gDefault就是一个实现了IActivityManager的对象,其实如果对Binder通信熟悉的话,看到这里就知道am是什么了。我们继续往下看,ActivityManagerNative的asInterface()方法如下,注意传入其中的参数b是一个BinderProxy对象:
|
|
其中obj是BinderProxy对象,为了知道obj.queryLocalInterface(descriptor);的结果是什么,需要看一下BinderProxy的定义:
|
|
显然,BinderProxy的queryLocalInterface()方法返回null,所以这里会返回ActivityManagerProxy对象,其中的mRemote就是BinderProxy对象。
1.4 继续Instrumentation中execStartActivity()分析
由上面的分析可知ActivityManagerNative.getDefault()的结果为ActivityManagerProxy对象,所以接下来分析ActivityManagerProxy的startActivity()方法:
|
|
这里是非常典型的Binder通信,最终会调用到继承ActivityManagerNative的AMS中的startActivity()方法:
|
|
这里直接调用了startActivityAsUser()方法,接着是ActivityStackSupervisor的startActivityMayWait()方法,在startActivityMayWait()中又调用了startActivityLocked()方法,这个方法非常重要,因为在这个方法中进行了一系列的检查,比如权限检查,代码如下:
|
|
如果在宿主的Manifest文件中没有声明该Activty,就会导致ActivityInfo对象aInfo==null,从而返回START_CLASS_NOT_FOUND结果。之后,在Instrumentation的execStartActivity()返回之后会检查这个值,发现是这个结果就会抛出异常:
|
|
到这里就解释了为什么一定要在宿主的Manifest文件中声明插件的组件信息。
之后主要是ActivityStackSupervisor和ActivityStack()之间的跳转,一直到ActivityStackSupervisor的realStartActivtyLock()方法,由于其中没有特别需要注意的地方,就直接放流程图了(其实画时序图更好,这里偷个懒):
显然,最终会调用到realStartActivityLock()方法,该方法如下:
|
|
那这个app.thread是什么呢?其实是ApplicationThreadProxy对象,感兴趣的可以跟踪一下,是在ActivityManagerNative的onTransact()中赋值的:
|
|
其中ApplicationThreadNative.asInterface(data.readStrongBinder());返回一个ApplicationThreadProxy对象,这个与前面ServieManagerNative.asInterface()类似,不再赘述.
1.5 AMS与ApplicationThread的Binder IPC过程
再回到realStartActivityLocked方法,这就很明显是要进行Binder IPC了,而且此时AMS是client,ApplicationThread是server,最终会调用ApplicationThread(ApplicationThread继承自ApplicationThreadNative)中的onTransact()方法,注意现在已经从AMS所在的System Server进程切换到了ActivityThread是在的进程,即宿主App运行的进程:
|
|
而ApplicationThread中的scheduleLaunchActivity方法如下:
|
|
注意最后面的H.LAUNCH_ACTIVITY,会通过H(继承自Handler)发送消息H.LAUNCH_ACTIVITY,而H中对这个消息的处理如下:
|
|
而handleLaunchActivity方法如下:
|
|
这里又调用了performLaunchActivity()方法:
|
|
注意两点:
1) r.packageInfo.getClassLoader()中的r.packageInfo是LoadedApk对象,Atlas中的init方法中AndroidHack.injectClassLoader(packageName,delegateClassLoader);中利用反射将LoadedApk中的mClassLoader对象替换为DelegateClassLoader对象:
|
|
2)其中调用了Instrumentation的newActivity方法来创建Activity,所以,如果需要hook的话,就需要重写这个方法。
到这里,我们可以总结出Activity.startActivity()过程中ActivityThread(主要是其中的ApplicationThread)和AMS的跨进程通信过程:
1.6 Activity.startActivity()的Hook点
要Hook掉Instrumentation,就要先获取ActivityThread对象。那么如何获取呢?注意到ActivityThread中的一个static方法currentActivityThread:
|
|
要注意ActivityThread是@hide修饰的,需要通过反射调用该方法:
|
|
这样就获取到了当前App进程的ActivityThread对象,另外注意到ActivityThread中的Instrumentation对象为包访问权限,所以也需要使用反射来Hook:
|
|
这样,就利用InstrumentationHook对象替换掉了ActivityThread中的mInstrumentation.
1.7 InstrumentationHook需要重写哪些方法
在1.6中我们已经将mInstrumentation给hook掉了,那么需要重写哪些方法呢?
先看一下,除了Activity.startActivity()之外,还有哪些地方会有startActivity()方法?
其实还有Application.startActivity(),Fragment.startActivity()方法,总共这3种方法的流程关系如下:
显然,这3中调用方式只是前面的几步流程不太一样,从Instrumentation的execStartActivity方法开始就完全一样了。
所以execStartActivity方法需要重写,注意它有多个重载方法。
在ActivityThread的handleLaunchActivity方法中,调用了performLaunchActivity方法来创建Activity,该方法如下:
|
|
- 注意其中调用了Instrumentation的newActivity()方法来创建Activity,注意这是一个重要的方法,需要在我们自己的Instrumentation类中重写该方法。
- 注意在后面调用了activity.setTheme应用主题,所以如果需要将我们的delegateResources这个Resources对象应用到主题中,就需要在newActivity()中进行hook,所以在InstrumentatinoHook的newActivity代码如下:
|
|
至于在ContextImplHook中重写写startActivity()方法,纯粹是为了:
1)提前进行必要的插件安装;
2)提前进行判断,如果没有插件存在该Activity,则在这里就可以进行异常处理,比如跳到欢迎页面
但是注意ContextImplHook中重写bindService和startService的目的不是这个。因为ContextImplHook对象是要替换Activity等ContextWrapper子类(共同点是对含有mBase)中的mBase(ContextImpl对象)的,而bindService和startService的hook点不像startActivity那么多,所以在ContextImplHook中重写是为了在启动插件中的Service之前,保证插件已经安装,并且在插件安装完之后,在BundleLifecycleHandler的case BundleEvent.LOADED事件处理中进行插件资源和ClassLoader的替换。
2.startService()过程分析
由于前面分析过startActivity()的过程,而startService()的过程和startActivity()的过程有很多类似的地方,特别是App进程与System Server进程中的AMS进行Binder IPC的地方。
所以这里就直接给出流程图:
注意最后ActivityThread的handleCreateService方法:
|
|
在其中利用LoadedApk中的ClassLoader来加载该Service类并生成对象。而由于在ContextImplHook中已经将LoadedApk中的ClassLoader替换为DelegateClassLoader对象,所以最终可以加载到插件中的Service类。