DroidPlugin源码分析服务与静态广播的处理

上一篇文章分析过DroidPlugin对Activity的处理过程,不得不为对DroidPlugin的工程师们钦佩不已,那么是不是Service可以像Activity的处理过程一样来处理呢?前面讲过每一个代理进程只是预定义了一个Service,如果某一个插件中有多个Service,那岂不是某一个时刻只能有一个Service运行呢?由此可以判定可能Service的处理和Activity不一样。

一方面:平时使用Activity主要是用于展示界面和用户交互,Activity的生命周期可能受用户控制,当用户操作界面进入新界面或返回界面时。也可能受系统控制,进入锁屏界面时,或者来电话时。也就是说Activity生命周期与用户和系统都是强关系。而Service呢?启动或停止,完全由我们主动控制,也就是说Service与用户和系统都是弱关系。所以我们完全可以无需经过系统手动调用Service的生命周期来让他运行。

另一方面:我们一直说Service是运行在后台提供服务的。其实正在提供服务的并不是Service,而是在Service内实例化的Binder本地对象。包括Service的生命周期也是为Binder本地对象服务的。也就是说我们可以在一个Service里面实例化多个Binder提供服务,说白了我们可以在一个Service里面通过手动控制Service生命周期运行多个Service。前提是我们需要对这些Service管理起来。

有了上面的理解,接下来就以StartService为例分析DroidPlugin对Service的处理。
StartService和StartActivity一样都是通过调用AMS代理对象请求AMS服务的。那么不多说了直接看分发Hook AMS的类IActivityManagerHookHandle的内部类startService的beforeInvoke函数。
在beforeInvoke函数中只是调用了replaceFirstServiceIntentOfArgs函数:

    private static ServiceInfo replaceFirstServiceIntentOfArgs(Object[] args) throws RemoteException {
        int intentOfArgIndex = findFirstIntentIndexInArgs(args);
        if (args != null && args.length > 1 && intentOfArgIndex >= 0) {
            Intent intent = (Intent) args[intentOfArgIndex];
            ServiceInfo serviceInfo = resolveService(intent);
            if (serviceInfo != null && isPackagePlugin(serviceInfo.packageName)) {
                ServiceInfo proxyService = selectProxyService(intent);
                if (proxyService != null) {
                    Intent newIntent = new Intent();
                    newIntent.setAction(proxyService.name + new Random().nextInt());

                    newIntent.setClassName(proxyService.packageName, proxyService.name);
                    newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
                    newIntent.setFlags(intent.getFlags());
                    args[intentOfArgIndex] = newIntent;
                    return serviceInfo;
                }
            }
        }
        return null;
}

分析过Activity的流程,这个函数就不难分析了:
主要工作如下:
1 从参数中找到Intent的位置,然后取出Intent对象,然后通过Intent对象获得对应的ServiceInfo对象。
2 判断 当前service 的包名是不是已近安装的插件apk包名。
3 选择一个合适的代理服务,来替换要启动的目标服务。选择的过程之前分析过Activity,Service的基本类似这里不做过多分析,这里需要特别说一下的就是选择的Service必然是继承自AbstractServiceStub。分析完这个函数再说AbstractServiceStub隐藏的秘密。
4 创建一个Intent,设置启动服务为代理服务,将目标服务保存到代理服务intent中。
5 把新创建的代理Intent替换原来参数Intent的位置。由此可以骗过系统检查。

至此,目标服务已经被代理服务所替代,AMS启动完毕后,就会调用代理服务的onCreate, onStart。
前面提到所有预定义的代理服务都是继承自AbstractServiceStub。接下来看隐藏的秘密。
AbstractServiceStub的onCreate函数
这个函数没有做太多的事情,只是设置isRunning 为True。
AbstractServiceStub的onStart函数如下:

    public void onStart(Intent intent, int startId) {
        try {
            if (intent != null) {
                if (intent.getBooleanExtra("ActionKillSelf", false)) {
                    startKillSelf();
                    if (!ServcesManager.getDefault().hasServiceRunning()) {
                        stopSelf(startId);
                        boolean stopService = getApplication().stopService(intent);                    
}
                } else {
                    mCreator.onStart(this, intent, 0, startId);
                }
            }
        } catch (Throwable e) {
            handleException(e);
        }
        super.onStart(intent, startId);
}

