在DroidPlugin源碼中,作者提供了一份關(guān)于其實(shí)現(xiàn)原理的PPT講義,這里我把其中介紹基本實(shí)現(xiàn)原理的章節(jié)貼出來(lái)。
以下用DP代指DroidPlugin

本文主要介紹Hook機(jī)制,看下作者是如何做到“偷天換日”。
Hook機(jī)制
我認(rèn)為,DP所指的“Hook”和傳統(tǒng)意義上的Hook是有點(diǎn)區(qū)別的,例如在API Hook中提到這樣一句話“通過(guò)hook‘接觸’到需要修改的api函數(shù)入口點(diǎn),改變它的地址指向新的自定義的函數(shù)”。Window上的API Hook可能會(huì)導(dǎo)致被hook的函數(shù)不被執(zhí)行(不知道我這樣理解對(duì)不對(duì))。而DP的Hook機(jī)制,被hook的函數(shù)是會(huì)被執(zhí)行的。閱讀DP源碼過(guò)程中我就有這樣的疑問(wèn),那DP的Hook到底做了啥!
Java動(dòng)態(tài)代理
再回答上面的疑問(wèn)之前,我們需要了解“Java動(dòng)態(tài)代理”這個(gè)玩意。關(guān)于動(dòng)態(tài)代理的資料有很多,這里我就簡(jiǎn)單介紹下。
Java動(dòng)態(tài)代理是Java反射提供的一個(gè)強(qiáng)大功能,使用動(dòng)態(tài)代理,我們可以在被hook方法執(zhí)行前后,做些額外操作。也可以修改被hook方法的入?yún)?,修改方法返回值。下面舉個(gè)最簡(jiǎn)單的代理例子,來(lái)說(shuō)明代理能做什么。
首先定義一個(gè)接口IShopping,聲明一個(gè)購(gòu)物的方法shopping。
package com.kisson.proxy;
public interface IShopping {
void shopping(String money);
}
接著定義一個(gè)類(lèi)GoShopping,實(shí)現(xiàn)IShopping的shopping方法。
package com.kisson.proxy;
public class GoShopping implements IShopping{
@Override
public void shopping() {
// TODO Auto-generated method stub
System.out.println("我去逛街咯!"+" 我有"+money+"元");
}
}
接著我們需要利用動(dòng)態(tài)代理去hook接口IShopping的shopping方法。然后定義一個(gè)IShoppingHook類(lèi)并實(shí)現(xiàn)InvocationHandler接口
package com.kisson.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class IShoppingHook implements InvocationHandler {
private Object mHookedObject;
public IShoppingHook(Object hookedObject) {
this.mHookedObject = hookedObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 方法執(zhí)行前,做預(yù)處理
beforeHook();
//錢(qián)越多越好?。。? args[0] = "2000000";
//在這里就是執(zhí)行shopping方法的過(guò)程,通過(guò)反射來(lái)完成
Object result = method.invoke(mHookedObject, args);
// 方法執(zhí)行后,做收尾處理
afterHook();
return result;
}
public void beforeHook() {
// todo:something
System.out.println("工作都已經(jīng)做完了,可以安心購(gòu)物咯!");
}
public void afterHook() {
// todo:something
System.out.println("買(mǎi)的真開(kāi)心!");
}
}
上面的準(zhǔn)備工作就緒,接著看如何實(shí)現(xiàn)。
package com.kisson.proxy;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ProxyTest test = new ProxyTest();
IShopping shopping = new GoShopping();
test.newProxyInstance(shopping).shopping("100");;
}
private IShopping newProxyInstance(IShopping shopping) {
return (IShopping) Proxy.newProxyInstance(shopping.getClass()
.getClassLoader(), shopping.getClass().getInterfaces(),
new IShoppingHook(shopping));
}
}
執(zhí)行結(jié)果如下:
工作都已經(jīng)做完了,可以安心購(gòu)物咯!
我去逛街咯! 我有2000000元
買(mǎi)的真開(kāi)心!
以上動(dòng)態(tài)代理的過(guò)程我們做了兩件事:
1.在shopping方法執(zhí)行前后,分別執(zhí)行了beforeHook和afterHook方法。
2.改變了shopping方法的入?yún)?,本?lái)只有100元,最終結(jié)果卻得到了2000000元!
DP使用Hook機(jī)制完成了三個(gè)工作:
- 動(dòng)態(tài)代理實(shí)現(xiàn)函數(shù)hook。
- Binder代理繞過(guò)部分系統(tǒng)服務(wù)限制。
- IO重定向
以后針對(duì)這三個(gè)點(diǎn)分別介紹下。
Binder代理繞過(guò)部分系統(tǒng)服務(wù)限制
原理
LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
上述代碼,是獲取系統(tǒng)位置服務(wù)。如果我們需要獲取類(lèi)似的系統(tǒng)服務(wù),都是通過(guò)Context的getSystemService方法來(lái)獲取。

