红橙Darren视频笔记 启动不在清单文件注册的activity 安卓8有效

本文主要是介绍红橙Darren视频笔记 启动不在清单文件注册的activity 安卓8有效,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考链接

https://www.jianshu.com/p/aa03c4458b9a
https://www.jianshu.com/p/af148ab5ddf7

前提:

了解InvocationHandler的使用以及Activity的启动流程
另外还需要对反射比较了解
如果不是很了解可以参考如下两篇文章
https://blog.csdn.net/u011109881/article/details/117263548
https://blog.csdn.net/u011109881/article/details/117716367

思路:

activity的启动流程中有两个重要的部分 一个是检查startActivity中传递的intent的有效性,其中会检测activity是否在清单文件中注册。
第二个是launchActivity 通过ActivityThread内部的handler处理各种msg 其中有一项是起到启动activity的作用的
我们的思路是
1.在第一步中ActivityManager.startActivity中检测有效性的时候,偷偷将原来的intent替换掉,传递给系统一个空壳activity的intent 该activity什么都没有 仅仅是在清单文件注册过,我们将真正想要启动的activity的intent附加在空壳intent中
2.在ActivityThread中,将系统的Handler多进行一层包装 拦截在系统的handler之前处理launchActivity的事件 将之前偷换的intent再换回去,最终达到启动不在清单文件注册的activity的目的

coding部分:

创建三个activity 其中有两个是在清单文件注册过的,我们想打开的是那个没有注册的activity

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void jump(View view) {Intent intent = new Intent(MainActivity.this, SecondActivity.class);startActivity(intent);}
}/*** Created by hjcai on 2021/6/9.* 临时Activity 已经在清单文件注册* 用于糊弄intent的检测流程*/
public class TempActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_temp);}
}/*** Created by hjcai on 2021/6/9.* <p>* 真正想要启动的Activity*/
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);}
}//清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.hookstartactivity"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:name=".MainApplication"android:theme="@style/Theme.LearnEassyJoke"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name=".TempActivity" /><!--        <activity android:name=".SecondActivity"/>--></application></manifest>

