写在前面的话:本篇文章基于API Level 26!未做其他版本适配!未做资源处理!插件的资源引用一定要注释!
基于上一篇文章,为什么不能用反射调用插件的Activity
运用反射确实可以调用插件的Activity的方法,但这样的启动的Activity是没有生命的周期!就像下面代码,没啥卵用。
MainActivity activity = new MainActivity();
正常的启动应该是 startActivity() ,这样的Activity才是有生命周期的!如下:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.miss.myplugin", "com.miss.myplugin.MainActivity"));
startActivity(intent);
下面是startActivity背后的流程:
调用startActivity方法后跨进程调用AMS中的方法,AMS会检查要打开的Activity是否在Manifest中注册,如果已经注册再跨进程用ActivityThread启动目标Activity。所以我们要启动插件中的Activity首要问题就是解决如何瞒过AMS的检测!
怎样绕过AMS的检测
Hook,运用反射和动态代理瞒天过海,先看下图:
咱们启动宿主的Activity不犯法吧,肯定能通过AMS检测。那我们可以这样,intent中填插件的路径和类名,在AMS之前用hook替换成宿主的Activity瞒过检测,AMS检测后再把Activity替换回来,如此启动插件的Activity是有正常生命周期的!
先分析前半部分逻辑
既然是startActivity启动的,咱们先深入下他,所有的startActivity最后调用startActivityForResult的三参方法,如下
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
// TODO : 在这使用的 intent , 咱们从这深入
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
咱们在这里深入,这里有intent
// TODO : 在这使用的 intent , 咱们从这深入,
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
寻找Instrumentation.java 中 execStartActivity() 实现
...
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
// 这里就是我们的 Hook 点
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
...
...
我们可以通过反射拿到 ActivityManager.getService() 的对象,再用动态代理改写 startActivity 的实现。看下getService()的实现
/**
* getService 返回的是 IActivityManager 对象,IActivityManager 是接口,即 IActivityManager 的 startActivity 方法
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
// 下面是 Singleton
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
看代码有点乱在这里总结下:需要的是偷梁换柱intent,intent 在 IActivityManager 的 startActivity 方法被使用,要找到具体实现此方法的对象!ActivityManager.getService() 返回的 是 IActivityManagerSingleton.get() ,IActivityManagerSingleton 是个 Singleton对象,Singleton 的 get()方法返回 mInstance,最后是 mInstance 实现的 startActivity 方法!通过反射拿到 mInstance 对象,再用动态代理改写 startActivity 逻辑,这就是插件化的灵魂!
下面是代码的具体实现(API:26)
public class HookUtil {
private static final String TAG = "TAG";
public static void hookAMS() {
// Hook 可以加载 Activity ,但不能有资源文件
/**
* TODO : hook 点
*int result = ActivityManager.getService()
* .startActivity(whoThread, who.getBasePackageName(), intent,
* intent.resolveTypeIfNeeded(who.getContentResolver()),
* token, target != null ? target.mEmbeddedID : null,
* requestCode, 0, null, options);
*
* 利用反射拿到对象,再用动态代理修改 startActivity 的逻辑 =====》ActivityManager.getService()
*
* getService() 返回的是 IActivityManagerSingleton.get() ===》 IActivityManager 是个接口
*
*
* private static final Singleton<IActivityManager> IActivityManagerSingleton = ...
*
* 利用反射,可以拿到 属性 IActivityManagerSingleton(这是个静态类,是我们需要的)
*
* 拿到 IActivityManagerSingleton 再利用反射 获得 IActivityManager ,有了他再用动态代理修改startActivity的实现逻辑
*
*
*/
try {
Class<?> classActivityManager = Class.forName("android.app.ActivityManager");
Field fieldIActivityManagerSingleton = classActivityManager.getField("IActivityManagerSingleton");
fieldIActivityManagerSingleton.setAccessible(true);
// 这是个静态变量,所以传入 null
Object singleton = fieldIActivityManagerSingleton.get(null);
Class<?> classSingleton = Class.forName("android.util.Singleton");
Field fieldMInstance = classSingleton.getDeclaredField("mInstance");
fieldMInstance.setAccessible(true);
// 实现动态代理的类
Object mInstance = fieldMInstance.get(singleton);
// 动态代理要用的接口
Class<?> iActivityManagerClass = Class.forName("android.app.IActivityManager");
/**
* ActivityManager.getService()
* .startActivity(whoThread, who.getBasePackageName(), intent,
* intent.resolveTypeIfNeeded(who.getContentResolver()),
* token, target != null ? target.mEmbeddedID : null,
* requestCode, 0, null, options);
*
*/
Object o = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{iActivityManagerClass},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/***
* 在此做逻辑更改
*/
// 方法过滤
if ("startActivity".equals(method.getName())) {
// 记录参数位置
int index = -1;
for (int i = 0; i < args.length; i++) {
// 筛选出参数列表中的 intent
if (args[i] instanceof Intent) {
index = i;
break;
}
}
// 这个是启动插件的intent
Intent intent = (Intent) args[index];
// 代理intent
Intent intentProxy = new Intent();
// 启动宿主的代理 Activity
intentProxy.setClassName("com.miss.plugin", "com.miss.plugin.ProxyActivity");
intentProxy.putExtra(TAG, intent);
// 狸猫换太子,嘴里叼着太子
args[index] = intentProxy;
}
return method.invoke(mInstance, args);
}
});
// 反射调用
fieldMInstance.set(singleton, o);
} catch (Exception e) {
e.printStackTrace();
}
}
}
到这里我们就完成了宿主启动插件activity时宿主activity对插件activity的替换,瞒过AMS的注册检测!AMS检测结束后跨进程调用ActivityThread启动activity,这时候我们再把intent替换回来,把宿主activity替换成插件的activity!
再分析后半部分逻辑
跟上面一样先找hook点
AMS检测结束后会调用ActivityThread的scheduleLaunchActivity()方法
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
和上面一样,继续找intent,很明显此处没有,咱们从这里深入
sendMessage(H.LAUNCH_ACTIVITY, r);
此处是sendMessage,H 是 Handler ,若是这样咱们可以看看Handler的处理
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
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, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
}
此处依旧没有intent,咱们继续深入**handleLaunchActivity()**的实现
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
performPauseActivityIfNeeded(r, reason);
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManager.getService()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
此处依旧没有,继续深入performLaunchActivity(r, customIntent);
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
...
...
}
成功找到intent
ComponentName component = r.intent.getComponent();
r中有intent,r 是 ActivityClientRecord ,找到最初赋值的地方
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
// r 在这里被赋值, msg.obj 中含有 intent
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
既然如此,看下Handler的源码
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
// 注意!!!创建的 mCallback 返回的必须是 false !!!
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handleMessage方法是必须重写的,在dispatchMessage方法中,如果mCallback不为空,则会先执行mCallback.handleMessage(msg),而ActivityThread的Handler调用的是默认的构造方法,默认构造方法callback为null
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
总结:这样是不是就有思路了。创建mCallBack替换系统的,从而拿到msg.obj,这样一来就可以拿到intent了,有了intent岂不是为所欲为。把插件的Activity替换回来,大功告成!
后半部分的代码
public class HookUtil {
。。。
。。。
public static void hookHandler() {
try {
// 获取 ActivityThread 类的 Class 对象
Class<?> clazz = Class.forName("android.app.ActivityThread");
// 获取 ActivityThread 对象
Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
activityThreadField.setAccessible(true);
Object activityThread = activityThreadField.get(null);
// 获取 mH 对象
Field mHField = clazz.getDeclaredField("mH");
mHField.setAccessible(true);
final Handler mH = (Handler) mHField.get(activityThread);
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
// 创建的 callback
Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
// 通过msg 可以拿到 Intent,可以换回执行插件的Intent
// 找到 Intent的方便替换的地方 --- 在这个类里面 ActivityClientRecord --- Intent intent 非静态
// msg.obj == ActivityClientRecord
switch (msg.what) {
case 100:
try {
Field intentField = msg.obj.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
// 启动代理Intent
Intent proxyIntent = (Intent) intentField.get(msg.obj);
// 启动插件的 Intent
Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
if (intent != null) {
intentField.set(msg.obj, intent);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
case 159:
try {
// 获取 mActivityCallbacks 对象
Field mActivityCallbacksField = msg.obj.getClass()
.getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);
for (int i = 0; i < mActivityCallbacks.size(); i++) {
if (mActivityCallbacks.get(i).getClass().getName()
.equals("android.app.servertransaction.LaunchActivityItem")) {
Object launchActivityItem = mActivityCallbacks.get(i);
// 获取启动代理的 Intent
Field mIntentField = launchActivityItem.getClass()
.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);
// 目标 intent 替换 proxyIntent
Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
if (intent != null) {
mIntentField.set(launchActivityItem, intent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
// 必须 return false
return false;
}
};
// 替换系统的 callBack
mCallbackField.set(mH, callback);
} catch (Exception e) {
e.printStackTrace();
}
}
}
写在最后的思考
插件化最麻烦的不是业务,而是对Android版本升级的维护!一直要盯着Android源码的变动,对不同版本进行适配。Hook点不是唯一的,选的时候尽量选静态变量,这样能保证变量的唯一性。如果选择的hook点不是静态变量,万一执行的时候没被执行呢?前半部分主要是寻找hook点、反射、动态代理,顺便把原本的intent存起来。前半部分写完后面就简单多了,没有动态代理,主要是反射!
插件化一定要做好Android版本适配!
本模块还没解决资源的相关问题,插件调用资源的代码一定要注释
setContentView(R.layout.activity_main);
本篇文章还没写完,后续更新!