插件化知识详细分解及原理 之代理,hook,反射
Posted 刘镓旗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插件化知识详细分解及原理 之代理,hook,反射相关的知识,希望对你有一定的参考价值。
上一篇我们说了Binder机制,通过aidl的demo和系统源码的对比进行了运行过程的分析,这一篇我们说代理模式及反射,之前说过了,只是为了梳理插件化需要了解的知识点,所以不会特别深的去讲解。
代理模式:
也叫做委托模式,分为静态代理和动态代理。代理模式也是平时比较常用的设计模式之一,代理模式有代码简洁,高扩展性的特性.主要目的就是为访问者提供一个代理,以达到限制某个对象的访问,也就是说想访问一个对象,其实我给你的是一个代理,不让你直接使用我。估计不理解的人会问为什么使用代理模式,他限制了对象的访问,也就是说不能通过new的方式得到你想要的对象,只能通过访问代理类才能使用,这样的话,我们就实现了内部对象的保护,而且如果有一天我的真实角色因为某个原因换了个名或者换了个方法字段等等,那对外来说一点不影响,因为他拿到的只是代理而已
静态代理,还记的我们上一篇分析的aidl吗,他其实就是一个典型静态代理,我们再看一下代码
public static com.huanju.chajianhuatest.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.huanju.chajianhuatest.IMyAidlInterface))) {
return ((com.huanju.chajianhuatest.IMyAidlInterface) iin);
}
return new com.huanju.chajianhuatest.IMyAidlInterface.Stub.Proxy(obj);
}
如果不是一个进程就返回了一个Stub.Proxy的代理类,如果客户端访问方法的时候,会调用服务端的方法然后将结果返回给客户端,这就是一个代理模式,而且android中的所有系统服务全部使用的这种模式。
动态代理,就是在实现阶段不需要制定代理谁,在运行的时候指定一个代理类,这样就更灵活了,主要实现通过Java提供的InvocationHanler类,
1.写一个类实现InvocationHanlder接口
2.重写接口的invoke方法
3.通过调用Proxy.newProxyInstance(ClassLoader loader, Class[] claz , InvocationHanlder handler)返回一个代理对象;
这里先不给出demo,等说完反射,我们一起实现一个hook系统类的demo,将会用到动态代理及反射
Hook机制
hook,又叫钩子,通常是指对一些方法进行拦截。这样当这些方法被调用时,也能够执行我们自己的代码,这也是面向切面编程的思想(AOP)
android中,本身并不提供这样的拦截机制,但是有时候,我们可以在一些特殊的场合实现一种的Hook方法。
大致思路:
1.找到需要Hook方法的系统类
2.利用代理模式来代理系统类的运行拦截我们需要拦截的方法
3.使用反射的方法把这个系统类替换成你的代理类
反射机制:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
在android中Google很多的类的某些方法不让第三方应用去调用,通过java反射机制能把这些隐藏方法获取出来并调用
相关API:
1.实例化Class对象,有三种方式,
Class.forName(类名全路径); //通过Class的静态方法
对象.getClass() //通过对象.getClass方法
int.class //基本数据类型及基本数据类型的封装了,例如Integer
2.获取父类
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
Class<?> superclass = clazz.getSuperclass();
3.获取实现接口
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
Class<?>[] interfaces = clazz.getInterfaces()
4.获取指定参数构造函数及实例化
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
Constructor<?> constructor = clazz.getConstructor(Class<?> ... class);//获取公共的
Constructor<?> constructor = clazz.getDeclaredConstructor()//获取私有的
constructor.newInstance(Object args);
5.获取所有构造函数及构造参数的类型
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
Constructor<?>[] constructors = clazz.getConstructors();//公共的
Constructor<?>[] constructors = clazz.getDeclaredConstructors()//包括私有的
for (int i = 0; i < constructors.length; i++) {
Class<?> clazzs[] = constructors[i].getParameterTypes();//获取类型
System.out.print("constructors[" + i + "] (");
for (int j = 0; j < clazzs.length; j++) {
if (j == clazzs.length - 1)
System.out.print(clazzs[j].getName());
else
System.out.print(clazzs[j].getName() + ",");
}
System.out.println(")");
}
6.通过无参实例化对象
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
class.newInstance();
7.获取字段,修改字段
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
Field field = clazz.getField(String name);//获取公共字段
Field field = clazz.getDeclaredField(String name);//获取私有公共字段
Field[] field = clazz.getFields();//获取所有公共字段
Field[] field = clazz.getDeclaredFields();//获取包括私有所有字段
Field field = clazz.getDeclaredField("heihei");
field.setAccessible(true);//设置java取消访问检查,也就是说如果是私有的也可以访问,
field.set(obj, "Java反射机制");
8.获取方法,运行方法
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
clazz.getMethod(String name ,Class<?> ... parame);//获取公共指定方法
clazz.getDeclaredMethod(String name ,Class<?> ... parame)//获取私有指定方法
clazz.getMethods()//获取公共所有方法
clazz.getDeclaredMethods();//获取包括私有全部方法
Method method = clazz.getMethod("add");
method.invoke(clazz.newInstance());
method = clazz.getMethod("getInfo", int.class, String.class);
method.setAccessible(true)//设置java取消访问检查,也就是说如果是私有的也可以访问,
method.invoke(clazz.newInstance(), 20, "张三");
9.获取数组或者list中的类型,如果不是数组或集合返回null
Class<?> clazz = Class.forName(类名全路径); //通过Class的静态方法
Class<?> componentType = clazz.getComponentType();
好了我们上面讲代理模式,hook机制,反射大概都梳理了一遍,下面我们将运用这些知识写一个demo,来hook系统类的方法,简单点我们就hook一下剪切板服务的复制粘贴的方法,在系统运行这个方法的时候我们让他使用复制粘贴的都是一句话,当然这里是你可以想干的任何事情。
1.我们首先要知道剪切板服务怎么创建的,要知道他创建过程,我们才能拿到这个服务对象并代理他,
我们知道所有的服务对象都是代理对象,他们都被一个叫ServiceManager的类管理,我们来看一下,平时我们使用系统服务都是通过
Context.getSystemService(Context.CLIPBOARD_SERVICE)方法来获取系统的服务
Context的具体实现类是ContextImpl,这个类在platform_frameworks_base-master\core\java\android\app\ContextImpl
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
到了一个SystemServiceRegistry类中,从一个map中取
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
再看这个map怎么初始化的
registerService(Context.HDMI_CONTROL_SERVICE, HdmiControlManager.class,
new StaticServiceFetcher<HdmiControlManager>() {
@Override
public HdmiControlManager createService() {
IBinder b = ServiceManager.getService(Context.HDMI_CONTROL_SERVICE);
return new HdmiControlManager(IHdmiControlService.Stub.asInterface(b));
}});
看到了一个叫ServiceManager,但是并不是所有的服务都是这么初始化的,比如我们的ActivityManager,他就是返回的这个ActivityManager的包装类
registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
}});
但是,ActivityManager里面所有的核心操作都是使用ActivityManagerNative.getDefault()完成的。那么这个语句干了什么呢?
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
IActivityManager am = asInterface(b);
return am;
}
};
我们看到了其实他最终也是通过ServiceManager.getService来获取的,而且其实获取服务就分为两步
IBinder b = ServiceManager.getService("activity");
IActivityManager am = asInterface(b);
那么我们就先看ServiceManager这个类,这个类在platform_frameworks_base-master\core\java\android\os\ServiceManager
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;
}
看到了他内部也维护着一个map,我们可以通过反射替换这个map里面的内容为Hook过的IBinder对象,由于系统在getService的时候每次都会优先查找缓存,因此返回给使用者的都是被我们修改过的对象,从而实现代理
我们再看一下asInterface方法,之前我们分析过就时aidl中的方法
public static android.content.IClipboard asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // Hook点
if (((iin != null) && (iin instanceof android.content.IClipboard))) {
return ((android.content.IClipboard) iin);
}
return new android.content.IClipboard.Stub.Proxy(obj);
}
这里每次都会看一下本进程中是否存在这个Binder对象,如果有就直接返回了,那么我们想想办法
拦截这个方法让这个方法返回一个我们伪造的系统服务对象来替代系统的
实现代码
- Activity中
>
public class MainActivity extends Activity {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
ClipHelper.binder();
} catch (Exception e) {
e.printStackTrace();
}
EditText editText = new EditText(this);
setContentView(editText);
}
}
- ClipHelper
>
package com.weishu.binder_hook.app.text;
import android.os.IBinder;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
/**
* hook系统剪切板服务
* Created by 刘镓旗 on 2017/1/22.
*/
public class ClipHelper {
public static void binder(){
try {
//1.剪切板服务,是在系统的ServiceManager中的getSerVice方法中得到的,我们先拿到ServiceManager
Class<?> serviceMangerClass = Class.forName("android.os.ServiceManager");
//2.拿到getService方法
Method getServiceMethod = serviceMangerClass.getDeclaredMethod("getService", String.class);
//3.通过这个方法,拿到原本的系统服务代理对象
IBinder binder = (IBinder) getServiceMethod.invoke(null,"clipboard");
//4.我们通过这个对象,创建我们自己的代理对象,瞒天过海骗过系统
IBinder myBinder = (IBinder) Proxy.newProxyInstance(serviceMangerClass.getClassLoader(),
new Class[]{IBinder.class}
,new MyClipProxy(binder)
);
//5.拿到ServiceManager中的数组
Field field = serviceMangerClass.getDeclaredField("sCache");
field.setAccessible(true);
Map<String, IBinder> map = (Map) field.get(null);
//将我们的服务类存入map
map.put("clipboard",myBinder);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- MyClipProxy
s
package com.weishu.binder_hook.app.text;
import android.os.IBinder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* hook系统剪切板服务
* Created by 刘镓旗 on 2017/1/22.
*/
public class MyClipProxy implements InvocationHandler {
private final IBinder mBase;
public MyClipProxy(IBinder binder) {
mBase = binder;//这里传入的是原系统的代理类
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//拦截原系统类查询本地是否有这个代理的方法
if("queryLocalInterface".equals(method.getName())){
//我们这里要创建我们自己的系统类,然后返回
//1.拿到系统的aidl类中的stub,因为这个对象本来就是个代理,而且源码执行了
// static private IClipboard getService() {
// synchronized (sStaticLock) {
// if (sService != null) {
// return sService;
// }
// IBinder b = ServiceManager.getService("clipboard");
// sService = IClipboard.Stub.asInterface(b);
// return sService;
// }
// }
Class<?> mStubClass = Class.forName("android.content.IClipboard$Stub");
//2.在拿到IClipboard本地对象类
Class<?> mIClipboard = Class.forName("android.content.IClipboard");
//3.创建我们自己的代理
return Proxy.newProxyInstance(mStubClass.getClassLoader(),
new Class[]{mIClipboard},
new MyClip(mBase,mStubClass));
}
//不是这个方法还是返回原系统的执行
return method.invoke(mBase,args);
}
}
- MyClip
>
package com.weishu.binder_hook.app.text;
import android.content.ClipData;
import android.os.Binder;
import android.os.IBinder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* hook系统剪切板服务
* Created by 刘镓旗 on 2017/1/22.
*/
public class MyClip implements InvocationHandler {
private Object mBase;
public MyClip(IBinder base, Class stub) {
//拿到asInteface方法,因为源码中执行了这一句,我们也要执行这一句
try {
Method asInterface = stub.getDeclaredMethod("asInterface", IBinder.class);
mBase = asInterface.invoke(null,base);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里我们拦截粘贴的方法,
if("getPrimaryClip".equals(method.getName())){
return ClipData.newPlainText(null,"我是刘镓旗,我改了系统源码,哈哈哈");
}
//再拦截是否有复制的方法,放系统认为一直都有
if("hasPrimaryClip".equals(method.getName())){
return true;
}
//其他启动还是返回原有的
return method.invoke(mBase,args);
}
}
哦了,到这这一篇就说完了,下次我们继续的内容是类加载及dex加载过程
以上是关于插件化知识详细分解及原理 之代理,hook,反射的主要内容,如果未能解决你的问题,请参考以下文章
Android插件化完美实现代码资源加载及原理讲解 附可运行demo
小白也能看懂的插件化DroidPlugin原理-- 反射机制和Hook入门