Android插件化架构 - 拦截Activity的启动流程绕过AndroidManifest检测

1. 概述


  了解了Java的动态代理设计模式之后,配合上一期的文章Android插件化架构 - Activity的启动流程分析,那么接下来就需要亲自操刀去拦截Activity的启动流程了。前面好事没少干,那么现在就来干干坏事,到底怎样才能让没有注册的Activity启动不报错呢?答案就是Hook下钩子。
  

2. Hook启动流程


  怎么样去找Hook点是个问题,把钩子下在哪里呢?一般的套路肯定最好是静态,然后是接口,配合反射注入就可以了。Activity启动流程的源码我就不再贴了,如果不了解请移步这里Android插件化架构 - Activity的启动流程分析,我这里直接下钩子。

     /**
     * hook start activity
     */
    public void hookStartActivity() throws Exception{
        // 先获取ActivityManagerNative中的gDefault
        Class<?> amnClazz = Class.forName("android.app.ActivityManagerNative");
        Field defaultField = amnClazz.getDeclaredField("gDefault");
        defaultField.setAccessible(true);
        Object gDefaultObj = defaultField.get(null);

        // 获取Singleton里面的mInstance
        Class<?> singletonClazz = Class.forName("android.util.Singleton");
        Field amsField = singletonClazz.getDeclaredField("mInstance");
        amsField.setAccessible(true);
        Object amsObj = amsField.get(gDefaultObj);

        // 动态代理Hook下钩子
        amsObj = Proxy.newProxyInstance(mContext.getClass().getClassLoader(),
                amsObj.getClass().getInterfaces(),
                new StartActivityInvocationHandler(amsObj));
        // 注入
        amsField.set(gDefaultObj,amsObj);
    }

    /**
     * Start Activity Invocation Handler
     */
    private class StartActivityInvocationHandler implements InvocationHandler{
        private Object mAmsObj;
        public StartActivityInvocationHandler(Object amsObj){
            this.mAmsObj = amsObj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 拦截到所有ActivityManagerService的方法
            Log.e("TAG","methodName"+method.getName());
            return method.invoke(mAmsObj,args);
        }
    }

3. 借尸还魂


  上面我们已经拦截到了Activity的启动了,也能够看到startActivity方法的打印。但是如果不做任何处理还是会蹦,那么我们需要有一个Activity预先在AndroidMnifest.xml中注册一下,它是不怕太阳的,通过它可以做到借尸还魂。

     /**
     * Start Activity Invocation Handler
     */
    private class StartActivityInvocationHandler implements InvocationHandler{
        private Object mAmsObj;
        public StartActivityInvocationHandler(Object amsObj){
            this.mAmsObj = amsObj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 拦截到所有ActivityManagerService的方法
            Log.e("TAG","methodName"+method.getName());
            if(method.getName().equals("startActivity")){
                // 启动Activity的方法,找到原来的Intent
                Intent realIntent = (Intent) args[2];
                // 代理的Intent
                Intent proxyIntent = new Intent();
                proxyIntent.setComponent(new ComponentName(mContext,mProxyActivity));
                // 把原来的Intent绑在代理Intent上面
                proxyIntent.putExtra("realIntent",realIntent);
                // 让proxyIntent去晒太阳,借尸
                args[2] = proxyIntent;
            }
            return method.invoke(mAmsObj,args);
        }
    }