这个函数主要做了如下工作:
是否记得在分析进程管理的时候当一个进程中Activity数为零,且Service数不为零是就会发送一个Intent 设置ActionKillSelf为True,查看是否存在插件Service在运行,如果没有则关闭Service,同时kill进程。

1 判断Intent中是否存在ActionKillSelf参数,且为True。
如果为True,先启动一个线程,然后等待服务Destory之后,再调用杀掉运行该服务的预定义插件进程。
2 判断是否还有插件服务在该进程中运行,如果有返回,如果没有则停止当前服务。这样自然就会调用该服务的onDestory函数,这样第一步的线程就会继续执行,然后停止该进程。
3 在StartService中,ActionKillSelf是没有的,则为false,因此会执行
mCreator.onStart(this, intent, 0, startId);这行代码。
mCreator是一个ServcesManager类对象。
接下来看ServcesManager的onStart函数。

    public int onStart(Context context, Intent intent, int flags, int startId) throws Exception {
        Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
        if (targetIntent != null) {
            ServiceInfo targetInfo = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0);
            if (targetInfo != null) {
                Service service = mNameService.get(targetInfo.name);
                if (service == null) {
                    handleCreateServiceOne(context, intent, targetInfo);
                }
                handleOnStartOne(targetIntent, flags, startId);
            }
        }
        return -1;
    }

这个函数主要做了如下工作:
1 通过intent 找到要启动的目标Intent。然后通过目标Intent找到目标服务的serviceInfo.
2 以目标服务的名字从mNameService查找要启动的目标服务是否已经启动。
3 如果没有启动则调用handleCreateServiceOne 创建该服务。
4如果目标服务已经缓存,说明已经启动了,或者在创建目标服务完成后,调用handleOnStartOne调用服务的onStart函数

先分析handleCreateServiceOne函数具体创建服务的过程

    private void handleCreateServiceOne(Context hostContext, Intent stubIntent, ServiceInfo info) throws Exception {
        ResolveInfo resolveInfo = hostContext.getPackageManager().resolveService(stubIntent, 0);
        ServiceInfo stubInfo = resolveInfo != null ? resolveInfo.serviceInfo : null;
        PluginManager.getInstance().reportMyProcessName(stubInfo.processName, info.processName, info.packageName);
        PluginProcessManager.preLoadApk(hostContext, info);
        Object activityThread = ActivityThreadCompat.currentActivityThread();
        IBinder fakeToken = new MyFakeIBinder();
        Class CreateServiceData = Class.forName(ActivityThreadCompat.activityThreadClass().getName() + "$CreateServiceData");
        Constructor init = CreateServiceData.getDeclaredConstructor();
        if (!init.isAccessible()) {
            init.setAccessible(true);
        }
        Object data = init.newInstance();

        FieldUtils.writeField(data, "token", fakeToken);
        FieldUtils.writeField(data, "info", info);
        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
            FieldUtils.writeField(data, "compatInfo", CompatibilityInfoCompat.DEFAULT_COMPATIBILITY_INFO());
        }

        Method method = activityThread.getClass().getDeclaredMethod("handleCreateService", CreateServiceData);
        if (!method.isAccessible()) {
            method.setAccessible(true);
        }
        method.invoke(activityThread, data);
        Object mService = FieldUtils.readField(activityThread, "mServices");
        Service service = (Service) MethodUtils.invokeMethod(mService, "get", fakeToken);
        MethodUtils.invokeMethod(mService, "remove", fakeToken);
        mTokenServices.put(fakeToken, service);
        mNameService.put(info.name, service);


        if (stubInfo != null) {
            PluginManager.getInstance().onServiceCreated(stubInfo, info);
        }
}

