专栏名称: 开发者全社区
分享和推送Java/Android方向的技术和文章,让你成为这方面的大牛,让你每天都成长一点。同时,我们也会邀请BAT的大牛分享原创!
目录
相关文章推荐
郭霖  ·  2024年终总结,花开终有时 ·  4 天前  
鸿洋  ·  Android AMS 自述 ·  4 天前  
郭霖  ·  原创:写给初学者的Jetpack ... ·  5 天前  
51好读  ›  专栏  ›  开发者全社区

Android插件化Hook技术之---Activity的启动过程拦截

开发者全社区  · 公众号  · android  · 2017-04-19 10:22

正文

相关阅读:

吊炸天!74款APP完整源码!

Android经久不衰最受欢迎的开源库整理,你一定用过10个以上,架构师必备

【干货】支付宝App架构揭秘—使用的开源组件总结!

作者:LooperJing
简书:http://www.jianshu.com/p/69bfbda302df

这篇文章主要讲解如何利用动态代理技术Hook掉系统的AMS服务,来实现拦截Activity的启动流程。代码量不是很多,为了更容易的理解,需要掌握JAVA的反射,动态代理技术,以及Activity的启动流程。

如果对上面的知识点有些遗忘,建议点击文末相关链接阅读,否则跳过。

1、寻找Hook点的原则

Android中主要是依靠分析系统源码类来做到的,首先我们得找到被Hook的对象,我称之为Hook点;什么样的对象比较好Hook呢?一般来说,静态变量和单例变量是相对不容易改变,是一个比较好的hook点,而普通的对象有易变的可能,每个版本都不一样,处理难度比较大。我们根据这个原则找到所谓的Hook点。

2、寻找Hook点

通常点击一个Button就开始Activity跳转了,这中间发生了什么,我们如何Hook,来实现Activity启动的拦截呢?

    public void start(View view) {
        Intent intent = new Intent(this, OtherActivity.class);
        startActivity(intent);
    }

我们的目的是要拦截startActivity方法,跟踪源码,发现最后启动Activity是由Instrumentation类的execStartActivity做到的。其实这个类相当于启动Activity的中间者,启动Activity中间都是由它来操作的

对于ActivityManagerNative这个东东,熟悉Activity/Service启动过程的都不陌生

继承了Binder,实现了一个IActivityManager接口,这就是为了远程服务通信做准备的"Stub"类,一个完整的AID L有两部分,一个是个跟服务端通信的Stub,一个是跟客户端通信的Proxy。ActivityManagerNative就是Stub,阅读源码发现在ActivityManagerNative 文件中还有个ActivityManagerProxy,这里就多不扯了。

static public IActivityManager getDefault() {      
      return gDefault.get();  }

ActivityManagerNative.getDefault()获取的是一个IActivityManager对象,由IActivityManager去启动Activity,IActivityManager的实现类是ActivityManagerService,ActivityManagerService是在另外一个进程之中,所有Activity 启动是一个跨进程的通信的过程,所以真正启动Activity的是通过远端服务ActivityManagerService来启动的。

其实gDefalut借助Singleton实现的单例模式,而在内部可以看到先从ServiceManager中获取到AMS远端服务的Binder对象,然后使用asInterface方法转化成本地化对象,我们目的是拦截startActivity,所以改变IActivityManager对象可以做到这个一点,这里gDefault又是静态的,根据Hook原则,这是一个比较好的Hook点

3、Hook掉startActivity,输出日志

我们先实现一个小需求,启动Activity的时候打印一条日志。

结合注释应该很容易看懂,在Application中配置一下

看看执行结果:


可以看到,我们成功的Hook掉了startActivity,输出了一条日志。有了上面的基础,现在我们开始来点有用的东西,Activity不用在清单文件中注册,就可以启动起来,这个怎么搞呢?

4、无需注册,启动Activity

如下,TargetActivity没有在清单文件中注册,怎么去启动TargetActivity?

   public void start(View view) {
        Intent intent = new Intent(this, TargetActivity.class);
        startActivity(intent);
    }

这个思路可以是这样,上面已经拦截了启动Activity流程,在invoke中我们可以得到启动参数intent信息,那么就在这里,我们可以自己构造一个假的Activity信息的intent,这个Intent启动的Activity是在清单文件中注册的,当真正启动的时候(ActivityManagerService校验清单文件之后),用真实的Intent把代理的Intent在调换过来,然后启动即可。

首先获取真实启动参数intent信息

有了上面的两个步骤,这个代理的Intent是可以通过ActivityManagerService检验的,因为我在清单文件中注册过

      name=".ProxyActivity" />

为了不启动ProxyActivity,现在我们需要找一个合适的时机,把真实的Intent换过了来,启动我们真正想启动的Activity。看过Activity的启动流程的朋友,我们都知道这个过程是由Handler发送消息来实现的,可是通过Handler处理消息的代码来看,消息的分发处理是有顺序的,下面是Handler处理消息的代码:

handler处理消息的时候,首先去检查是否实现了callback接口,如果有实现的话,那么会直接执行接口方法,然后才是handleMessage方法,最后才是执行重写的handleMessage方法,我们一般大部分时候都是重写了handleMessage方法,而ActivityThread主线程用的正是重写的方法,这种方法的优先级是最低的,我们完全可以实现接口来替换掉系统Handler的处理过程。这里详见Android源码解析Handler系列第(一)篇 --- Message全局池

自定义Callback类

最后在application中注入

执行,点击MainActivity中的按钮成功跳转到了TargetActivity。这是插件化系列博客的第一篇。

如果对上面的知识点有些遗忘,建议按需扫读下面三篇文章。(点击“阅读原文”)

养成好的阅读习惯,没事多点点广告,活动下筋骨!


看完本文有收获?请分享给更多人

Java和Android架构

欢迎关注我们,一起讨论技术,扫描和长按下方的二维码可快速关注我们。搜索微信公众号:JANiubility。

公众号:JANiubility

推荐文章
郭霖  ·  2024年终总结,花开终有时
4 天前
鸿洋  ·  Android AMS 自述
4 天前
中央戏剧学院就业创业指导中心  ·  校园招聘会单位发布之二八:中公教育
7 年前
互联网分析师  ·  荷兰人发明走路自行车,走一步飞10米
7 年前