Android Hook之拦截Activity的启动


Android中的插件化和热修复已经应用了很多年了,这里面涉及的主要就是Hook,即反射 + 代理的技巧去实现一些常规代码无法实现的功能.

之前有说过Hook点击事件的方式,这里讲一下拦截Activity的Hook方法,这也是插件化的基础.

前提

在进行Hook之前,我们必须熟悉源码,找到可以反射的的点,然后进行替换,这里要拦截Activity的启动,那么肯定得看下Activity的启动流程.具体的就不说了,其实最后会执行到Instrumentation中的execStartActivity方法.

有下面这些点我们可以知道:

  • 应用层可以访问到Instrumentation类,因此可以实现一个它的子类,重写一些方法进行替换
  • ActivityActivityThread中持有了Instrumentation对象,字段名为mInstrumentation,因此可以利用反射获取到这个字段
  • 启动Activity肯定会调用到Instrumentation#execStartActivity方法,因此这个就是替换点了

看到上面的3个点,思路基本上就清晰了.从Activity或者ActivityThread中反射拿到mInstrumentation字段,然后替换成自己的Instrumentation实现,重写execStartActivity方法,然后在应用代码中进行替换.

源码

其实有两种思路,一个是从Activity入手,一个是从ActivityThread入手.

Activity入手

    fun hookInstrumentationWithActivity(sourceActivity:Activity, callback: ActivityStartingCallback){
        val instrumentationField = Activity::class.java.getDeclaredField("mInstrumentation")
        if (!instrumentationField.isAccessible){
            instrumentationField.isAccessible = true
        }
        val originInstrumentation:Instrumentation = instrumentationField.get(sourceActivity) as Instrumentation
        instrumentationField.set(sourceActivity,ProxyInstrumentation(originInstrumentation,callback))
    }

这种方式,需要传入Activity对象,并不是很实用.

ActivityThread入手

    fun hookInstrumentation(callback: ActivityStartingCallback) {
        //获取ActivityThread类
        val activityThreadClass = Class.forName("android.app.ActivityThread")
        val currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread")
        currentActivityThreadMethod.isAccessible = true
        val currentActivityThread = currentActivityThreadMethod.invoke(null)
        val mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation")
        mInstrumentationField.isAccessible = true
        val mInstrumentation = mInstrumentationField[currentActivityThread] as Instrumentation
        val proxyInstrumentation: Instrumentation = ProxyInstrumentation(mInstrumentation,callback)
        //替换
        mInstrumentationField[currentActivityThread] = proxyInstrumentation
    }

这种方式不需要传入Activity,直接在Application初始化过程中注入即可.

源码地址

https://github.com/jiangkang/KTools


文章作者: 姜康
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 姜康 !
评论
 上一篇
文件压缩格式 文件压缩格式
zlibzlib 是一个压缩,解压数据的工具库,只是单纯的数据压缩,不管数据来源。 gzipgzip 只能压缩单个文件 tartar 压缩多个文件,通常与gzip配合使用。比如linux上常见的. tar. gz zipzip 用于压缩多个
2020-08-22
下一篇 
Android Studio查看和调试AOSP源码 Android Studio查看和调试AOSP源码
之前说过使用VSCode阅读AOSP源码的方法,但是作为Android开发,还是对Android Studio熟悉一些,这里看下如何使用Android Studio查看AOSP源码. 如果你之前没完整的编译过AOSP,可以按照下面的流程进行
2020-08-21
  目录