1.DroidPlugin介紹
DroidPlugin 是Andy Zhang在Android系統(tǒng)上實(shí)現(xiàn)了一種新的 插件機(jī)制 :它可以在無(wú)需安裝唐责、修改的情況下運(yùn)行APK文件,此機(jī)制對(duì)改進(jìn)大型APP的架構(gòu),實(shí)現(xiàn)多團(tuán)隊(duì)協(xié)作開發(fā)具有一定的好處巍实。
具體介紹可以詳見(jiàn)官方github介紹筒狠,這里不再贅述
https://github.com/DroidPluginTeam/DroidPlugin/blob/master/readme_cn.md
2.Hook機(jī)制
2.1 類圖
2.2 關(guān)鍵類介紹
Hook
抽象基類挺庞,定義了hook需要的一些基本操作讼稚,每一個(gè)被hook的類會(huì)對(duì)應(yīng)一個(gè)Hook具體類。
ProxyHook
ProxyHook extends Hook implements InvocationHandler
ProxyHook在Hook類的基礎(chǔ)上實(shí)現(xiàn)了InvocationHandler接口茧痒,增加了動(dòng)態(tài)代理相關(guān)的操作肮韧。一般Hook具體類都需要?jiǎng)討B(tài)代理,所以一般都會(huì)直接繼承于ProxyHook
BaseHookHandle
Handle是“處理”的意思文黎,所以BaseHookHandle定義了具體的hook操作惹苗。每一個(gè)Hook具體類會(huì)包含一個(gè)BaseHookHandle具體類作為類成員殿较,BaseHookHandle具體類里面定義了該Hook具體類需要進(jìn)行的具體的hook操作
HookedMethodHandler
如果你要hook一個(gè)類耸峭,這個(gè)類里面有多個(gè)方法需要被hook,這時(shí)候每個(gè)方法會(huì)對(duì)應(yīng)一個(gè)HookedMethodHandler類來(lái)定義如何去hook該方法。因?yàn)锽aseHookHandle是定義具體的hook操作的類淋纲,所以BaseHookHandle里會(huì)包含一個(gè)HookedMethodHandler的Map.
2.3 舉個(gè)栗子 Hook PackageManager
IPackageManagerHook
public class IPackageManagerHook extends ProxyHook {
@Override
protected BaseHookHandle createHookHandle() {
return new IPackageManagerHookHandle(mHostContext);
}
}
首先會(huì)對(duì)應(yīng)一個(gè)Hook具體類 IPackageManagerHook 劳闹,IPackageManagerHook類里會(huì)包含一個(gè)BaseHookHandle具體類 IPackageManagerHookHandle用來(lái)處理具體的hook細(xì)節(jié)
IPackageManagerHookHandle
IPackageManagerHookHandle用來(lái)處理具體的hook細(xì)節(jié),在init方法里添加了所有被hook的方法對(duì)應(yīng)的HookedMethodHandler對(duì)象
@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在原始方法被調(diào)用之前執(zhí)行某些操作洽瞬,
如果返回true,說(shuō)明這件事我來(lái)處理了本涕,原始方法你不要管了,此時(shí)原始方法就不會(huì)被調(diào)用伙窃,
如果返回false,原始方法會(huì)被調(diào)用
*/
boolean suc = beforeInvoke(receiver, method, args);
Object invokeResult = null;
if (!suc) {
invokeResult = method.invoke(receiver, args);
}
/*
子類可以重寫afterInvoke在原始方法被調(diào)用之后執(zhí)行某些操作菩颖,
*/
afterInvoke(receiver, method, args, invokeResult);
//mUseFakedResult 為 true 說(shuō)明 該方法的返回值使用我偽造的結(jié)果--mFakedResult
if (mUseFakedResult) {
return mFakedResult;
} else {//mUseFakedResult 為 false 說(shuō)明 該方法的返回值使用調(diào)用原始方法的返回結(jié)果--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;
}
/**
* 在某個(gè)方法被調(diào)用之前執(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的包名,才需要進(jìn)行hook
if (instance.isPluginPackage(pkg0) && instance.isPluginPackage(pkg1)) {
//調(diào)用了instance.checkSignatures來(lái)進(jìn)行簽名檢測(cè)
int result = instance.checkSignatures(pkg0, pkg1);
//設(shè)置偽造的結(jié)果 這樣checkSignatures方法的返回值會(huì)使用這個(gè)偽造的結(jié)果
setFakedResult(result);
//返回true,說(shuō)明這件事我來(lái)處理了鳍怨,原始方法你不要管了呻右,此時(shí)原始方法就不會(huì)被調(diào)用
return true;
}
}
//如果包名不是我們的插件apk的包名,比如是宿主的包名鞋喇,此時(shí)就調(diào)用super.beforeInvoke 直接返回false声滥,
// 此時(shí)會(huì)調(diào)用原始的方法,并且使用原始方法的返回值作為返回值侦香,就跟該方法沒(méi)有被hook一樣
return super.beforeInvoke(receiver, method, args);
}
}
HookedMethodHandler.doHookInner方法在哪被調(diào)用呢落塑,請(qǐng)繼續(xù)看下節(jié)纽疟。
ProxyHook -- 實(shí)現(xiàn)動(dòng)態(tài)代理
public abstract class ProxyHook extends Hook implements InvocationHandler {
protected Object mOldObj;
public ProxyHook(Context hostContext) {
super(hostContext);
}
/**
* 設(shè)置被代理的原始的對(duì)象
*/
public void setOldObj(Object oldObj) {
this.mOldObj = oldObj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//isEnable 返回false 說(shuō)明該hook被設(shè)置為不生效,此時(shí)直接在mOldObj上執(zhí)行該方法憾赁,返回
if (!isEnable()) {
return method.invoke(mOldObj, args);
}
//mHookHandles里查找該方法對(duì)應(yīng)的HookedMethodHandler對(duì)象
HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
//如果有 就執(zhí)行hookedMethodHandler.doHookInner方法來(lái)具體實(shí)現(xiàn)對(duì)該方法的hook
if (hookedMethodHandler != null) {
return hookedMethodHandler.doHookInner(mOldObj, method, args);
}
//如果沒(méi)有仰挣,說(shuō)明該方法不需要被hook,直接在mOldObj上執(zhí)行該方法,返回
return method.invoke(mOldObj, args);
} catch (Exception e) {
//一些異常處理 略
}
}
}
IPackageManagerHook --用代理對(duì)象替換原始的PackageManager對(duì)象
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();
//從主線程對(duì)象里通過(guò)反射拿到sPackageManager對(duì)象缠沈,作為原始對(duì)象賦值給mOldObj
setOldObj(FieldUtils.readField(currentActivityThread, "sPackageManager"));
Class<?> iPmClass = mOldObj.getClass();
//生成代理對(duì)象
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);
//用代理對(duì)象替換原始對(duì)象
FieldUtils.writeField(currentActivityThread, "sPackageManager", newPm);
//調(diào)用宿主的context的getPackageManager獲取PackageManager對(duì)象
PackageManager pm = mHostContext.getPackageManager();
Object mPM = FieldUtils.readField(pm, "mPM");
//如果該對(duì)象不是我們的代理對(duì)象膘壶,就把該對(duì)象也替換成我們的代理對(duì)象
if (mPM != newPm) {
FieldUtils.writeField(pm, "mPM", newPm);
}
}
}
IPackageManagerHook對(duì)象的onInstall方法會(huì)在插件框架被安裝的時(shí)候調(diào)用。
被Hook的checkSignatures方法被調(diào)用的完整過(guò)程
至此洲愤,整個(gè)Hook的過(guò)程就分析完了颓芭。