1.DroidPlugin介绍
DroidPlugin 是Andy Zhang在Android系统上实现了一种新的 插件机制 :它可以在无需安装、修改的情况下运行APK文件,此机制对改进大型APP的架构,实现多团队协作开发具有一定的好处。
具体介绍可以详见官方github介绍,这里不再赘述
https://github.com/DroidPluginTeam/DroidPlugin/blob/master/readme_cn.md
2.Hook机制
2.1 类图
2.2 关键类介绍
Hook
抽象基类,定义了hook需要的一些基本操作,每一个被hook的类会对应一个Hook具体类。
ProxyHook
ProxyHook extends Hook implements InvocationHandler
ProxyHook在Hook类的基础上实现了InvocationHandler接口,增加了动态代理相关的操作。一般Hook具体类都需要动态代理,所以一般都会直接继承于ProxyHook
BaseHookHandle
Handle是“处理”的意思,所以BaseHookHandle定义了具体的hook操作。每一个Hook具体类会包含一个BaseHookHandle具体类作为类成员,BaseHookHandle具体类里面定义了该Hook具体类需要进行的具体的hook操作
HookedMethodHandler
如果你要hook一个类,这个类里面有多个方法需要被hook,这时候每个方法会对应一个HookedMethodHandler类来定义如何去hook该方法。因为BaseHookHandle是定义具体的hook操作的类,所以BaseHookHandle里会包含一个HookedMethodHandler的Map.
2.3 举个栗子 Hook PackageManager
IPackageManagerHook
public class IPackageManagerHook extends ProxyHook {
@Override
protected BaseHookHandle createHookHandle() {
return new IPackageManagerHookHandle(mHostContext);
}
}
首先会对应一个Hook具体类 IPackageManagerHook ,IPackageManagerHook类里会包含一个BaseHookHandle具体类 IPackageManagerHookHandle用来处理具体的hook细节
IPackageManagerHookHandle
IPackageManagerHookHandle用来处理具体的hook细节,在init方法里添加了所有被hook的方法对应的HookedMethodHandler对象
@Override
protected void init() {
sHookedMethodHandlers.put("getPackageInfo", new getPackageInfo(mHostContext));
sHookedMethodHandlers.put("getPackageUid", new getPackageUid(mHostContext));
sHookedMethodHandlers.put("getPackageGids", new getPackageGids(mHostContext));
sHookedMethodHandlers.put("currentToCanonicalPackageNames", new currentToCanonicalPackageNames(mHostContext));
//……
}
分析HookedMethodHandler -- 以被Hook的 "checkSignatures"方法为例
首先看看HookedMethodHandler
public class HookedMethodHandler {
private static final String TAG = HookedMethodHandler.class.getSimpleName();
protected final Context mHostContext;
private Object mFakedResult = null;
private boolean mUseFakedResult = false;
public HookedMethodHandler(Context hostContext) {
this.mHostContext = hostContext;
}
public synchronized Object doHookInner(Object receiver, Method method, Object[] args) throws Throwable {
long b = System.currentTimeMillis();
try {
mUseFakedResult = false;
mFakedResult = null;
/*
子类可以重写beforeInvoke在原始方法被调用之前执行某些操作,
如果返回true,说明这件事我来处理了,原始方法你不要管了,此时原始方法就不会被调用,
如果返回false,原始方法会被调用
*/
boolean suc = beforeInvoke(receiver, method, args);
Object invokeResult = null;
if (!suc) {
invokeResult = method.invoke(receiver, args);
}
/*
子类可以重写afterInvoke在原始方法被调用之后执行某些操作,
*/
afterInvoke(receiver, method, args, invokeResult);
//mUseFakedResult 为 true 说明 该方法的返回值使用我伪造的结果--mFakedResult
if (mUseFakedResult) {
return mFakedResult;
} else {//mUseFakedResult 为 false 说明 该方法的返回值使用调用原始方法的返回结果--invokeResult
return invokeResult;
}
} finally {
long time = System.currentTimeMillis() - b;
if (time > 5) {
Log.i(TAG, "doHookInner method(%s.%s) cost %s ms", method.getDeclaringClass().getName(), method.getName(), time);
}
}
}
public void setFakedResult(Object fakedResult) {
this.mFakedResult = fakedResult;
mUseFakedResult = true;
}
/**
* 在某个方法被调用之前执行,如果返回true,则不执行原始的方法,否则执行原始方法
*/
protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
return false;
}
protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
}
public boolean isFakedResult() {
return mUseFakedResult;
}
public Object getFakedResult() {
return mFakedResult;
}
}
checkSignatures
private class checkSignatures extends HookedMethodHandler {
public checkSignatures(Context context) {
super(context);
}
@Override
protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
//API 2.3, 4.01_r1, 4.0.3_r1, 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
/* public int checkSignatures(String pkg1, String pkg2) throws android.os.RemoteException;*/
//上面的注释是作者写的被hook的方法的声明
final int index0 = 0, index1 = 1;
String pkg0 = null, pkg1 = null;
if (args != null && args[index0] != null && args[index0] instanceof String) {
pkg0 = (String) args[index0];
}
if (args != null && args[index1] != null && args[index1] instanceof String) {
pkg1 = (String) args[index1];
}
if (!TextUtils.isEmpty(pkg0) && !TextUtils.isEmpty(pkg1)) {
PluginManager instance = PluginManager.getInstance();
//如果包名是我们的插件apk的包名,才需要进行hook
if (instance.isPluginPackage(pkg0) && instance.isPluginPackage(pkg1)) {
//调用了instance.checkSignatures来进行签名检测
int result = instance.checkSignatures(pkg0, pkg1);
//设置伪造的结果 这样checkSignatures方法的返回值会使用这个伪造的结果
setFakedResult(result);
//返回true,说明这件事我来处理了,原始方法你不要管了,此时原始方法就不会被调用
return true;
}
}
//如果包名不是我们的插件apk的包名,比如是宿主的包名,此时就调用super.beforeInvoke 直接返回false,
// 此时会调用原始的方法,并且使用原始方法的返回值作为返回值,就跟该方法没有被hook一样
return super.beforeInvoke(receiver, method, args);
}
}
HookedMethodHandler.doHookInner方法在哪被调用呢,请继续看下节。
ProxyHook -- 实现动态代理
public abstract class ProxyHook extends Hook implements InvocationHandler {
protected Object mOldObj;
public ProxyHook(Context hostContext) {
super(hostContext);
}
/**
* 设置被代理的原始的对象
*/
public void setOldObj(Object oldObj) {
this.mOldObj = oldObj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//isEnable 返回false 说明该hook被设置为不生效,此时直接在mOldObj上执行该方法,返回
if (!isEnable()) {
return method.invoke(mOldObj, args);
}
//mHookHandles里查找该方法对应的HookedMethodHandler对象
HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
//如果有 就执行hookedMethodHandler.doHookInner方法来具体实现对该方法的hook
if (hookedMethodHandler != null) {
return hookedMethodHandler.doHookInner(mOldObj, method, args);
}
//如果没有,说明该方法不需要被hook,直接在mOldObj上执行该方法,返回
return method.invoke(mOldObj, args);
} catch (Exception e) {
//一些异常处理 略
}
}
}
IPackageManagerHook --用代理对象替换原始的PackageManager对象
public class IPackageManagerHook extends ProxyHook {
private static final String TAG = IPackageManagerHook.class.getSimpleName();
public IPackageManagerHook(Context hostContext) {
super(hostContext);
}
@Override
protected BaseHookHandle createHookHandle() {
return new IPackageManagerHookHandle(mHostContext);
}
@Override
protected void onInstall(ClassLoader classLoader) throws Throwable {
Object currentActivityThread = ActivityThreadCompat.currentActivityThread();
//从主线程对象里通过反射拿到sPackageManager对象,作为原始对象赋值给mOldObj
setOldObj(FieldUtils.readField(currentActivityThread, "sPackageManager"));
Class> iPmClass = mOldObj.getClass();
//生成代理对象
List> interfaces = Utils.getAllInterfaces(iPmClass);
Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
Object newPm = MyProxy.newProxyInstance(iPmClass.getClassLoader(), ifs, this);
//用代理对象替换原始对象
FieldUtils.writeField(currentActivityThread, "sPackageManager", newPm);
//调用宿主的context的getPackageManager获取PackageManager对象
PackageManager pm = mHostContext.getPackageManager();
Object mPM = FieldUtils.readField(pm, "mPM");
//如果该对象不是我们的代理对象,就把该对象也替换成我们的代理对象
if (mPM != newPm) {
FieldUtils.writeField(pm, "mPM", newPm);
}
}
}
IPackageManagerHook对象的onInstall方法会在插件框架被安装的时候调用。
被Hook的checkSignatures方法被调用的完整过程
至此,整个Hook的过程就分析完了。