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等等。