最近学习了如何Hook Android中的剪贴板服务,特此写下一篇博客记录。
首先说明下什么是Hook,Hook即通过使用代理对象替换系统原有对象,达到增强或修改系统类的功能的手段。一般我们替换的对象都会选择不易改变的静态对象。
下面首先介绍Android中获取系统服务的步骤,然后再介绍如何Hook 剪贴板服务ClipboardManager。
以下代码版本为Android 4.4
首先我们调用Context的getSystemService方法,由于Context的实际功能实现类为 ContextImpl,故我们追踪ContextImpl的代码:
@Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
private static final HashMap SYSTEM_SERVICE_MAP =
new HashMap();
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
static {
registerService(ALARM_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(ALARM_SERVICE);
IAlarmManager service = IAlarmManager.Stub.asInterface(b);
return new AlarmManager(service, ctx);
}});
registerService(CLIPBOARD_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new ClipboardManager(ctx.getOuterContext(),
ctx.mMainThread.getHandler());
}});
}
ServiceFetcher是ContextImpl的一个内部类,当我们初次调用getService方法时,会调用createService返回结果
SYSTEM_SERVICE_MAP通过静态代码块初始化注册完毕,其中主要的createService返回方式有两种:public class ClipboardManager extends android.text.ClipboardManager {
private static IClipboard sService;
static private IClipboard getService() {
synchronized (sStaticLock) {
if (sService != null) {
return sService;
}
IBinder b = ServiceManager.getService("clipboard");
sService = IClipboard.Stub.asInterface(b);
return sService;
}
}
}
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static IServiceManager sServiceManager;
private static HashMap sCache = new HashMap();
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.content.IClipboard {
private static final java.lang.String DESCRIPTOR = "android.content.IClipboard";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an android.content.IClipboard interface,
* generating a proxy if needed.
*/
public static android.content.IClipboard asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.content.IClipboard))) {
return ((android.content.IClipboard)iin);
}
return new android.content.IClipboard.Stub.Proxy(obj);
}
}
总的来说,Android获取系统服务步骤如下图所示:
其中我们可以进行Hook的点,主要是红色的两个部分:
/**
* hook 方法1
*
* @throws Exception
*/
public static void hook1() throws Exception {
// 加载ClipboardManager类
Class> clipboardManagerClazz = Class
.forName("android.content.ClipboardManager");
// 通过getService static方法获取真实IClipboard对象
Method getServiceMethod = clipboardManagerClazz
.getDeclaredMethod("getService");
getServiceMethod.setAccessible(true);
// 真实IClipboard对象
Object clipboardManager = getServiceMethod.invoke(null);
// 获取sService的IClipboard缓存
Field sServiceFeild = clipboardManagerClazz
.getDeclaredField("sService");
sServiceFeild.setAccessible(true);
// 替换sService
sServiceFeild.set(null, Proxy
.newProxyInstance(clipboardManager.getClass().getClassLoader(),
clipboardManager.getClass().getInterfaces(),
new ClipboardManagerProxyHandler(
clipboardManager)));
}
/**
* Created by superxlcr on 2016/9/20.
* 剪贴板代理处理类
*/
public class ClipboardManagerProxyHandler implements InvocationHandler {
// 真正的clipboardManager
private Object clipboardManager;
public ClipboardManagerProxyHandler(Object clipboardManager) {
this.clipboardManager = clipboardManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
switch (method.getName()) {
case "getPrimaryClip": // 粘贴内容
return ClipData.newPlainText(null, "you are hook!");
case "hasPrimaryClip": // 剪贴板永远有粘贴内容
return true;
}
// 其余情况由真实对象处理
return method.invoke(clipboardManager, args);
}
}
/**
* hook方法2
*
* @throws Exception
*/
public static void hook2() throws Exception {
// 加载ServiceManager类
Class> serviceManagerClazz = Class
.forName("android.os.ServiceManager");
// 获取getService方法
Method getServiceMethod = serviceManagerClazz
.getMethod("getService", String.class);
// 获取真正的clipboardManager对象
IBinder clipboardManagerIBinder = (IBinder) getServiceMethod
.invoke(null, CLIPBOARD);
// 获取sCache HashMap缓存
Field sCacheField = serviceManagerClazz.getDeclaredField("sCache");
// private变量
sCacheField.setAccessible(true);
// static变量
HashMap sCache = (HashMap) sCacheField.get(null);
// 把代理放入缓存
sCache.put(CLIPBOARD, (IBinder) Proxy.newProxyInstance(
clipboardManagerIBinder.getClass().getClassLoader(),
clipboardManagerIBinder.getClass().getInterfaces(),
new ClipboardManagerIBinderProxyHandler(
clipboardManagerIBinder)));
}
/**
* Created by superxlcr on 2016/9/21.
* 剪贴板通信代理处理类
*/
public class ClipboardManagerIBinderProxyHandler implements InvocationHandler {
// 真正的clipboardManagerIBinder
private IBinder clipboardManagerIBinder;
public ClipboardManagerIBinderProxyHandler(
IBinder clipboardManagerIBinder) {
this.clipboardManagerIBinder = clipboardManagerIBinder;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
if (method.getName().equals("queryLocalInterface")) { // 返回伪造代理对象
// 加载IClipboard内部类Stub
Class> IClipboardStubClazz = Class
.forName("android.content.IClipboard$Stub");
// 获取asInterface方法
Method asInterfaceMethod = IClipboardStubClazz
.getMethod("asInterface", IBinder.class);
// 通过asInterface static方法,得到真正IClipboard对象
Object clipboardManager = asInterfaceMethod
.invoke(null, clipboardManagerIBinder);
return Proxy.newProxyInstance(
clipboardManager.getClass().getClassLoader(),
clipboardManager.getClass().getInterfaces(),
new ClipboardManagerProxyHandler(clipboardManager));
}
return method.invoke(clipboardManagerIBinder, args);
}
}