如何绕过清单文件,动态注入activity

本文探讨了如何在Android中绕过清单文件动态注入Activity,以实现插件开发。通过分析Activity的实例化过程,了解到Instrumentation在其中的作用。接着介绍了如何偷梁换柱,包括绕过未注册Activity的校验和利用Intent参数来启动插件Activity。最后,详细阐述了如何替换系统方法,用自己的newActivity方法来控制Activity的创建。
摘要由CSDN通过智能技术生成

【Android】Android插件开发 —— 打开插件的Activity(Hook系统方法)


Android打开插件中Activity的实现原理


摘要

Android打开插件Activity的方式有很多种,类名固定的可以使用预注册的方式。代理也是一种很好的方式,同时代理的方式也可以用于打开插件中的Service。

这两种方式都有一些弊端,这篇文章要分享的是如何更好地打开插件中的Activity,采用Instrumentation注入的方式。


1. Activity是如何被实例化的

1.1 在哪一个类、哪一个方法中实例化的

当开发者实现一个Activity时,不能自己添加一个带参数的构造方法。如果添加了,也需要实现一个无参构造方法。原因是在调用Context.startActivity(…)后,系统会利用反射的方式根据activityClass实例化一个Activity对象:

?
1
Activity activity = (Activity)clazz.newInstance();

其中clazz就是传入的Activity的子类。这一行代码是在Instrumentaion.java中的newActivity(…)方法中的。也就是说,Activity的实例化是在Instrumentation这个类里面,通过反射的方式进行的。

1.2 谁持有了Instrumentation的引用

每个Activity对象都持有了一个mInstrumentation的变量,该变量并不是Activity自己创建的,而是由ActivityThread传递给Activity的。而在ActivityThread中,持有了一个名为mInstrumentation的变量。因此,创建Activity的Instrumentation的对象,是被ActivityThread持有着。


2. 如何偷梁换柱打开插件的Activity

2.1 Activity是否在AndroidManifest.xml注册的校验

如果一个Activity没有注册,想要打开它,会抛出异常:

Unable to find explicit activity class XXXXActivity have you declared this activity in your AndroidManifest.xml?

这个异常是Instrumentation的checkStartActivityResult方法中抛出的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/** @hide */
public static void checkStartActivityResult( int res, Object intent) {
     if (res >= ActivityManager.START_SUCCESS) {
         return ;
     }
 
     switch (res) {
         case ActivityManager.START_INTENT_NOT_RESOLVED:
         case ActivityManager.START_CLASS_NOT_FOUND:
             if (intent instanceof Intent && ((Intent)intent).getComponent() != null )
                 throw new ActivityNotFoundException(
                         "Unable to find explicit activity class "
                         + ((Intent)intent).getComponent().toShortString()
                         + "; have you declared this activity in your AndroidManifest.xml?" );
             throw new ActivityNotFoundException(
                     "No Activity found to handle " + intent);
         ...(以下省略)
     }
}

很明显的一件事,插件中的Activity是没有在AndroidManifest.xml中注册的,直接打开肯定抛异常崩溃。读者可能会想:我要是直接重写这个方法,不管什么情况全部不抛异常,校验不就通过了吗?但是很抱歉,这个方法是static类型的,子类无法重写。这个条件我们没有办法解决,只能绕过去。

2.2 如何绕过Activity的注册校验

Instrumentation有一个很重要的事:checkStartActivityResult(…)方法 是在newActivity(…)方法之前执行的。请看这两个信息:

当开发者使用一个已经注册过的Activity去接受校验时,肯定能通过校验; 实例化出来的Activity一定是经过校验的那一个Activity吗?不一定。

让我们看一下Instrumentation#newActivity(…)方法的具体实现(有两个重载实现):
第一个:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Activity newActivity(Class<!--?--> clazz, Context context,
         IBinder token, Application application, Intent intent, ActivityInfo info,
         CharSequence title, Activity parent, String id,
         Object lastNonConfigurationInstance) throws InstantiationException,
         IllegalAccessException {
 
     Activity activity = (Activity)clazz.newInstance();
     ActivityThread aThread = null ;
     activity.attach(context, aThread, this , token, 0 , application, intent,
             info, title, parent, id,
             (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
             new Configuration(), null );
     return activity;
}

第二个:

?
1
2
3
4
5
6
public Activity newActivity(ClassLoader cl, String className,
         Intent intent)
         throws InstantiationException, IllegalAccessException,
         ClassNotFoundException {
     return (Activity)cl.loadClass(className).newInstance();
}

注意参数列表中有一个 Intent intent 参数,这个参数就是我们在startActivity(Intent intent);传入的那个intent。两个重载方法的参数列表中都包含了这个intent。
所以绕过注册验证的思路大致就出来了:

第一步:
Intent intent = new Intent(this, 某个已经注册的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值