实现原理:首先创建一个占位StubActivity,这个Activity必须要添加声明,用来代替目标的Activity,然后在ActivityThread中的Handler回调中替换掉原来的Callback,改为自己的Callback,并在此修改成自己要启动的真正的Activity,最后启动StubActivity,在启动intent里面放进目标intent,等最后取出目标intent替换原来的intent即可。
另一个问题是,如何加载其它apk或dex文件中的activity?其实这个也很简单,先加载dex文件,得到一个ClassLoader,然后用这个类加载器替换LoadedApk中的ClassLoader,然后就能加载dex文件中的activity对象,用完记着换回去。
最后一个问题是,要实现在任何地方都能启动未注册的Activity,当然也包括正常的Activity,要实现的这样的目的,就需要拦截activity启动过程,我们直接hook掉系统的IActivityManager中的启动对象即可,IActivityManager(Android10以后是IActivityTaskManager)中有一个叫做Singleton的对象,这个对象里有个泛型参数,放的是IActivityManager或者IActivityTaskManager对象,替换成我们自己的代理对象,即可拦截系统AMS服务所有调用,比如拦截startActivity等。
完整代码如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {private ClassLoader mDexClassLoader;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.bn_start1).setOnClickListener(this);findViewById(R.id.bn_start2).setOnClickListener(this);HookHelper.getInstance().init(this).setHookEnabled(true);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.bn_start1: //启动本地Activity测试//方式一,主动启动Intent intent = new Intent(this, TargetActivity.class);intent.putExtra("arg1", "123456"); //携带参数测试HookHelper.startActivity(this, intent);//方式二,被动拦截HookHelper.getInstance().hookAMS(true);startActivity(new Intent(this, TargetActivity.class));break;case R.id.bn_start2: //启动远程Activity测试HookHelper.getInstance().hookAMS(false);startRemoteActivity();break;}}/* 启动远程Activity */private void startRemoteActivity() {try {ClassLoader loader = getDexClassLoader();//替换系统默认ClassLoader为加载后的ClassLoaderHookHelper.getInstance().changeClassLoader(loader);//获取远程ActivityClass RemoteActivity = loader.loadClass("com.zwxuf.remotemodule.RemoteActivity");Intent intent = new Intent(this, RemoteActivity);HookHelper.startActivity(this, intent);} catch (Exception e) {e.printStackTrace();}}private ClassLoader getDexClassLoader() {if (mDexClassLoader == null) {try {ApplicationInfo info = getPackageManager().getApplicationInfo("com.zwxuf.remotemodule", 0);mDexClassLoader = new DexClassLoader(info.sourceDir, info.sourceDir + ".tmp", null, getClassLoader());} catch (Exception e) {e.printStackTrace();}}return mDexClassLoader;}
}
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.util.Log;import androidx.annotation.NonNull;import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;public class HookHelper {private static final String TAG = HookHelper.class.getSimpleName();public static HookHelper mInstance;private Object mOrigCallback;private ClassLoader mOrigClassLoader;private Object mLoadedApk;private Handler mHandler = new Handler(Looper.getMainLooper());private boolean mHookAMSEnabled;private Object mOrigAMS;public static HookHelper getInstance() {if (mInstance == null) {mInstance = new HookHelper();}return mInstance;}private HookHelper() {}public HookHelper init(Context context) {try {//保存LoadedApk对象if (mLoadedApk == null) {Field mLoadedApkField = Application.class.getDeclaredField("mLoadedApk");mLoadedApkField.setAccessible(true);mLoadedApk = mLoadedApkField.get((Application) context.getApplicationContext());}} catch (Exception e) {e.printStackTrace();}return this;}/*** 启用禁用hook** @param enabled*/public void setHookEnabled(boolean enabled) {if (enabled == (mOrigCallback != null)) {return;}try {Class<?> ActivityThread = Class.forName("android.app.ActivityThread");Field sCurrentActivityThreadFiled = ActivityThread.getDeclaredField("sCurrentActivityThread");sCurrentActivityThreadFiled.setAccessible(true);Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);Field mHField = ActivityThread.getDeclaredField("mH");mHField.setAccessible(true);Handler mH = (Handler) mHField.get(sCurrentActivityThread);Field mCallbackField = Handler.class.getDeclaredField("mCallback");mCallbackField.setAccessible(true);Object mCallback = mCallbackField.get(mH);if (enabled) {mOrigCallback = mCallback;mCallbackField.set(mH, new ProxyCallback());} else {mCallbackField.set(mH, mOrigCallback);mOrigCallback = null;}} catch (Exception e) {e.printStackTrace();}}private class ProxyCallback implements Handler.Callback {@Overridepublic boolean handleMessage(@NonNull Message msg) {if (msg.what == 100) { // LAUNCH_ACTIVITYtry {Object record = msg.obj;Field intentField = record.getClass().getDeclaredField("intent");intentField.setAccessible(true);Intent intent = (Intent) intentField.get(record);if (intent != null) restoreTargetIntent(intent);} catch (Exception e) {e.printStackTrace();}mHandler.postDelayed(new Runnable() {@Overridepublic void run() {restoreClassLoader(); //恢复类加载器}}, 500);} else if (msg.what == 159) { // LAUNCH_ACTIVITYtry {Object record = msg.obj;Field fCallbacks = record.getClass().getDeclaredField("mActivityCallbacks");fCallbacks.setAccessible(true);List<?> lists = (List) fCallbacks.get(record);if (lists != null) {for (int i = 0; i < lists.size(); i++) {Object item = lists.get(i);Class itemClazz = item.getClass();try {Field mIntent = itemClazz.getDeclaredField("mIntent");mIntent.setAccessible(true);Intent intent = (Intent) mIntent.get(item);if (intent != null) restoreTargetIntent(intent);} catch (Exception e) {e.printStackTrace();}}}} catch (Exception e) {e.printStackTrace();}mHandler.postDelayed(new Runnable() {@Overridepublic void run() {restoreClassLoader(); //恢复类加载器}}, 500);}return false;}}/*** 恢复目标intent** @param intent*/private static void restoreTargetIntent(Intent intent) {Intent targetIntent = intent.getParcelableExtra("targetIntent");if (targetIntent != null) {Parcel parcel = Parcel.obtain();targetIntent.writeToParcel(parcel, 0);parcel.setDataPosition(0);intent.readFromParcel(parcel);parcel.recycle();}}/*** 启动未注册的Activity** @param context* @param intent*/public static void startActivity(Context context, Intent intent) {Intent baseIntent = new Intent(context, StubActivity.class); //占位intentbaseIntent.putExtra("targetIntent", intent); //存储目标intentcontext.startActivity(baseIntent);}public void changeClassLoader(ClassLoader loader) {ClassLoader mOldClassLoader = replaceClassLoader(loader);if (mOrigClassLoader == null) {mOrigClassLoader = mOldClassLoader;}}public void restoreClassLoader() {if (mOrigClassLoader != null) {replaceClassLoader(mOrigClassLoader);mOrigClassLoader = null;}}private ClassLoader replaceClassLoader(ClassLoader loader) {try {Field mClassLoaderField = mLoadedApk.getClass().getDeclaredField("mClassLoader");mClassLoaderField.setAccessible(true);ClassLoader oldClassLoader = (ClassLoader) mClassLoaderField.get(mLoadedApk);mClassLoaderField.set(mLoadedApk, loader);Log.i(TAG, "replaceClassLoader success");return oldClassLoader;} catch (Exception e) {Log.e(TAG, "replaceClassLoader:" + e.toString());}return null;}/*** 拦截AMS服务** @param enabled*/public void hookAMS(boolean enabled) {if (enabled == mHookAMSEnabled) return;try {Class parentClass = Class.forName("android.util.Singleton");Field mField = parentClass.getDeclaredField("mInstance");mField.setAccessible(true);Class hookClass;Object mSingleton;if (Build.VERSION.SDK_INT < 29) {hookClass = Class.forName("android.app.IActivityManager");mSingleton = getAMSingleton();} else {//android10以上hookClass = Class.forName("android.app.IActivityTaskManager");mSingleton = getATMSingleton();}if (enabled) {mOrigAMS = mField.get(mSingleton);InvocationHandlerProxy handlerProxy = new InvocationHandlerProxy(mOrigAMS);Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class<?>[]{hookClass},handlerProxy);mField.set(mSingleton, proxy);}else {mField.set(mSingleton, mOrigAMS);}mHookAMSEnabled = enabled;Log.i(TAG, "hookAMS:" + enabled);} catch (Exception e) {Log.e(TAG, "hookAMS:" + e.toString());}}/*** 代理对象处理器*/public class InvocationHandlerProxy implements InvocationHandler {Object original; //原始对象public InvocationHandlerProxy(Object original) {this.original = original;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (method.getName().equals("startActivity")) {if (args != null && args.length > 0) {int index = -1;for (int i = 0; i < args.length; i++) {if (args[i] instanceof Intent) {index = i;break;}}if (index != -1) {Intent targetIntent = (Intent) args[index];Intent intent = new Intent();intent.putExtra("targetIntent", targetIntent);intent.setClassName("com.zwxuf.mydemo", StubActivity.class.getName());args[index] = intent;Log.i(TAG, "replace intent success");}}}return method.invoke(original, args);}}private Object getAMSingleton() {try {Class<?> CActivityManager = Class.forName("android.app.ActivityManager");Field FSingleton = CActivityManager.getDeclaredField("IActivityManagerSingleton");FSingleton.setAccessible(true);return FSingleton.get(null);} catch (Exception e) {Log.e(TAG, "getSingleton:" + e.toString());}return null;}private static Object getATMSingleton() {try {Class<?> CActivityTaskManager = Class.forName("android.app.ActivityTaskManager");Field FSingleton = CActivityTaskManager.getDeclaredField("IActivityTaskManagerSingleton");FSingleton.setAccessible(true);return FSingleton.get(null);} catch (Exception e) {Log.e(TAG, "getSingleton:" + e.toString());}return null;}}
public class TargetActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_target);Intent intent = getIntent();String value = intent.getStringExtra("arg1");Log.i("WALX", String.valueOf(value));}
}