一 :需要對Activity生命周期進行管理的原因及解決方案
原因: 在Android平臺上使用ClassLoader把插件的Activity,Service加載進來時企软,加載進來的Activity和Service是普通的類,是沒有生命周期的檐束,因為Activity,Service等組件的生命周期是由AMS管理的。所以動態(tài)加載Activity首先要解決的一個問題就是對Activity生命周期進行管理夭拌。
解決方案: 使用代理的思想规阀,要啟動一個插件Activity首先啟動一個代理的StubActivity恒序,StubActivity注冊在Host程序中。再在合適的時機把StubActivity替換成插件Activity谁撼,以此來突破必須在注冊Activity的限制AndroidManifest限制歧胁。
二: Activity啟動流程
具體流程可以去看源碼或者閱讀《Android藝術(shù)探索這本書》
簡化總結(jié)如下
三:Hook點選擇及Hook方法
選擇Hook點
**1 把TargetActivity替換成StubActivity (Hook掉ActivityManagerProxy)
**選擇在啟動流程進入到system_server進程之前,Hook掉ActivityManagerNative.getDefault()的返回值(ActivityManagerProxy)。這樣當Activity的流程走到ActivityManagerNative.getDefault().startActivity時与帆,方法會進入到InvocationHandler的invoke內(nèi)部了赌,在這個方法內(nèi)部選擇替換掉ActivityManagerProxy.startActivity()的Intent參數(shù),使得Intent顯示啟動目標Activity為StubActivity玄糟,因為StubActivity在AndroidManifest文件中注冊過勿她,所以能通過AMS的真實性校驗。
Hook startActivity()方法阵翎,選擇替換掉Intent參數(shù)
if ("startActivity".equals(method.getName())) {
// 只攔截這個方法
// 替換參數(shù), 任你所為;甚至替換原始Activity啟動別的Activity偷梁換柱
// API 23:
// public final Activity startActivityNow(Activity parent, String id,
// Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
// Activity.NonConfigurationInstances lastNonConfigurationInstances) {
// 找到參數(shù)里面的第一個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的啟動流程比較熟悉逢并,可以發(fā)現(xiàn)當啟動流程由AMS進程轉(zhuǎn)到App進程時,通過ApplicationThread進行Binder IPC郭卫,這個時候ApplicationThread里面的ScheduleLaunchActivity會被調(diào)用砍聊,且運行在App進程的Binder線程池,這個方法通過H Handler發(fā)送msg轉(zhuǎn)發(fā)到ActivityThread里面的handleLaunchActivity贰军。
仔細觀察Handler的dispatchMessage方法可以發(fā)現(xiàn) msg.callback的優(yōu)先級大于mCallback大于handleMessage. 因為msg.callback我們沒辦法預(yù)知玻蝌,所以可以選擇Hook掉mCallback,當msg.what=LAUNCH_ACTIVITY時词疼,選擇將Intent的顯示啟動目標為TargetActivity俯树,然后就會順利的創(chuàng)建出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;
// 根據(jù)源碼:
// 這個對象是 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 {
// 把替身恢復(fù)成真身
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();
}
}
}