快醒醒吧!安卓程序员必备hook技术之进阶篇

#引子

Hook技术在android开发领域算是一项黑科技,那么一个新的概念进入视线,我们最关心的3个问题就是,它是什么,有什么用,怎么用

本系列将由浅入深 手把手讲解这三大问题

本文是第二篇,进阶篇

上一篇文章中,用了一个最最简单的案例 讲解hook是个什么玩意. 咱不能老玩低端,来点复杂的吧。Activity的启动流程,做安卓开发的人都是绕不开它的,但是要真正知悉其源码逻辑,还是不太容易.

#鸣谢

翻了很多关于hook Activity启动流程的博客,这位大佬的文章给我的启发最大
https://blog.csdn.net/gdutxiaoxu/article/details/81459910
但是,可能大佬的博文对于有些基础不足的初中级安卓工程师或者并不熟悉hook的某些大佬 还不够友好,所以我把大佬的思想用更通俗,更具象化的方式再展示一遍,并且提供可运行的github demo.并且,阅读源码的时候一些坑,我都会详细给出解决方案。

#正文大纲

一. 两种启动Activity的方式源码追踪
二. 第一种启动方式的hook方案
三. 第二种启动方式的hook方案
四. 目前方案弊端分析
五. 最终解决方案
六. HOOK开发可能的坑

Demo地址

https://github.com/18598925736/ActivityHookDemo

#正文

##一. 两种启动Activity的方式源码追踪

(源码基于 SDK 28 ~ android-9.0)

方式1:使用Activity自带的startActivity

示例代码

private void startActivityByActivity() {
   
        Intent i = new Intent(MainActivity.this, Main2Activity.class);
        startActivity(i);
    }

程序执行走向图.

代码追踪:


这里有个if(mParent==null)判定,先看true分支:

发现一个坑,mInstrumentation.execStartActivity 这里居然不能继续往下索引了?很奇怪,不过不重要,我们直接进入Instrumentation.java去找这个方法:


在这个execStartActivity中,可以找到关键代码

int result = ActivityManager.getService()
           .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                   token, target != null ? target.mEmbeddedID : null,
                  requestCode, 0, null, options);
checkStartActivityResult(result, intent);

通过这种方式启动Activity,最终的执行权被交给了 ActivityManager.getService()(即AMS),它的作用是 启动一个Activity并且返回result,然后checkStartActivityResult(result, intent);这句话,对当前的跳转意图intent进行检测;

have you declared this activity in your AndroidManifest.xml 这句异常应该很熟悉了吧?启动一个没有注册的Activity的报错.

再看个if(mParent==null)false分支:



控制权依然是交给了mInstrumentation.execStartActivity(),剩余的代码索引和上面的一样.

所以,代码索引的结论,按照一张图来表示就是:

方式2:使用applictonContextstartActivity

private void startActivityByApplicationContext() {
        Intent i = new Intent(MainActivity.this, Main2Activity.class);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        getApplicationContext().startActivity(i);
    }

方式1 中已经展示了源码索引的方式,所以这里不再赘述贴图.直接给出代码索引结论图:

两张图对比,我们很容易得出一个结论:
启动Activity的最终执行权,都被交给了 Instrumentation.java 类,
方式1:Activity.startActivity的最终执行者是 它的mInstrumentation成员,mInstrumentation的持有者是 Activity自身.
方式2:getApplicationContext().startActivity(i); 的最终执行者是:ActivityThreadmInstrumentation成员,持有者是ActivityThread 主线程.
两种方式都可以把mInstrumentation当作hook切入点,将它从它的持有者中"偷梁换柱".

下面开始动手尝试:

##二. 第一种启动方式的hook方案

创建一个HookActivityHelper.java ,然后三步走:

  1. 找到hook点,以及hook对象的持有者,上文中已经说明:hook点是ActivitymInstrumentation成员,持有者就是Activity
      Field mInstrumentationField = Activity.class.getDeclaredField("mInstrumentation");
      mInstrumentationField.setAccessible(true);
      Instrumentation base = (Instrumentation) mInstrumentationField.get(activity);

base是系统原来的执行逻辑,存起来后面用得着.

  1. 创建Instrumentation代理类, 继承Instrumentation然后,重写execStartActivity方法,加入自己的逻辑,然后再执行系统的逻辑.
private static class ProxyInstrumentation extends Instrumentation {
   
        public ProxyInstrumentation(Instrumentation base) {
   
            this.base = base;
        }

        Instrumentation base;

        public ActivityResult execStartActivity(
                Context who, IBinder contextThread, IBinder token, Activity target,
                Intent intent, int requestCode, Bundle options) {
   

            Log.d("ProxyInstrumentation", "我们自己的逻辑");

            //这里还要执行系统的原本逻辑,但是突然发现,这个execStartActivity居然是hide的,只能反射咯
            try {
   
                Class<?> InstrumentationClz = Class.forName("android.app.Instrumentation");
                
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值