准备工作做得差不多了 下面创建工具类 主要作用有两个
1.拦截startActivity(通过InvocationHandler+反射实现)

    public static void hookStartActivity() throws Exception {// 查看API28的API源码 发现启动activity时// 1.ActivityManager.getService().startActivity方法内部 进行了intent可用性检查// 2.而真正启动activity是在ActivityThread handleMessage(EXECUTE_TRANSACTION)的时候// 在第一步中 我们需要先使用临时的空壳activity的intent来通过检测// 在第二步中 我们需要取出handleMessage里面我们塞进去的壳activity的intent 替换为我们真正想要启动的activity的intent/* 以下仅仅涉及第一步 大致思路* 我们需要拦截IActivityManager原先实现者的startActivity方法* IActivityManager的原先实现者是android.util.Singleton里面的mInstance对象* 因此我们的核心是取得mInstance对象* 取得过程:* 反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象* 再从IActivityManagerSingleton中取得mInstance对象*/try {// 1.反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象Object defaultSingleton = getIActivityManagerSingleton();if (defaultSingleton == null) {Log.e(TAG, "hookStartActivity: failed");return;}// 反射android.util.Singleton对象Class<?> singletonClazz = Class.forName("android.util.Singleton");// 2.从IActivityManagerSingleton中取得mInstance对象Field instanceField = singletonClazz.getDeclaredField("mInstance");instanceField.setAccessible(true);Object activityManagerInstance = instanceField.get(defaultSingleton);// mInstance不是静态变量 需要从具体实例中获取对象 这里是defaultSingleton获取// Proxy 的三个参数// 1.class loader// 2.需要代理的接口类// 3.实现InvocationHandler的类 InvocationHandler中包含真正想要执行接口方法的对象// ActivityManager.getService().startActivity内部拦截intent 在InvocationHandler中 将其intent替换成我们空壳activity的intentClass<?> interfaceActivityManager = Class.forName("android.app.IActivityManager");Object proxy = Proxy.newProxyInstance(HookStartActivityUtil.class.getClassLoader(),new Class[]{interfaceActivityManager},new HookInvocationHandler(activityManagerInstance));//替换为代理类IActivityManagerProxyinstanceField.set(defaultSingleton, proxy);} catch (ClassNotFoundException e) {e.printStackTrace();}}private static Object getIActivityManagerSingleton() {try {// 反射获取ActivityManager实例Class<?> activityManagerClazz = Class.forName("android.app.ActivityManager");// 拿到其中的静态变量 IActivityManagerSingletonField field = activityManagerClazz.getDeclaredField("IActivityManagerSingleton");field.setAccessible(true);return field.get(null);// IActivityManagerSingleton是static变量 field.get(null)可以获取IActivityManagerSingleton对象} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}return null;}/*** Created by hjcai on 2021/6/10.* * 拦截startActivity方法 达到偷梁的目的*/
public class HookInvocationHandler implements InvocationHandler {private static final String TAG = "HookInvocationHandler";public static final String REQUEST_TARGET_INTENT_KEY = "REQUEST_TARGET_INTENT_KEY";// 代理ActivityManagerprivate final Object mActivityManager;public HookInvocationHandler(Object activityManagerServiceClass) {this.mActivityManager = activityManagerServiceClass;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Log.e(TAG, method.getName());if (method.getName().equals("startActivity")) {Log.e(TAG, "invoke: startActivity....");// 拦截startActivityIntent intent;int index = 0;// 查找intent在startActivity方法中是第几个参数 由于Android版本更新 参数index可能变化for (int i = 0, length = args.length; i < length; i++) {if (args[i] instanceof Intent) {index = i;break;}}//获取一开始设置进去的真正想要进入activity的intentintent = (Intent) args[index];//创建占坑Activity的IntentIntent subIntent = new Intent();subIntent.setClassName("com.example.hookstartactivity", "com.example.hookstartactivity.TempActivity");// 保存插件Activity的IntentsubIntent.putExtra(REQUEST_TARGET_INTENT_KEY, intent);// 借尸还魂中的借尸 由于想要打开的activity没有注册 这里替换为已经注册的TempActivity 真正想要打开的activity的intent放在intent的bundle里面args[index] = subIntent;}return method.invoke(mActivityManager, args);}
}

2.拦截launchActivity(通过对ActivityThread中的handler进行二次封装+反射实现)

    public static void hookLaunchActivity() throws Exception {try {Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");Field currentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");currentActivityThreadField.setAccessible(true);Object currentActivityThread = currentActivityThreadField.get(null);Field handlerField = activityThreadClazz.getDeclaredField("mH");handlerField.setAccessible(true);Handler mH = (Handler) handlerField.get(currentActivityThread);Field callbackField = Handler.class.getDeclaredField("mCallback");callbackField.setAccessible(true);//Handler的mCallback替换为CallBackProxycallbackField.set(mH, new CallBackProxy(mH));} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}/*** Created by hjcai on 2021/6/11.* * 在launchActivity前拦住 达到换柱的目的*/
public class CallBackProxy implements Handler.Callback {private final Handler mHandler;public CallBackProxy(Handler handler) {this.mHandler = handler;}@Overridepublic boolean handleMessage(Message msg) {if (msg.what == 100) {Object o = msg.obj;try {Field field = o.getClass().getDeclaredField("intent");field.setAccessible(true);//获取占坑Activity的IntentIntent intent = (Intent) field.get(o);//获取之前保存的插件Activity的IntentIntent targetIntent = intent.getParcelableExtra(REQUEST_TARGET_INTENT_KEY);//将占坑的Activity替换为插件Activityif (targetIntent == null) {return false;}// 借尸还魂中的还魂intent.setComponent(targetIntent.getComponent());} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}mHandler.handleMessage(msg);return true;}
}

完整工具类代码

/*** Created by hjcai on 2021/6/9.*/
public class HookStartActivityUtil {private static final String TAG = "HookStartActivityUtil";Context mContext;Class<?> mTempClazz;public HookStartActivityUtil(Context context, Class<?> tempClazz) {mContext = context;mTempClazz = tempClazz;}public static void hookStartActivity() throws Exception {// 查看API28的API源码 发现启动activity时// 1.ActivityManager.getService().startActivity方法内部 进行了intent可用性检查// 2.而真正启动activity是在ActivityThread handleMessage(EXECUTE_TRANSACTION)的时候// 在第一步中 我们需要先使用临时的空壳activity的intent来通过检测// 在第二步中 我们需要取出handleMessage里面我们塞进去的壳activity的intent 替换为我们真正想要启动的activity的intent/* 以下仅仅涉及第一步 大致思路* 我们需要拦截IActivityManager原先实现者的startActivity方法* IActivityManager的原先实现者是android.util.Singleton里面的mInstance对象* 因此我们的核心是取得mInstance对象* 取得过程:* 反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象* 再从IActivityManagerSingleton中取得mInstance对象*/try {// 1.反射android.app.ActivityManager对象 取得其中的IActivityManagerSingleton对象Object defaultSingleton = getIActivityManagerSingleton();if (defaultSingleton == null) {Log.e(TAG, "hookStartActivity: failed");return;}// 反射android.util.Singleton对象Class<?> singletonClazz = Class.forName("android.util.Singleton");// 2.从IActivityManagerSingleton中取得mInstance对象Field instanceField = singletonClazz.getDeclaredField("mInstance");instanceField.setAccessible(true);Object activityManagerInstance = instanceField.get(defaultSingleton);// mInstance不是静态变量 需要从具体实例中获取对象 这里是defaultSingleton获取// Proxy 的三个参数// 1.class loader// 2.需要代理的接口类// 3.实现InvocationHandler的类 InvocationHandler中包含真正想要执行接口方法的对象// ActivityManager.getService().startActivity内部拦截intent 在InvocationHandler中 将其intent替换成我们空壳activity的intentClass<?> interfaceActivityManager = Class.forName("android.app.IActivityManager");Object proxy = Proxy.newProxyInstance(HookStartActivityUtil.class.getClassLoader(),new Class[]{interfaceActivityManager},new HookInvocationHandler(activityManagerInstance));//替换为代理类IActivityManagerProxyinstanceField.set(defaultSingleton, proxy);} catch (ClassNotFoundException e) {e.printStackTrace();}}private static Object getIActivityManagerSingleton() {try {// 反射获取ActivityManager实例Class<?> activityManagerClazz = Class.forName("android.app.ActivityManager");// 拿到其中的静态变量 IActivityManagerSingletonField field = activityManagerClazz.getDeclaredField("IActivityManagerSingleton");field.setAccessible(true);return field.get(null);// IActivityManagerSingleton是static变量 field.get(null)可以获取IActivityManagerSingleton对象} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}return null;}public static void hookLaunchActivity() throws Exception {try {Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");Field currentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");currentActivityThreadField.setAccessible(true);Object currentActivityThread = currentActivityThreadField.get(null);Field handlerField = activityThreadClazz.getDeclaredField("mH");handlerField.setAccessible(true);Handler mH = (Handler) handlerField.get(currentActivityThread);Field callbackField = Handler.class.getDeclaredField("mCallback");callbackField.setAccessible(true);//Handler的mCallback替换为CallBackProxycallbackField.set(mH, new CallBackProxy(mH));} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}}

最后在application中初始化工具类并调用两个hook方法

public class MainApplication extends Application {@Overridepublic void onCreate() {super.onCreate();HookStartActivityUtil hookStartActivityUtil =new HookStartActivityUtil(this, TempActivity.class);try {hookStartActivityUtil.hookStartActivity();hookStartActivityUtil.hookLaunchActivity();} catch (Exception e) {e.printStackTrace();}}
}

最终效果:
在这里插入图片描述
布局文件我就不贴出来了,最后我的代码仅在Android 8.0(API27) 是work的 Android9.0(API28)是不work的
从https://blog.csdn.net/u011109881/article/details/117716367 的分析可知 Android 9.0取消了msg 100 并通过各种TransactionItem来执行activity的生命周期 因此显然我在代码中拦截msg 100 在9.0是无法生效的。

这篇关于红橙Darren视频笔记 启动不在清单文件注册的activity 安卓8有效的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1016223

相关文章

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

浅谈主机加固,六种有效的主机加固方法

在数字化时代,数据的价值不言而喻,但随之而来的安全威胁也日益严峻。从勒索病毒到内部泄露,企业的数据安全面临着前所未有的挑战。为了应对这些挑战,一种全新的主机加固解决方案应运而生。 MCK主机加固解决方案,采用先进的安全容器中间件技术,构建起一套内核级的纵深立体防护体系。这一体系突破了传统安全防护的局限,即使在管理员权限被恶意利用的情况下,也能确保服务器的安全稳定运行。 普适主机加固措施:

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识