实现原理:首先创建一个占位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;
@Override
protected 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);
}
@Override
public 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为加载后的ClassLoader
HookHelper.getInstance().changeClassLoader(loader);
//获取远程Activity
Class 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 {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 100) { // LAUNCH_ACTIVITY
try {
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() {
@Override
public void run() {
restoreClassLoader(); //恢复类加载器
}
}, 500);
} else if (msg.what == 159) { // LAUNCH_ACTIVITY
try {
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() {
@Override
public 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); //占位intent
baseIntent.putExtra("targetIntent", intent); //存储目标intent
context.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;
}
@Override
public 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 {
@Override
protected 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));
}
}