Service Hook

2, Service Hook

Service和Activity很类似,都有穿马甲和脱马甲的过程,但是细节不同。

有2种方法启动一个service, startService和bindService。在此就以startService为例进行论述。

2.1 service注册

AndroidManifest.xml里面的确注册了大量的service,但不是空的。

<service
   android:name=".stub.ServiceStub$StubP00$P00"
   android:exported="false"
   android:label="@string/stub_name_service">
   <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="com.morgoo.droidplugin.category.PROXY_STUB" />
   </intent-filter>
</service>

这些注册的service都继承于ServiceStub, ServiceStub继承于AbstractServiceStub,这个类后面再分析。

2.2 替换

在AMS章节论述过, 调用AMS的startService方法其实是调用startService的beforeInvoke方法。

IactivityManagerHookHandle的内部类startService的beforeInvoke方法如下,

info = replaceFirstServiceIntentOfArgs(args);

直接调用replaceFirstServiceIntentOfArgs方法,该方法主要逻辑如下,

1,获取intent以及对应的ServiceInfo

int intentOfArgIndex = findFirstIntentIndexInArgs(args);
if (args != null && args.length > 1 && intentOfArgIndex >= 0) {
    Intent intent = (Intent) args[intentOfArgIndex];
    ServiceInfo serviceInfo = resolveService(intent);

2,替换为已经注册了的马甲service(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);

3, 将原始的intent作为一个EXTRA_TARGET_INTENT参数设置到这个新的intent里面

newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
newIntent.setFlags(intent.getFlags());
args[intentOfArgIndex] = newIntent;

2.3 恢复

什么时候service开始恢复真身呢?

Activity与Service组件最大的不同点在于,Activity组件可以与用户进行交互;这一点意味着用户的行为会对Activity组件产生影响,

对来说最重要的影响就是Activity组件的生命周期;用户点击按钮从界面A跳转到界面B,会引起A和B这两个Activity一系列

生命周期的变化。而Service组件则代表后台任务,除了内存不足系统回收之外,它的生命周期完全由的代码控制,

与用户的交互无关。

Service的Hook方案是:

注册一个真正的Service组件ProxyService,让这个Service承载一个真正的Service组件所具备的能力 (进程优先级等);

当启动插件的服务比如PluginService的时候,统一启动这个ProxyService,当这个ProxyService 运行起来之后,

再在它的onStartCommand等方法里面进行分发,执行PluginService的onStartCommond等对应的方法。

在onStartCommand等方法里面进行恢复过程。

其实就是android 系统管理ProxyService的生命周期, ProxyService管理PluginService的生命周期。通过这种手段,

PluginService就像一个真的service一样。

并且相对于Activity, DroidPlugin框架中AndroidManifest.xml里每个进程只注册了一个service。如果插件程序要启动

多个service怎么办?DroidPlugin定义了一个ServcesManager来解决这个问题。

Droidplugin框架中注册的service都是继承于AbstractServiceStub, AbstractServiceStub定义如下,

public abstract class AbstractServiceStub extends Service {

首先看看mCreator变量,定义如下,

private static ServcesManager mCreator = ServcesManager.getDefault();

ServcesManager其实也是一个单例模式, getDefault方法如下,

public static ServcesManager getDefault() {
     synchronized (ServcesManager.class) {
        if (sServcesManager == null) {
            sServcesManager = new ServcesManager();
        }
      }
     return sServcesManager;
}

AbstractServiceStub的onStart方法主要逻辑如下,

mCreator.onStart(this, intent, 0, startId);

ServcesManager的onStart方法主要逻辑如下,

1,获取真实的intent

Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);

2,获取对应的ServiceInfo对象

ServiceInfo targetInfo = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0);

3,如果真实的Service还未创建就创建,然后启动该服务。

Service service = mNameService.get(targetInfo.name);
if (service == null) {
handleCreateServiceOne(context, intent, targetInfo);
}
handleOnStartOne(targetIntent, flags, startId);

2.3.1 handleCreateServiceOne

Service Hook的精华都在handleCreateServiceOne方法中,主要逻辑如下,

1,篡改handleCreateService()方法的CreateServiceData参数,为service指定一个假的token。

CreateServiceData是一个内部类因此需要反射。

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

2,调用handleCreateService()方法创建service对象

Method method = activityThread.getClass().getDeclaredMethod("handleCreateService", CreateServiceData);
        if (!method.isAccessible()) {
            method.setAccessible(true);
        }
        method.invoke(activityThread, data);

3,通过反射获取到ActivityThread的mServices成员,把之前的假token对应的条目删掉!也就是说,

从ActivityThread眼中看,它根本不包含任何service!

Object mService = FieldUtils.readField(activityThread, "mServices");
Service service = (Service) MethodUtils.invokeMethod(mService, "get", fakeToken);
MethodUtils.invokeMethod(mService, "remove", fakeToken);

4,那总要有人管这些service吧?把这些service都加到mTokenServices这个map中,让ServcesManager进行托管.

mTokenServices.put(fakeToken, service);
mNameService.put(info.name, service);

注意,最后将创建的service对象放入了mNameService变量中,

ServcesManager有三个HashMap,

private Map<Object, Service> mTokenServices = new HashMap<Object, Service>();
private Map<String, Service> mNameService = new HashMap<String, Service>();
private Map<Object, Integer> mServiceTaskIds = new HashMap<Object, Integer>();

将每个启动的服务都以及对应的信息放入三个HashMap中。

2.3.2 handleOnStartOne

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

其实就是调用启动服务的onStartCommand方法。

服务的其他方法的原理也都完全一样,在此就不详细论述了。例如, onBind, onUnbind等等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值