代理模式及hook实战

        上一篇大体说了一下Java中的反射技术,这篇正纳闷来讲讲代理模式和hook,最后会实战一下,hook一下Toast。

        代理模式(委托模式)有2种:静态代理和动态代理。其优点是扩展和隐私,对外只提供一个代理访问,只有我自己可以用真正的类做操作。安卓系统中大量用到代理模式,比如获取系统的服务,App进程只能拿到服务的代理对象。

        静态代理,即在编译时已确认自己的任务。

首先定义一个接口:

然后会有个类实现它:

在代理模式中,它将成为被代理类。

然后来看一下代理类

和被代理类实现同一接口,并实现同样的接口,重写方法,在前后加入自己想要插入的逻辑

实际生活中也有很多这样的例子,比如点外卖,告诉我你要个黄焖鸡米饭,ok别管了,剩下的交给我,你等着吃就可以了。

        动态代理,在运行时才知道自己的职责,需要配合Proxy类和InvocationHanlder接口重写invoke方法来实现,成功代理以后,每次调用被代理对象的方法都会被转到InvocationHanlder的invoke方法中,在这里可以通过判断方法名和参数来增加自己的逻辑。

        Hook——即钩子;在技术层面是指hook住某个方法,来达到自己想要的目的,挺着优点高大上,实际却是如此。想成功hook的话一般需要上一篇讲的反射和本篇的代理模式来配合实现。下面咱们先来分析一下安卓的Toast源码,然后通过一个小demo来看清hook和动态代理的真实面目。(分析源码是必须的步骤,虽然枯燥,但是想达到自己的目的也是没办法的事,带着问题去分析会避开很多“岔路口”,防止“走火入魔”)

        这里我们的目的就是代理吐司,让所有用到吐司的地方,都展示同样的内容。我们先来看看平时是怎么用吐司的(源码版本:7.1.1):

        Toast.makeText(MainActivity.this, "要显示的内容",Toast.LENGTH_SHORT).show();

点进makeText方法看看

创建了一个toast然后加载了一个布局,找到布局中的textview将要显示的内容设置给它并把它和显示时间设置给刚刚创建的toast。完了?后边还有个show方法,不妨看看它?

看到getService,一般安卓里都是获取远程服务的,先放这,看一下后边的,拿到一个TN的玩意,然后调用服务的enqueueToast;TN是在前边创建Toast的时候创建的

看看它是个啥玩意儿?

别的先不看,看一下它继承了一个ITransienNotification.Stub,分析过安卓源码的看到这一般就会明白,又涉及到跨进程通信了;实现接口重写其方法,等远程调用。接下来我们回过头看一下上边等getService

       

通过ServiceManager获取服务,返回了一个INotificationManager.Stub;它的实现类在NotificationManagerService里:

主要逻辑并不多,大体就是看是不是有马上要显示的吐司,有就安排显示,没有马上就要显示的,就创建了ToastRecord,并存起来。

indexOfToastLocked方法。

最后调用到showNextToastLocked方法

主要是record.callback.show(record.token)这一行,这个call是一个ITransienNotification类型的参数,还记得前边分析的Toast里的一个内部类TN吗?就是它,它实现了ITransienNotification这个接口:

也就是说又通过跨进程通信,调用到TN这个累不累的show方法

通过Handler发送了一个消息,来看一下这个Handler

就一个方法,直接调用handleShow方法

主要就是获取WindowManager,来添加我们要显示的吐司View;展示完成也是通过相同的方式handler发送消息,然后removeView这里就不贴代码了。

Toast源码到这基本流程就分析完了,下面咱们分析一下要怎么hook它。首先hook的最佳点是static的变量或单例,因为它一但被确认后一般不会被修改。那咱们这里就锁定在Toast中的getServicer方法,它被保存在static修饰的变量,然后接管它的enqueue方法,因为它有个参数是ITransientNotification,在toast类中它就是tn,它有个变量是个testview,我们修改它的内容就可以了。上代码~

通过反射获取到toast中的getService方法,然后实现我们的代理,最后再把它设置给toast中的sService。然后是我们的代理类:

实现InvocationHander重写invoke方法,这样每次调用sService中的方法都会转到我们invoke方法里,我们只拦截enqueueToast方法,然后替换里边的TN里的textview的内容即可。最后还想说一句,在看源码的时候,不要忽略源码中的提示信息。

        本篇内容结束。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值