最近学习了如何Hook Android中的剪贴板服务,特此写下一篇博客记录。
首先说明下什么是Hook,Hook即通过使用代理对象替换系统原有对象,达到增强或修改系统类的功能的手段。一般我们替换的对象都会选择不易改变的静态对象。
下面首先介绍Android中获取系统服务的步骤,然后再介绍如何Hook 剪贴板服务ClipboardManager。
以下代码版本为Android 4.4
首先我们调用Context的getSystemService方法,由于Context的实际功能实现类为 ContextImpl,故我们追踪ContextImpl的代码:
1 |
|
ContextImpl去检查了一个系统服务的缓存HashMap SYSTEM_SERVICE_MAP,如下所示(以下仅截取关键代码):
1 | private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = |
ServiceFetcher是ContextImpl的一个内部类,当我们初次调用getService方法时,会调用createService返回结果
SYSTEM_SERVICE_MAP通过静态代码块初始化注册完毕,其中主要的createService返回方式有两种:
- 返回xxxManager(我们这次实验的剪贴板就是这种形式),其中一般在内部会有接口的缓存,初次获取缓存的方式与下一种方法相同
- 调用ServiceManager的getService方法返回一个通信用的IBinder,通过IInterface(即IxxxManager)的内部类Stub的asInterface方法转换为可用的接口(可能是实体也是能是代理)
ClipboardManager的关键代码如下:
1 | public class ClipboardManager extends android.text.ClipboardManager { |
ClipboardManager实际服务的提供都会使用getService来调用。
先来看看ServiceManager的getService方法:
1 | public final class ServiceManager { |
该方法首先回去检查sCache的缓存service,如果没有则会调用IServiceManager去获取真正的Service服务。
接下来asInterface的关键代码如下(IClipboard.Stub类,实际就是AIDL自动生成的类文件):
1 | /** Local-side IPC implementation stub class. */ |
在asInterface方法中,首先调用了IBinder对象的queryLocalInterface方法来查找是否本地含有此接口(如果是同一进程就含有),如果不是则返回代理对象。
总的来说,Android获取系统服务步骤如下图所示:
其中我们可以进行Hook的点,主要是红色的两个部分:
- Hook xxxManager中的sService服务缓存
- 利用sCache缓存表Hook SystemService中getService返回的IBinder对象
方法一的代码如下:
1 | /** |
Java动态代理的InvocationHandler接口如下:
1 | /** |
在这里我们替换掉了,判断剪贴板是否有内容的hasPrimaryClip方法与返回剪贴内容的getPrimaryClip方法,使剪贴板粘贴总是返回you are hook
方法二代码如下所示:
1 | /** |
其中对IBinder的动态代理如下:
1 | /** |
这里我们修改了queryLocalInterface方法,使其返回我们代理的IClipboard接口对象,其余部分与方法一相同。
最后附上该工程的地址,有兴趣的同学可以下载看看:https://github.com/superxlcr/ClipboardManagerHook