android实现启动未声明的Activity

news/2025/1/11 3:49:36/

实现原理:首先创建一个占位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));}
}

 

 


http://www.ppmy.cn/news/665268.html

相关文章

电脑计算机怎么没有桌面显示器,显示器无信号,教您电脑显示屏不显示怎么修复...

电脑使用久了&#xff0c;就什么问题都会碰见的&#xff0c;有的用户是看淡了&#xff0c;可是对于电脑新手来说却是一个不小的打击&#xff0c;如果电脑出现一开机显示器就显示无信号&#xff0c;主机运行应该正常&#xff0c;风扇转着&#xff0c;连接线也重新插拔过&#xf…

开机后,电脑显示屏无信号怎么办?

转自&#xff1a;微点阅读&#xff08;www.weidianyuedu.com&#xff09;微点阅读 - 范文大全 - 免费学习知识的网站 随着使用电脑的用户越来越多&#xff0c;而使用的用户遇到的问题就越多了&#xff0c;而经常用电脑的同学大部分都遇到过电脑显示器无信号的情况吧。其实相比显…

计算机启动后花屏然后无信号,电脑花屏,显示屏突然无信号(黑一下)或者一直黑...

第1步:首先检查电脑的外部接线是否接好,把各个连线重新插一遍,看故障是否排除。 第2步:如果故障依旧,接着打开主机箱查看机箱内有无多余金属物,或主板变形造成的短路,闻一下机箱内有无烧焦的糊味,主板上有无烧毁的芯片,CPU周围的电容有无损坏等。 第3步:如果没有,接着清理主板上…

服务器换了显卡显示屏不亮,显示屏显示无信号 换了个显卡 显示屏不亮了

#注意&#xff1a;风扇转不证明电源没问题&#xff01;低效电源表现就是&#xff1a;风扇转却不能点亮主板... ... 【主机可以开机】&#xff1f;有证明吗&#xff1f;仅仅风扇转不代表主机启动&#xff01; 如果显示环节好的而【显示器不显示】恰恰是主机没启动&#xff01; …

电脑开机了,显示屏无信号怎么办?

随着使用电脑的用户越来越多&#xff0c;而使用的用户遇到的问题就越多了&#xff0c;而经常用电脑的同学大部分都遇到过电脑显示器无信号的情况吧。其实相比显示器没有任何显示而言&#xff0c;电脑显示器无信号的故障更容易解决。下面&#xff0c;小编就来教大家如何去处理电…

电脑开机后,显示屏无信号怎么处理?

转自&#xff1a;微点阅读 https://www.weidianyuedu.com 随着使用电脑的用户越来越多&#xff0c;而使用的用户遇到的问题就越多了&#xff0c;而经常用电脑的同学大部分都遇到过电脑显示器无信号的情况吧。其实相比显示器没有任何显示而言&#xff0c;电脑显示器无信号的故障…

VueX笔记

vuex是vue的一个插件&#xff0c;是一种组间通信的方式&#xff0c;整个项目可以共享数据和方法 安装&#xff1a; npm i vuex3 如上图所示&#xff0c;如果需求非常简单&#xff0c;我们就可以绕过dispatch方法&#xff0c;直接去调用commit方法 dispatch方法用于编写业务代…

2022壬寅虎年春节记

春节总览[编年体流水账] 大年27&#xff1a; 还没放假&#xff0c;最后一天上课&#xff0c;但完全学不下去&#xff0c;看了《喜剧之王》、《功夫》&#xff0c;拍得真好大年28&#xff1a; 配眼镜&#xff0c;顺便去了爷爷家 然后去姥爷家喝羊汤&#xff0c;姥爷说不用拿东西…