一. 什么是 Hook
Hook 英文翻译过来就是「钩子」的意思,那我们在什么时候使用这个「钩子」呢?在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序,包括应用触发事件和后台逻辑处理,也是根据事件流程一步步地向下执行。而「钩子」的意思,就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件。
Hook 的这个本领,使它能够将自身的代码「融入」被勾住(Hook)的程序的进程中,成为目标进程的一个部分。API Hook 技术是一种用于改变 API 执行结果的技术,能够将系统的 API 函数执行重定向。在 Android 系统中使用了沙箱机制,普通用户程序的进程空间都是独立的,程序的运行互不干扰。这就使我们希望通过一个程序改变其他程序的某些行为的想法java基于什么基础不能直接实现,但是 Hook 的出现给我们开拓了解决此类问题的道路。
什么是沙箱机制
沙箱是一个虚拟系统程序,沙箱提供的环境相对于每一个运行的程序的进程空间都是独立的,程序的运行互不干扰,而且不会对现有的系统产生影响。
二、Hook 分类
1、根据Android开发模式
2、根 Hook 对象与 Hook 后处理事件方式
3、针对Hook的不同进程上来说
四. API Hook 原理
通过对 Android 平台的虚拟机注入与 Java 反射的方式,来改变 Android 虚拟机调用函数的方式(ClassLoader),从而达到 Java 函数重定向的目的,这里我们将此类操作称为 Java API Hook。
基础知识
由此可见,Hook的基础知识,是反射及基于反射的动态代理
- 反射
反射(Reflection)是什么呢?
反射有时候也被称为内省(Introspection),事实上,反射,就是一种内省的方式,Java不允许在运行时改变程序结构或类型变量的结构,但它允许在运行时去探知、加载、调用在编译期完全未知的class,可以在运行时加载该class,生成实例对象(instance object),调用method,或对field赋值。这种类似于“看透”了class的特性被称为反射(Reflection),我们可以将反射直接理解为:可以看到自己在水中的倒影,这种操作与直接操作源代码效果相同,但灵活性高得多。
在之前学习热更新的时候有介绍过反射,详见 Android热更新二:理解Java反射 。
- java 的动态代理
首先了解一些代理模式的定义。
为其他对象提供一种代理以控制这个对象的访问。
从代码的角度来分,代理可以分为两种:一种是静态代理,另一种是动态代理。
之前讲设计模式的时候,也讲过动态代理,详见 Android常见设计模式五:代理模式。
五、Hook Activity 的 startActivity
原理知道后,还是实战来得舒畅,下面以startActivity启动一个activity为例,在Activity中启动另一个Activity,首先我们需要了解activity的启动流程,一步步跟踪,发现最终在Activity.startActivityForResult中以如下方式启动:
如果你对Activity启动熟悉的话会发现,此处的mInstrumentation就是ActivityThread通过ativity.attach传过来的,而ActivityThread一个app唯一的,而mInstrumentation就是在ActivityThread创建后马上创建的,此时,是不是感觉这个mInstrumentation符合hook点,ok,先hook一把
其中InstrumentProxy如下:
此处,先获取ActivityThread中的mInstrumentation属性,然后再采用静态代理将其替换掉,这样就hook住系统的方法,我们也就可以在InstrumentProxy中任意插桩。
倘若你没有发现mInstrumentation符合hook点,你可以继续跟踪Instrumentation.execStartActivity方法,里面有个非常明显的hook点:
对,就是ActivityManagerNative.getDefault(),我们可以进入ActivityManagerNative类,发现getDefault方法实现如下:
其中gDefault是个static属性,完全符合hook要求,具体hook如下:
其中ProxyHandler如下:
采用动态代理的方法对IActivityManager进行了接管,同样完成了startActivity的hook。
其实不选单例、静态属性或共有属性,整个private的也是可以的,还以启动startActivity为例,考虑到Activity是继承ContextWrapper,而ContextWrapper中有个属性mBase,如果我们能对mBase hook也是可以的,这样就需要对ContextImpl来个代理就可以了,代码可以如下:
这样尽管可以,但是启动activity就需要注意了,只要能走到mBase.startActivity(intent)接口才生效,如果没走它,就hook失效啰,所以hook点选择很关键,尽管都hook到了东西,但是是不是hook住了全部,还需要验证。
六、Hook View 的 OnClickListener
下面通过 Hook View 的 OnClickListener 来说明 Hook 的使用方法。
首先进入 View 的 setOnClickListener 方法,我们看到 OnClickListener 对象被保存在了一个叫做 ListenerInfo 的内部类里,其中 mListenerInfo 是 View 的成员变量。ListeneInfo 里面保存了 View 的各种监听事件,比如 OnClickListener、OnLongClickListener、OnKeyListener 等等。
我们的目标是 Hook OnClickListener,所以就要在给 View 设置监听事件后,替换 OnClickListener 对象,注入自定义的操作。
到这里,我们成功 Hook 了 OnClickListener,在点击之前和点击之后可以执行某些操作,达到了我们的目的。下面是调用的部分,在给 Button 设置 OnClickListener 后,执行 Hook 操作。点击按钮后,日志的打印结果是:Before click → onClick → After click。
七、使用 Hook 拦截应用内的通知
当应用内接入了众多的 SDK,SDK 内部会使用系统服务 NotificationManager 发送通知,这就导致通知难以管理和控制。现在我们就用 Hook 技术拦截部分通知,限制应用内的通知发送操作。
发送通知使用的是 NotificationManager 的 notify 方法,我们跟随 API 进去看看。它会使用 INotificationManager 类型的对象,并调用其 enqueueNotificationWithTag 方法完成通知的发送。
INotificationManager 是跨进程通信的 Binder 类,sService 是 NMS(NotificationManagerService) 在客户端的代理,发送通知要委托给 sService,由它传递给 NMS,具体的原理在这里不再细究,感兴趣的可以了解系统服务和应用的通信过程。
我们发现 sService 是个静态成员变量,而且只会初始化一次。只要把 sService 替换成自定义的不就行了么,确实如此。下面用到大量的 Java 反射和动态代理,特别要注意代码的书写。
Hook 的时机还是尽量要早,我们在 attachBaseContext 里面操作。
这样我们就完成了对通知的拦截,可见 Hook 技术真的是非常强大,好多插件化的原理都是建立在 Hook 之上的。
八、结语
以上,我们知道,hook技术涉及到的知识点主要有反射、代理及android的一些底层知识,如果想要较好地掌握好hook相关的内容,就需要花更多的时间去学习和总结。
下面总结一下注意点:
- Hook 的选择点:静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
- Hook 过程:
寻找 Hook 点,原则是静态变量或者单例对象,尽量 Hook public 的对象和方法。
选择合适的代理方式,如果是接口可以用动态代理。
偷梁换柱——用代理对象替换原始对象。
- Android 的 API 版本比较多,方法和类可能不一样,所以要做好 API 的兼容工作。
另外,市面上游几个比较成熟的Hook 方案,如果有需要大量使用此技术的不妨参考参考:
- Xposed
- Cydia Substrate
Cydia Substrate 框架为苹果用户提供了越狱相关的服务框架,当然也推出了 Android 版 。Cydia Substrate 是一个代码修改平台,它可以修改任何进程的代码。不管是用 Java 还是 C/C++(native代码)编写的,而 Xposed 只支持 Hook app_process 中的 Java 函数。
- Legend
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/20469.html