1.DroidPlugin介紹
DroidPlugin 是Andy Zhang在Android系統(tǒng)上實現了一種新的 插件機制 :它可以在無需安裝、修改的情況下運行APK文件,此機制對改進大型APP的架構,實現多團隊協(xié)作開發(fā)具有一定的好處。
具體介紹可以詳見官方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接口,增加了動態(tài)代理相關的操作。一般Hook具體類都需要動態(tài)代理,所以一般都會直接繼承于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細節(jié)
IPackageManagerHookHandle
IPackageManagerHookHandle用來處理具體的hook細節(jié),在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在原始方法被調用之前執(zhí)行某些操作,
如果返回true,說明這件事我來處理了,原始方法你不要管了,此時原始方法就不會被調用,
如果返回false,原始方法會被調用
*/
boolean suc = beforeInvoke(receiver, method, args);
Object invokeResult = null;
if (!suc) {
invokeResult = method.invoke(receiver, args);
}
/*
子類可以重寫afterInvoke在原始方法被調用之后執(zhí)行某些操作,
*/
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;
}
/**
* 在某個方法被調用之前執(zhí)行,如果返回true,則不執(zhí)行原始的方法,否則執(zhí)行原始方法
*/
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方法在哪被調用呢,請繼續(xù)看下節(jié)。
ProxyHook -- 實現動態(tài)代理
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上執(zhí)行該方法,返回
if (!isEnable()) {
return method.invoke(mOldObj, args);
}
//mHookHandles里查找該方法對應的HookedMethodHandler對象
HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
//如果有 就執(zhí)行hookedMethodHandler.doHookInner方法來具體實現對該方法的hook
if (hookedMethodHandler != null) {
return hookedMethodHandler.doHookInner(mOldObj, method, args);
}
//如果沒有,說明該方法不需要被hook,直接在mOldObj上執(zhí)行該方法,返回
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<Class<?>> 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的過程就分析完了。