getSystemService方法的是在ContextImpl類(lèi)中實(shí)現(xiàn)的。很久沒(méi)看源碼,看了下源碼發(fā)現(xiàn)該方法的實(shí)現(xiàn)被修改了。
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
這里出現(xiàn)了一個(gè)SystemServiceRegistry類(lèi),其作用是用來(lái)管理所有通過(guò)getSystemService返回的系統(tǒng)服務(wù)。
在類(lèi)中SystemServiceRegistry類(lèi)中有很大一坨靜態(tài)代碼塊,用于注冊(cè)各種系統(tǒng)服務(wù)。
//位置服務(wù)的注冊(cè)
registerService(Context.LOCATION_SERVICE, LocationManager.class,
new CachedServiceFetcher<LocationManager>() {
@Override
public LocationManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}});
在這里我們可以看到LocationManager的創(chuàng)建過(guò)程了,通過(guò)調(diào)用ServiceManager的getService方法來(lái)創(chuàng)建IBinder對(duì)象,作為L(zhǎng)ocationManager構(gòu)造方法的參數(shù)。在ContexImpl類(lèi)中有個(gè)mServiceCache對(duì)象,用于保存創(chuàng)建的各種XXManager對(duì)象,類(lèi)似單例模式吧。
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static IServiceManager sServiceManager;
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
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;
}
//....省略部分代碼
}
通過(guò)ServiceManager源碼,可以知道:
- 使用sCache對(duì)象來(lái)保存系統(tǒng)中已經(jīng)創(chuàng)建的IBinder。
- getService方法中,首先會(huì)在sCache中去查找是否有緩存,如果沒(méi)有,則調(diào)用IServiceManager的getService方法創(chuàng)建IBinder。
DP hook系統(tǒng)服務(wù)的原理:
- 對(duì)ServiceManager進(jìn)行操作,反射得到sCache對(duì)象,將系統(tǒng)已經(jīng)原有的系統(tǒng)移除掉(Remove方法),接著再通過(guò)反射調(diào)用ServiceManager的getService方法獲取新的服務(wù)(IBinder),并在此加入sCache。
- 將系統(tǒng)服務(wù)中(IBinder)某些有執(zhí)行限制的方法,進(jìn)行動(dòng)態(tài)代理處理,在這些方法執(zhí)行的前后進(jìn)行“欺騙”操作。
總結(jié)起來(lái)就是偷天換日、欺上瞞下!
源碼分析之偷天換日
首先來(lái)看看“偷天換日”的類(lèi)圖。

