一 :需要对Activity生命周期进行管理的原因及解决方案
原因: 在Android平台上使用ClassLoader把插件的Activity,Service加载进来时,加载进来的Activity和Service是普通的类,是没有生命周期的,因为Activity,Service等组件的生命周期是由AMS管理的。所以动态加载Activity首先要解决的一个问题就是对Activity生命周期进行管理。
解决方案: 使用代理的思想,要启动一个插件Activity首先启动一个代理的StubActivity,StubActivity注册在Host程序中。再在合适的时机把StubActivity替换成插件Activity,以此来突破必须在注册Activity的限制AndroidManifest限制。
二: Activity启动流程
具体流程可以去看源码或者阅读《Android艺术探索这本书》
简化总结如下
三:Hook点选择及Hook方法
选择Hook点
**1 把TargetActivity替换成StubActivity (Hook掉ActivityManagerProxy)
**选择在启动流程进入到system_server进程之前,Hook掉ActivityManagerNative.getDefault()的返回值(ActivityManagerProxy)。这样当Activity的流程走到ActivityManagerNative.getDefault().startActivity时,方法会进入到InvocationHandler的invoke内部,在这个方法内部选择替换掉ActivityManagerProxy.startActivity()的Intent参数,使得Intent显示启动目标Activity为StubActivity,因为StubActivity在AndroidManifest文件中注册过,所以能通过AMS的真实性校验。
Hook startActivity()方法,选择替换掉Intent参数
if ("startActivity".equals(method.getName())) {
// 只拦截这个方法
// 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱
// API 23:
// public final Activity startActivityNow(Activity parent, String id,
// Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
// Activity.NonConfigurationInstances lastNonConfigurationInstances) {
// 找到参数里面的第一个Intent 对象
Intent raw;
int index = 0;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
raw = (Intent) args[index];
Intent newIntent = new Intent();
// 这里包名直接写死,如果再插件里,不同的插件有不同的包 传递插件的包名即可
String targetPackage = "com.weishu.intercept_activity.app";
// 这里我们把启动的Activity临时替换为 StubActivity
ComponentName componentName = new ComponentName(targetPackage, StubActivity.class.getCanonicalName());
newIntent.setComponent(componentName);
// 把我们原始要启动的TargetActivity先存起来
newIntent.putExtra(HookHelper.EXTRA_TARGET_INTENT, raw);
// 替换掉Intent, 达到欺骗AMS的目的
args[index] = newIntent;
Log.d(TAG, "hook success");
return method.invoke(mBase, args);
}
return method.invoke(mBase, args);
2 把StubActivity换回TargetActivity(Hook掉Handler.callback)
如果对于Activity的启动流程比较熟悉,可以发现当启动流程由AMS进程转到App进程时,通过ApplicationThread进行Binder IPC,这个时候ApplicationThread里面的ScheduleLaunchActivity会被调用,且运行在App进程的Binder线程池,这个方法通过H Handler发送msg转发到ActivityThread里面的handleLaunchActivity。
仔细观察Handler的dispatchMessage方法可以发现 msg.callback的优先级大于mCallback大于handleMessage. 因为msg.callback我们没办法预知,所以可以选择Hook掉mCallback,当msg.what=LAUNCH_ACTIVITY时,选择将Intent的显示启动目标为TargetActivity,然后就会顺利的创建出TargetActivity。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Hook掉CallBack
/* package */ class ActivityThreadHandlerCallback implements Handler.Callback {
Handler mBase;
public ActivityThreadHandlerCallback(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
// ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
// 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码
case 100:
handleLaunchActivity(msg);
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
// 这里简单起见,直接取出TargetActivity;
Object obj = msg.obj;
// 根据源码:
// 这个对象是 ActivityClientRecord 类型
// 我们修改它的intent字段为我们原来保存的即可.
/* switch (msg.what) {
/ case LAUNCH_ACTIVITY: {
/ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
/ final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
/
/ r.packageInfo = getPackageInfoNoCheck(
/ r.activityInfo.applicationInfo, r.compatInfo);
/ handleLaunchActivity(r, null);
*/
try {
// 把替身恢复成真身
Field intent = obj.getClass().getDeclaredField("intent");
intent.setAccessible(true);
Intent raw = (Intent) intent.get(obj);
Intent target = raw.getParcelableExtra(HookHelper.EXTRA_TARGET_INTENT);
raw.setComponent(target.getComponent());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}