这个函数基本的工作如下:
1 先找到代理服务的ServiceInfo, 然后调用PluginManager.getInstance().reportMyProcessName(stubInfo.processName, info.processName, info.packageName); 关联包名和目标进程和代理进程信息,方便管理。在进程管理一文中分析过的。

2 PluginProcessManager.preLoadApk(hostContext, info) 设置LoadedApk,以及ClassLoader, 这个在插件对Activity的处理中已有分析。

3 通过查看源码可以了解如下信息,当启动一个服务,回到服务运行的应用空间来的时候(其实就是运行服务的进程中),创建服务的过程,是通过ActivityThread 的handleCreateService函数来创建的,同时在调用这个函数的时候需要传入CreateServiceData 数据类,在这个数据类中有一个iBinder对象,这个对象其实在ActivityManagerService中是一个ServiceRecord,主要用来记录一个服务信息的。当调用ActivityThread的函数handleCreateService创建服务完成之后,

A:会调用该服务的OnCreate函数。
B: 服务会以ServiceRecord 这个iBinder对象为Key Service对象为Value保存在ActivityThread的mService成员变量中。
有了这些了解以后,下面的代码主要完成如下工作:

4 目标服务实际是没有在ActivityManager中启动的,所以没有ServiceRecord对象,所以会先伪造一个IBinder对象MyFakeIBinder。

5 创建一个CreateServiceData 类对象。并把伪造的IBinder对象和目标ServiceInfo保存到该数据对象中。

6 调用ActivityThread函数handleCreateService创建目标服务对象。

7 从ActivityThread的成员变量mService中以前面伪造的IBinder对象为Key从里面取出刚刚创建的服务。

8 以伪造的IBinder对象和以目标服务类名为key分别保存到ServiceManager的成员变量mTokenServices和mNameService中。

9 调用PluginManager.getInstance().onServiceCreated(stubInfo, info);对刚刚启动的服务,和运行服务的进程进行管理。前面的进程管理的文章中已经有对Activity分析,Service基本类似。

handleOnStartOne调用Service的onStart函数的过程。

    private void handleOnStartOne(Intent intent, int flags, int startIds) throws Exception {
        ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0);
        if (info != null) {
            Service service = mNameService.get(info.name);
            if (service != null) {
                ClassLoader classLoader = getClassLoader(info.applicationInfo);
                intent.setExtrasClassLoader(classLoader);
                Object token = findTokenByService(service);
                Integer integer = mServiceTaskIds.get(token);
                if (integer == null) {
                    integer = -1;
                }
                int startId = integer + 1;
                mServiceTaskIds.put(token, startId);
                int res = service.onStartCommand(intent, flags, startId);
                QueuedWorkCompat.waitToFinish();
            }
        }
}

这个函数主要的工作如下:
1 通过Intent找到ServiceInfo,然后通过ServiceINfo的name从mNameService中找到service.
获取ClassLoader 设置Intent的ClassLoader。
2 通过service找到Token,然后通过Token在mServiceTaskIds查看是否设置startId.
3 如果为空则以token为key设置startId为Value保存到ServiceManger成员变量mServiceTaskIds中。
4 最后调用service,onstartCommend函数。
5 调用QueuedWork.waittoFinish等待完成。
至此插件对Service的处理分析完成。

DroidPlugin对静态广播的处理:
在DroidPlugin的文档中已经说过了,他们对静态广播的处理是通过动态注册的方式来实现的。也就是说DroidPlugin只是解析了插件Apk AndroidManifest文件中的静态广播,进行动态注册。
具体注册过程是在调用Application onCreate函数的时候。
当LoadedApk调用makeApplication函数的时候,会调用instrumentation.callApplicationOnCreate,而instrumentation已经被PluginInstrumentation篡改。就在PluginInstrumentation的callApplicationOnCreate函数中。会调用PluginProcessManager.registerStaticReceiver。具体函数就比较简单了,大家可以自行查看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值