2、 未在androidmanifest.xml中注册如何启动对应Activity
在启动Activity中出现ActivityNotFoundException有两种可能,一种是该Activity的应用未安装,一种是该Activity未在androidmanifest中申明。在插件化中,属于情况一。所以在startActivity中需启动Activity实际是系统中不能找到的,我们又该如何骗过系统,正常启动插件中的Activity呢?这里就要引入hook的机制了,我们用一个正常的Activity代替这个插件Activity骗过系统服务,然后再在自己的进程中拉起插件Activity,而这个Activity就像一个壳一样,帮助我们完成这次欺骗活动。
然后hook的地方需要注意了,在startActivity的流程中找一个单例,确保这个单例对象在每次startActivity都会被用上,而不会被其他对象代替。在这里,我们看到流程中ActivityManager里刚好有个单例(基于Android O框架)
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;
}
};
我们想办法把IActivityManagerSingleton.get() hook掉,来看下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;
}
}
}
到这里可以分析得到,我们只要把mInstance 替换掉就可以了。如下:
public void hookActivityManager() {
try {
Class activityManager = Class.forName("android.app.ActivityManager");
Field singleton = activityManager.getDeclaredField("IActivityManagerSingleton");
singleton.setAccessible(true);
Object object = singleton.get(null);
Class single = Class.forName("android.util.Singleton");
Field instance = single.getDeclaredField("mInstance");
instance.setAccessible(true);
Object am = instance.get(object);
Class<?> IActivityInterface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(mContext.getClassLoader(), new Class<?>[]{IActivityInterface}, new ActivityInvokeHanlder(am));
instance.set(object, proxy);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
到这里虽然我们startActivity中虽然指定的是插件Activity,但是在流程中我们用一个壳Activity代替了,系统认为可以正常处理它。
private class ActivityInvokeHanlder implements InvocationHandler {
private Object mBase;
public ActivityInvokeHanlder(Object object) {
mBase = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("startActivity")) {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
Intent intent = (Intent) args[i];
if (intent.getComponent() == null) {
break;
}
if (LoadDexUtils.PLUG_ACTIVITY_NAME[0].equals(intent.getComponent().getClassName())
|| LoadDexUtils.PLUG_ACTIVITY_NAME[1].equals(intent.getComponent().getClassName())) {
Log.i(TAG, "invoke = " + intent.getComponent().getClassName());
String plugActivity = intent.getComponent().getClassName();
intent = new Intent(mContext, mForkActivity);
intent.putExtra(LoadDexUtils.EXTRA_PLUG_ACTIVITY, plugActivity);
args[i] = intent;
}
}
}
}
return method.invoke(mBase, args);
}
}
到这里,我们开始传的intent对应Activity虽然是插件中的Activity,但是在把它交给系统之前,我们做了一个偷梁换柱的动作,告诉系统一个已经在androidmanifest中注册的Activity。最终系统经过检测,这是一个合法的Intent。