結(jié)合類(lèi)圖,我們來(lái)說(shuō)明下各個(gè)類(lèi)的作用。
1.Hook類(lèi)
import android.content.Context;
/**
* Created by Andy Zhang(zhangyong232@gmail.com) on 2015/3/2.
* ---------------------------------
* Hook類(lèi)是所有需要進(jìn)行Hook操作類(lèi)的基類(lèi),直接子類(lèi)有BinderHook,
* ServiceManagerCacheBinderHook,ProxyHook,SQLiteDatabaseHook等
*/
public abstract class Hook {
//mEnable用于標(biāo)識(shí)是否開(kāi)啟Hook操作,如果不開(kāi)啟,則走系統(tǒng)自由流程
private boolean mEnable = false;
//宿主的上下文環(huán)境
protected Context mHostContext;
protected BaseHookHandle mHookHandles;
public void setEnable(boolean enable, boolean reInstallHook) {
this.mEnable = enable;
}
public final void setEnable(boolean enable) {
setEnable(enable, false);
}
public boolean isEnable() {
return mEnable;
}
protected Hook(Context hostContext) {
mHostContext = hostContext;
mHookHandles = createHookHandle();
}
//創(chuàng)建BaseHookHandle對(duì)象
protected abstract BaseHookHandle createHookHandle();
/**
* 子類(lèi)實(shí)現(xiàn)onInstall方法,主要用于替換ServiceManager中sCache已經(jīng)存在的系統(tǒng)服務(wù)。
* 首先移除已經(jīng)存在的系統(tǒng)服務(wù)IBinder對(duì)象,然后自己生成這些系統(tǒng)服務(wù)的IBinder對(duì)象,并放入sCache對(duì)象中。
* onInstall方法可以理解為“安裝Fake服務(wù)的過(guò)程”
*/
protected abstract void onInstall(ClassLoader classLoader) throws Throwable;
protected void onUninstall(ClassLoader classLoader) throws Throwable {
}
}
2.BaseHookHandle
import android.content.Context;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Created by Andy Zhang(zhangyong232@gmail.com) on 2015/2/28.
* 所有IXXXBinderHook的基類(lèi),該類(lèi)的作用主要是負(fù)責(zé)HookedMethodHandler的“載入”、“讀取”
*/
public abstract class BaseHookHandle {
//宿主的上下文
protected Context mHostContext;
//HookedMethodHandler的對(duì)象緩存集合
protected Map<String, HookedMethodHandler> sHookedMethodHandlers = new HashMap<String, HookedMethodHandler>(5);
public BaseHookHandle(Context hostContext) {
mHostContext = hostContext;
init();
}
//將所有HookedMethodHandler對(duì)象存入到sHookedMethodHandlers中
protected abstract void init();
//獲取sHookedMethodHandlers中所有key值
public Set<String> getHookedMethodNames(){
return sHookedMethodHandlers.keySet();
}
//根據(jù)sHookedMethodHandlers的key值獲取對(duì)應(yīng)的HookedMethodHandler對(duì)象
public HookedMethodHandler getHookedMethodHandler(Method method) {
if (method != null) {
return sHookedMethodHandlers.get(method.getName());
} else {
return null;
}
}
}
3.HookedMethodHandler
import android.content.Context;
import com.morgoo.helper.Log;
import java.lang.reflect.Method;
/**
* 該類(lèi)可以說(shuō)整個(gè)Hook機(jī)制的核心,前面在介紹動(dòng)態(tài)代理的時(shí)候,我們聲明了IShoppingHook類(lèi)并實(shí)現(xiàn)InvocationHandler接口,
* 在其invoke方法中,通過(guò)發(fā)射機(jī)制調(diào)用被Hook的方法并在其調(diào)用前后分別調(diào)用了beforeHook和afterHook方法。
* 那么本類(lèi)的作用也是如此:在InvocationHandler的invoke方法需要調(diào)用的被Hook方法的過(guò)程,挪到doHookInner中實(shí)現(xiàn)。
* 在真正調(diào)用被Hook的方法前后,做一些處理,以繞過(guò)系統(tǒng)限制!
* **/
public class HookedMethodHandler {
private static final String TAG = HookedMethodHandler.class.getSimpleName();
protected final Context mHostContext;
//Hook后的fake服務(wù)(IBinder對(duì)象)
private Object mFakedResult = null;
//是否使用fake服務(wù)
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;
boolean suc = beforeInvoke(receiver, method, args);
Object invokeResult = null;
if (!suc) {
invokeResult = method.invoke(receiver, args);
}
afterInvoke(receiver, method, args, invokeResult);
if (mUseFakedResult) {
return mFakedResult;
} else {
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;
}
}
4.ServiceManagerCacheBinderHook
**
* Created by Andy Zhang(zhangyong232@gmail.com) on 2015/3/2.
* 本類(lèi)主要完成對(duì)系統(tǒng)ServiceManager類(lèi)中的對(duì)象和方法進(jìn)行hook操作。
* 包括:1.從sCache對(duì)象中remove掉已經(jīng)存在的系統(tǒng)服務(wù)Binder對(duì)象
* 2.生成fake系統(tǒng)服務(wù)Binder對(duì)象,并加入到sCache對(duì)象中
*/
public class ServiceManagerCacheBinderHook extends Hook implements InvocationHandler {
//系統(tǒng)服務(wù)名字,如ActivityManager的“activity”
private String mServiceName;
public ServiceManagerCacheBinderHook(Context hostContext, String servicename) {
super(hostContext);
mServiceName = servicename;
setEnable(true);
}
@Override
protected void onInstall(ClassLoader classLoader) throws Throwable {
//通過(guò)反射獲取sCache對(duì)象
Object sCacheObj = FieldUtils.readStaticField(ServiceManagerCompat.Class(), "sCache");
//判斷sCacheObj類(lèi)型是否是Map
if (sCacheObj instanceof Map) {
Map sCache = (Map) sCacheObj;
//通過(guò)Service Name獲取對(duì)應(yīng)系統(tǒng)服務(wù)的IBinder對(duì)象
Object Obj = sCache.get(mServiceName);
if (Obj != null && false) {
//FIXME 已經(jīng)有了怎么處理?這里我們只是把原來(lái)的給remove掉,再添加自己的。程序下次取用的時(shí)候就變成我們hook過(guò)的了。
//但是這樣有缺陷。
//add by me
// 作者這個(gè)疑問(wèn)是需要解決。因?yàn)橄到y(tǒng)原有的已經(jīng)被替換了,等于后續(xù)其他app使用系統(tǒng)服務(wù)的時(shí)候都是被替換的IBinder對(duì)象
//雖然說(shuō)沒(méi)有啥大影響,但是還是要做到善始善終。how to do?
throw new RuntimeException("Can not install binder hook for " + mServiceName);
} else {
//移除掉mServiceName對(duì)應(yīng)的系統(tǒng)服務(wù)IBinder對(duì)象
sCache.remove(mServiceName);
//這里又重新創(chuàng)建了對(duì)應(yīng)的系統(tǒng)服務(wù),注意這里不是被Hook過(guò)的,是通過(guò)反射調(diào)用ServiceManager的getService方法獲取
//到的。這里多這一步的目的,是獲取由系統(tǒng)創(chuàng)建的服務(wù),因?yàn)樯弦徊奖晃覀價(jià)emove調(diào)用的系統(tǒng)服務(wù)也有可能是被Hook過(guò)的。
IBinder mServiceIBinder = ServiceManagerCompat.getService(mServiceName);
if (mServiceIBinder != null) {
/**
*這里出現(xiàn)一個(gè)MyServiceManager類(lèi),該類(lèi)可以理解為類(lèi)似ServiceManger的存在。
* 將原始的mServiceIBinder對(duì)象通過(guò)addOriginService加入到緩存中
* */
MyServiceManager.addOriginService(mServiceName, mServiceIBinder);
//以下使用Java動(dòng)態(tài)代理來(lái)完成生成fake IBinder的過(guò)程
Class clazz = mServiceIBinder.getClass();
List<Class<?>> interfaces = Utils.getAllInterfaces(clazz);
Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
IBinder mProxyServiceIBinder = (IBinder) MyProxy.newProxyInstance(clazz.getClassLoader(), ifs, this);
sCache.put(mServiceName, mProxyServiceIBinder);
MyServiceManager.addProxiedServiceCache(mServiceName, mProxyServiceIBinder);
}
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//這里比較簡(jiǎn)單,就不說(shuō)明了!
IBinder originService = MyServiceManager.getOriginService(mServiceName);
if (!isEnable()) {
return method.invoke(originService, args);
}
HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
if (hookedMethodHandler != null) {
return hookedMethodHandler.doHookInner(originService, method, args);
} else {
return method.invoke(originService, args);
}
}
//打印各種異常!
catch (InvocationTargetException e) {
Throwable cause = e.getTargetException();
if (cause != null && MyProxy.isMethodDeclaredThrowable(method, cause)) {
throw cause;
} else if (cause != null) {
RuntimeException runtimeException = !TextUtils.isEmpty(cause.getMessage()) ? new RuntimeException(cause.getMessage()) : new RuntimeException();
runtimeException.initCause(cause);
throw runtimeException;
} else {
RuntimeException runtimeException = !TextUtils.isEmpty(e.getMessage()) ? new RuntimeException(e.getMessage()) : new RuntimeException();
runtimeException.initCause(e);
throw runtimeException;
}
} catch (IllegalArgumentException e) {
try {
StringBuilder sb = new StringBuilder();
sb.append(" DROIDPLUGIN{");
if (method != null) {
sb.append("method[").append(method.toString()).append("]");
} else {
sb.append("method[").append("NULL").append("]");
}
if (args != null) {
sb.append("args[").append(Arrays.toString(args)).append("]");
} else {
sb.append("args[").append("NULL").append("]");
}
sb.append("}");
String message = e.getMessage() + sb.toString();
throw new IllegalArgumentException(message, e);
} catch (Throwable e1) {
throw e;
}
} catch (Throwable e) {
if (MyProxy.isMethodDeclaredThrowable(method, e)) {
throw e;
} else {
RuntimeException runtimeException = !TextUtils.isEmpty(e.getMessage()) ? new RuntimeException(e.getMessage()) : new RuntimeException();
runtimeException.initCause(e);
throw runtimeException;
}
}
}
/**
* 前面有提到過(guò)BaseHookHandle主要是用來(lái)管理HookedMethodHandler對(duì)象的。
* 那么這里的ServiceManagerHookHandle類(lèi)是handle IBinder中“queryLocalInterface”方法的hook
* 該類(lèi)中有一個(gè)queryLocalInterface內(nèi)部類(lèi),繼承自HookedMethodHandler,前面有提到過(guò),HookedMethodHandler類(lèi)作用
* 主要是在調(diào)用被hook方法前后進(jìn)行某些處理。例如這里就是在IBinder調(diào)用queryLocalInterface方法后進(jìn)行處理:
* 設(shè)置被hook后的系統(tǒng)服務(wù)。
* queryLocalInterface返回的是IInterface對(duì)象(一定要注意IInterface和IBinder的區(qū)別,
* 在MyServiceManager中針對(duì)這兩個(gè)對(duì)象進(jìn)行了不同緩存),那么在這里就是IServiceManager對(duì)象(IServiceManager繼承自IInterface);
* 花絮:DP中,作者寫(xiě)了一個(gè)ServiceManagerBinderHook類(lèi),其實(shí)這個(gè)類(lèi)原本是用來(lái)Hook這個(gè)IServiceManager對(duì)象然后fake。
* 估計(jì)后來(lái)發(fā)現(xiàn)通過(guò)IBinder的queryLocalInterface方法可以生成IServiceManager,就棄用了ServiceManagerBinderHook類(lèi),所以在DP
* 中,這個(gè)類(lèi)沒(méi)有被使用過(guò)!?。? */
private class ServiceManagerHookHandle extends BaseHookHandle {
private ServiceManagerHookHandle(Context context) {
super(context);
}
@Override
protected void init() {
//創(chuàng)建IBinder的方法
sHookedMethodHandlers.put("queryLocalInterface", new queryLocalInterface(mHostContext));
}
class queryLocalInterface extends HookedMethodHandler {
public queryLocalInterface(Context context) {
super(context);
}
//在queryLocalInterface方法調(diào)用后,調(diào)用setFakedResult方法,設(shè)置fake過(guò)的系統(tǒng)服務(wù)
@Override
protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
Object localInterface = invokeResult;
Object proxiedObj = MyServiceManager.getProxiedObj(mServiceName);
if (localInterface == null && proxiedObj != null) {
setFakedResult(proxiedObj);
}
}
}
}
@Override
protected BaseHookHandle createHookHandle() {
return new ServiceManagerHookHandle(mHostContext);
}
}
Hook、HookedMethodHandler、BinderHook這個(gè)三基類(lèi)共同協(xié)作完成了偷天換日的操作。其實(shí)只要理解這個(gè)三個(gè)類(lèi)的功能是什么,就很好理解DP中Hook的精髓!
小結(jié)
越深入閱讀DP源碼,就越佩服作者思路。雖然原理看起來(lái)很簡(jiǎn)單,但是其中所付出時(shí)間可見(jiàn)一斑。
這節(jié)筆記就暫時(shí)做到這里,因?yàn)榘婷嬉呀?jīng)很長(zhǎng)了。希望各位不吝嗇你的喜歡!