还魂

     /**
     * hook Launch Activity
     */
    public void hookLaunchActivity() throws Exception{
        // 获取ActivityThread
        Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
        Field sCurrentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
        sCurrentActivityThreadField.setAccessible(true);
        Object sCurrentActivityThreadObj = sCurrentActivityThreadField.get(null);
        // 获取Handler mH
        Field mHField = activityThreadClazz.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(sCurrentActivityThreadObj);
        // 设置Callback
        Field callBackField = Handler.class.getDeclaredField("mCallback");
        callBackField.setAccessible(true);
        callBackField.set(mH, new ActivityThreadHandlerCallBack());
    }

    class ActivityThreadHandlerCallBack implements Handler.Callback {

        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == LAUNCH_ACTIVITY) {
                handleLaunchActivity(msg);
            }
            return false;
        }
    }
    // 还魂
    private void handleLaunchActivity(Message msg) {
        // final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
        try {
            Object obj = msg.obj;
            Field intentField = obj.getClass().getDeclaredField("intent");
            intentField.setAccessible(true);
            Intent proxyIntent = (Intent) intentField.get(obj);
            // 代理意图
            Intent originIntent = proxyIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
            if (originIntent != null) {
                // 替换意图
                intentField.set(obj, originIntent);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3. 兼容AppCompatActivity


  继承自Activity是百试百灵,再也不需要在AndroidMnifest中注册了,但是发现继承AppCompatActivity还是会报错,我都不记得当时是怎么解决这个问题的,反正搞了好几天,我选择遗忘那段操蛋的时光。

    // 兼容AppCompatActivity报错问题
    Class<?> forName = Class.forName("android.app.ActivityThread");
    Field field = forName.getDeclaredField("sCurrentActivityThread");
    field.setAccessible(true);
    Object activityThread = field.get(null);
    Method getPackageManager = activityThread.getClass().getDeclaredMethod("getPackageManager");
    Object iPackageManager = getPackageManager.invoke(activityThread);

    PackageManagerHandler handler = new PackageManagerHandler(iPackageManager);
    Class<?> iPackageManagerIntercept = Class.forName("android.content.pm.IPackageManager");
    Object proxy = newProxyInstance(Thread.currentThread().getContextClassLoader(),
        new Class<?>[]{iPackageManagerIntercept}, handler);

    // 获取 sPackageManager 属性
    Field iPackageManagerField = activityThread.getClass().getDeclaredField("sPackageManager");
    iPackageManagerField.setAccessible(true);
    iPackageManagerField.set(activityThread, proxy);

  总算走出了插件化架构的一小步,过程对于一般人来讲还是有点痛苦的,但是结果带来那种成就感还是值得的,后面我们解决一下资源和布局的加载问题,然后介绍一下360开源的插件化框架DroidPlugin,分析一下它的源码就直接拿过用吧。

所有分享大纲:2017Android进阶之路与你同行

视频讲解地址:http://pan.baidu.com/s/1o8bPZ9C

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中,可以使用 ActivityManager 监听 Activity 的启动情况,并在需要的时候拦截某个 Activity 的启动。具体步骤如下: 1. 在 AndroidManifest.xml 中注册一个监听器服务。 ```xml <service android:name=".MyService"> <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> </service> ``` 2. 创建一个 Service 类,并在其中实现一个继承自 Binder 的内部类 MyBinder。 ```java public class MyService extends Service { private MyBinder binder = new MyBinder(); public class MyBinder extends Binder { public void intercept(String packageName, String activityName) { // 在这里实现拦截逻辑 } } @Nullable @Override public IBinder onBind(Intent intent) { return binder; } } ``` 3. 在 MyBinder 的 intercept 方法中,通过 ActivityManager 监听 Activity 的启动情况,并在需要的时候拦截某个 Activity 的启动。 ```java public void intercept(String packageName, String activityName) { final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); activityManager.addOnBackStackChangedListener(new ActivityManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { String currentPackageName = ""; String currentActivityName = ""; if (activityManager.getRunningTasks(1).size() > 0) { ComponentName componentName = activityManager.getRunningTasks(1).get(0).topActivity; currentPackageName = componentName.getPackageName(); currentActivityName = componentName.getClassName(); } if (currentPackageName.equals(packageName) && currentActivityName.equals(activityName)) { // 拦截逻辑 } } }); } ``` 注意,为了保证拦截的准确性,需要在拦截逻辑中判断当前启动的 Activity 是否是需要拦截Activity。在上面的代码中,通过比较包名和类名的方式来判断。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值