首页 > 代码库 > Android系统篇之----免root实现Hook系统服务拦截方法
Android系统篇之----免root实现Hook系统服务拦截方法
技术概念来源:[ 360开源插件框架,项目地址:https://github.com/DroidPluginTeam/DroidPlugin ]
一、Binder机制回顾
在之前一篇文章中介绍了 Android中的Binder机制和系统远程服务调用机制,本文将继续借助上一篇的内容来实现Hook系统服务拦截指定方法的逻辑,了解了上一篇文章之后,知道系统的服务其实都是一个远程Binder对象,而这个对象都是由ServiceManager大管家管理的,用户在使用系统服务的时候,会通过指定服务的Stub方法的asInterface把远程的Binder对象转化成本地化对象即可使用,而在这个过程中,我们也知道因为系统服务是在system_server进程中的,所以这个系统服务使用过程中属于跨进程调用,那么返回的对象其实就是Proxy代理对象。
二、系统中服务使用流程
本文主要就是借助这个知识点,通过Hook系统的服务来拦截服务方法,下面我们就通过系统剪切板服务案例作为分析
这里看到了,使用系统服务的时候都是用了getSystemService方法,通过定义在Context中的服务描述符常量来获取服务对象,而getSystemService方法定义在ComtextImpl.java类中:
这里维护了一个ServiceFetcher的Map结构,看看这个结构在哪里填充数据的:
在registerService方法中添加一个服务名称和一个ServiceFetcher对象,而这个方法在静态代码块中进行调用的:
我们找到了ClipboardManager这个服务:
这里其实是一个ClipboardManager对象,其实这个对象是内部封装了IClipboard.Stub功能,可以看看其他的服务:
比如这里的联网服务,直接调用了IConnectivityManager.Stub类的asInterface方法获取Proxy对象。
下面就进去ClipboardManager.java中看看究竟:
看到这里的设置剪切板内容的方法,其实内部是调用了getService方法获取对象然后在调用指定方法,那么可以大概知道了这个getService方法返回的应该就是IClipboard.Stub通过asInterface方法返回的Proxy对象:
好吧,果然是这样,这里通过ServiceManager获取到Clipboard的远端IBinder对象,然后通过asInterface方法返回一个Proxy对象即可。
到这里我们就简单的分析完了系统中的获取剪切板的服务,其实系统中的服务都是这么个逻辑,只是有的可能会在外面包装一层罢了,下面总结一下流程:
现在只要记住一点:每次获取系统服务的流程都是一样的,先通过ServiceManager的getService方法获取远端服务的IBinder对象,然后在通过指定服务的Stub类的asInterface方法转化成本地可使用对象,而这个对象其实就是一个Proxy对象,在这个过程中,Stub类继承了Binder对象和实现了AIDL接口类型,Proxy对象实现了AIDL接口类型,而AIDL接口类型实现了IInterface接口类型。
三、Hook系统服务
上面分析完了Android中系统服务的使用流程以及原理解析,下面在来看一下Android中实现Hook机制的方法和原理解析,我们知道其实在很多系统中都存在这样一个Hook技术,有的也叫作钩子,但是不管任何系统,Hook技术的核心点都是一样的,只有两点即可完成Hook技术:
1、找到Hook点,即你想Hook哪个对象,那么得先找到这个对象定义的地方,然后使用反射获取到这个对象实例。所以这里可以看到,一般Hook点都是一个类的单例方法或者是静态变量,因为这样的话Hook起来就非常方便,都是static类型,反射调用都比较方便无需具体的实例对象即可。而关于这个点也是整个Hook过程中最难的点,因为很难找到这个点。Android中主要是依靠分析系统源码类来做到的。
2、构造一个Hook原始对象的代理类,关于这个代理其实在Java中有两种方式,一种是静态代理,一种是动态代理。
静态代理:代理类中维护一个原始对象的成员变量,每个方法调用之前调用原始对象的方法即可。无需任何条件限制
动态代理:比静态代理复杂点就是有一个规则:就是原始对象必须要实现接口才可以操作,原理是因为动态代理其实是自动生成一个代理类的字节码,类名一般都是Proxy$0啥的,这个类会自动实现原始类实现的接口方法,然后在使用反射机制调用接口中的所有方法。
案例使用就很简单了,下面我们通过源码分析这个原理:
主要调用了getProxyClass0方法生成代理类的字节码:
然后生成代理类:
看到这里,其实这里的生成字节码不是很复杂的,我们可以借助asm等工具包就可以手动的生成一个字节码,然后在使用类加载器加载即可,所以会发现需要传递类加载器。
当上面生成代理类之后,我们可以把这个类反编译看一下,看一下方法的调用,在代理类中的静态代码块:
会使用反射获取到类的所有方法。
然后在指定方法中调用传递进来的InvocationHandler的回调接口的invoke方法,就是这么实现的。
从源码角度分析完动态代理之后,发现其实没什么复杂的,就是手动的生成一个代理类,然后用反射获取类中所有的方法,在指定方法中用反射调用,同时回调InvocationHandler的invoke方法即可,所以这里看到InvocationHandler中的invoke方法的第一个参数对象是代理类对象。
注意:
在JavaWeb开发中我们会用到Spring框架中的AOP编程,其实他就是利用了Java中动态代理技术,但是他依赖的是CGlib的功能,不是采用Java原生的这种自动产生代理类,从上面可以看到能够做动态代理的类必须要实现一个接口,而这一个规则有时候要求非常高,很多类都没有实现接口,导致无法实现代理功能,所以Spring采用了cglib,原理其实差不多,他采用asm工具包也是手动生成一个类,但是这个类是原始对象的子类,这样也可以实现代理功能,但是这样一来也有一个限制了,那就是代理对象不能是final类型,同时一些主要方法最好不能是final类型的,当然这个限制和上面的那个接口实现限制比起来还是好点的。
到此我们了解了Java中的Hook技术的核心知识点了,下面就用开始的剪切板服务来做实验,我们Hook系统的剪切板服务功能,拦截其方法,上面也说道了,既然要Hook服务,首先得找到Hook点,通过开始对Android中系统服务的调用流程分析知道,其实这些服务都是一些保存在ServiceManager中的远端IBinder对象,这其实是一个Hook点:
其实ServiceManager中每次在获取服务的时候,其实是先从一个缓存池中查找,如果有就直接返回了:
而这个缓存池正好是全局的static类型,所以就可以很好的使用反射机制获取到他了,然后进行操作了。
接下来,我们就需要构造一个剪切板的服务IBinder对象了,然后在把这个对象放到上面得到的池子中即可。那么按照上面的动态代理的流程,
第一、原始对象必须实现一个接口,这里也正好符合这个规则,每个远程服务其实是实现了IBinder接口的。
第二、其次是要有原始对象,这个也可以,通过上面的缓存池即可获取
有了这两个条件那么接下来就可以使用动态代理构造一个代理类了:
通过反射去获取ServiceManager中的缓存池Binder对象就不多说了,完全反射机制即可,我们先获取到缓存池,然后得到剪切板服务Binder对象,构造一个代理类,最后在设置回去即可。下面主要来看一下构造了代理类之后,如何拦截哪些方法?
这里一定要注意了,有的同学可能想直接在这里拦截setPrimaryClip这样的剪切板方法不就可以了吗?想想是肯定不可以的,为什么呢?因为我们现在代理的是远端服务的Binder对象,他还没有转化成本地对象呢?如何会有这些方法呢,而我们真正要拦截的方法是IClipboardManager,其实就是Proxy类,而这个对象也是Stub类的asInterface方法得到的,所以我们现在的思路是有了远端服务的代理对象,拦截肯定是拦截这个代理对象Binder的一些方法,那么这个远端服务有哪些方法会在这个过程中被调用呢?我们再看看之前的一个简单AIDL的例子:
在asInterface方法中可以看到,传递进来的就是一个远端服务IBinder对象,这里会先调用它的queryLocalInerface方法获取本进程的本地化对象,那么这个方法就是拦截的目标了。
然后在想,我们如果想拦截IClipboardManager的setPrimaryClip方法,其实就是要拦截ClipboardManager$Proxy的这些方法,那么还需要做一次代理,代理ClipboardManager$Proxy类对象
第一、ClipboardManager$Proxy类实现了AIDL接口类型,符合规则。
第二、我们可以直接使用反射获取到IClipboardManager$Stub类,然后反射调用它的asInterface方法就可以得到了IClipboardManager$Proxy对象了,符合规则。
到这里,看来这个对象也符合了代理的条件,那么就简单了,继续使用动态代理机制产生一个代理类即可:
这个代理类的InvocationHandler中,先需要通过反射获取到Proxy原始对象:
最后才开始实现拦截操作。
下面来看一个实验结果:
我们调用系统的剪切板服务,但是返回的结果却是:
看到了,这里的剪切板的内容已经被之前拦截,内容也被替换了。
上面就全部介绍了如何Hook系统的剪切板服务功能,流程如下:
1、我们的目的就是拦截系统的服务功能,那么最开始的入口就是服务大管家ServiceManager对象,而在他内部也正好有一个远端服务对象的IBinder缓存池,那么这个变量就是我们操作的对象了,可以先使用反射机制去获取到他,然后在获取到指定的剪切板服务IBinder对象实例。
2、下一步肯定是Hook这个剪切板服务的Binder对象,这里采用动态代理方式产生一个Binder对象代理类,符合两个规则:
1》这个Binder对象实现了IBinder接口类型
2》我们已经得到了原始的Binder对象实例
构造完代理类之后,我们拦截的方法是queryLocalInterface方法,为什么是这个方法呢?因为在整个服务使用过程中之后在Stub类中使用到了这个方法,很多同学会认为为什么不在这里直接拦截系统方法呢?这是一个误区,要想清楚,这里的代理对象是远程服务的Binder,还不是本地化对象,不能会有哪些系统方法的,所以得再做一次Hook,去Hook住系统的本地化对象。
3、在拦截了Binder对象的queryLocalInterface方法之后,再一次做一下本地化服务对象的代理生成操作,而这个本地化对象一般都是IClipboard$Proxy,那么动态代理的规则:
1》本地化服务对象都会实现AIDL接口类型(这里才有哪些我们想拦截的系统方法)
2》通过反射调用IClipboard$Stub类的asInterface方法得到IClipboard$Proxy类对象实例
符合这两个规则那么就可以产生代理对象了,然后开始拦截服务的指定方法即可。
上面总结的这个流程是可以完全用于其他系统服务的Hook工作的,因为系统服务的机制都是一致的,所以这个流程一定要理解清楚,后面的工作才好进行。
四、补充说明
有的同学可能会好奇询问,这个Hook系统服务貌似只对本应用有效吧?哈哈,的确是这样的,上面的拦截只会对本应用有效,那有的同学会问,只对本应用有效意义就不是很大的,其实这个要看个人需求了,后面会介绍一些开发中遇到的问题,就需要借助这个技术去解决了,但是真正能够拦截系统的服务对所有的应用有效,其实想想实现也不难,因为应用都会请求服务,而所有的服务都在system_server进程中,那么就可以采用root之后,注入system_server进程,那时候在开始Hook工作即可完成真正意义上的拦截操作,这个用途就大了,比如我们可以修改系统的经纬度信息,伪造假的当前位置信息,篡改设备的IMEI值,让有的游戏识别唯一设备无效等。但是这个就需要root了之后才可以操作了,具体方案感兴趣的同学可以去这里看看:Android中注入系统进程实现拦截
五、总结
到这里我们就介绍完了Android中Hook系统服务的流程,本文中主要介绍了Hook系统剪切板服务,拦截指定方法,其实后面还会继续介绍拦截AMS和PMS服务,实现应用启动的拦截操作,达到我们想要的效果。
更多内容:点击这里
关注微信公众号,最新技术干货实时推送
Android系统篇之----免root实现Hook系统服务拦截方法