在DroidPlugin源碼中,作者提供了一份關(guān)于其實現(xiàn)原理的PPT講義青灼,這里我把其中介紹基本實現(xiàn)原理的章節(jié)貼出來徒役。
以下用DP代指DroidPlugin
本文主要介紹Hook機制,看下作者是如何做到“偷天換日”田巴。
Hook機制
我認(rèn)為,DP所指的“Hook”和傳統(tǒng)意義上的Hook是有點區(qū)別的挟秤,例如在API Hook中提到這樣一句話“通過hook‘接觸’到需要修改的api函數(shù)入口點壹哺,改變它的地址指向新的自定義的函數(shù)”。Window上的API Hook可能會導(dǎo)致被hook的函數(shù)不被執(zhí)行(不知道我這樣理解對不對)艘刚。而DP的Hook機制管宵,被hook的函數(shù)是會被執(zhí)行的。閱讀DP源碼過程中我就有這樣的疑問攀甚,那DP的Hook到底做了啥箩朴!
Java動態(tài)代理
再回答上面的疑問之前,我們需要了解“Java動態(tài)代理”這個玩意秋度。關(guān)于動態(tài)代理的資料有很多炸庞,這里我就簡單介紹下。
Java動態(tài)代理是Java反射提供的一個強大功能荚斯,使用動態(tài)代理埠居,我們可以在被hook方法執(zhí)行前后查牌,做些額外操作。也可以修改被hook方法的入?yún)⒗暮荆薷姆椒ǚ祷刂抵窖铡O旅媾e個最簡單的代理例子,來說明代理能做什么绎橘。
首先定義一個接口IShopping胁孙,聲明一個購物的方法shopping。
package com.kisson.proxy;
public interface IShopping {
void shopping(String money);
}
接著定義一個類GoShopping金踪,實現(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+"元");
}
}
接著我們需要利用動態(tài)代理去hook接口IShopping的shopping方法。然后定義一個IShoppingHook類并實現(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();
//錢越多越好7ㄏ!!靶瘸!
args[0] = "2000000";
//在這里就是執(zhí)行shopping方法的過程苫亦,通過反射來完成
Object result = method.invoke(mHookedObject, args);
// 方法執(zhí)行后,做收尾處理
afterHook();
return result;
}
public void beforeHook() {
// todo:something
System.out.println("工作都已經(jīng)做完了怨咪,可以安心購物咯!");
}
public void afterHook() {
// todo:something
System.out.println("買的真開心!");
}
}
上面的準(zhǔn)備工作就緒屋剑,接著看如何實現(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)做完了诗眨,可以安心購物咯!
我去逛街咯! 我有2000000元
買的真開心!
以上動態(tài)代理的過程我們做了兩件事:
1.在shopping方法執(zhí)行前后唉匾,分別執(zhí)行了beforeHook和afterHook方法。
2.改變了shopping方法的入?yún)⒔吵緛碇挥?00元巍膘,最終結(jié)果卻得到了2000000元!
DP使用Hook機制完成了三個工作:
- 動態(tài)代理實現(xiàn)函數(shù)hook芋簿。
- Binder代理繞過部分系統(tǒng)服務(wù)限制峡懈。
- IO重定向
以后針對這三個點分別介紹下。
Binder代理繞過部分系統(tǒng)服務(wù)限制
原理
LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
上述代碼与斤,是獲取系統(tǒng)位置服務(wù)肪康。如果我們需要獲取類似的系統(tǒng)服務(wù),都是通過Context的getSystemService方法來獲取撩穿。
getSystemService方法的是在ContextImpl類中實現(xiàn)的磷支。很久沒看源碼,看了下源碼發(fā)現(xiàn)該方法的實現(xiàn)被修改了食寡。
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
這里出現(xiàn)了一個SystemServiceRegistry類齐唆,其作用是用來管理所有通過getSystemService返回的系統(tǒng)服務(wù)。
在類中SystemServiceRegistry類中有很大一坨靜態(tài)代碼塊冻河,用于注冊各種系統(tǒng)服務(wù)箍邮。
//位置服務(wù)的注冊
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)建過程了,通過調(diào)用ServiceManager的getService方法來創(chuàng)建IBinder對象叨叙,作為LocationManager構(gòu)造方法的參數(shù)锭弊。在ContexImpl類中有個mServiceCache對象,用于保存創(chuàng)建的各種XXManager對象擂错,類似單例模式吧味滞。
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;
}
//....省略部分代碼
}
通過ServiceManager源碼,可以知道:
- 使用sCache對象來保存系統(tǒng)中已經(jīng)創(chuàng)建的IBinder钮呀。
- getService方法中剑鞍,首先會在sCache中去查找是否有緩存,如果沒有爽醋,則調(diào)用IServiceManager的getService方法創(chuàng)建IBinder蚁署。
DP hook系統(tǒng)服務(wù)的原理:
- 對ServiceManager進行操作,反射得到sCache對象蚂四,將系統(tǒng)已經(jīng)原有的系統(tǒng)移除掉(Remove方法)光戈,接著再通過反射調(diào)用ServiceManager的getService方法獲取新的服務(wù)(IBinder),并在此加入sCache遂赠。
- 將系統(tǒng)服務(wù)中(IBinder)某些有執(zhí)行限制的方法久妆,進行動態(tài)代理處理,在這些方法執(zhí)行的前后進行“欺騙”操作跷睦。
總結(jié)起來就是偷天換日筷弦、欺上瞞下!
源碼分析之偷天換日
首先來看看“偷天換日”的類圖抑诸。
結(jié)合類圖烂琴,我們來說明下各個類的作用。
1.Hook類
import android.content.Context;
/**
* Created by Andy Zhang(zhangyong232@gmail.com) on 2015/3/2.
* ---------------------------------
* Hook類是所有需要進行Hook操作類的基類哼鬓,直接子類有BinderHook监右,
* ServiceManagerCacheBinderHook,ProxyHook异希,SQLiteDatabaseHook等
*/
public abstract class Hook {
//mEnable用于標(biāo)識是否開啟Hook操作健盒,如果不開啟,則走系統(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對象
protected abstract BaseHookHandle createHookHandle();
/**
* 子類實現(xiàn)onInstall方法称簿,主要用于替換ServiceManager中sCache已經(jīng)存在的系統(tǒng)服務(wù)扣癣。
* 首先移除已經(jīng)存在的系統(tǒng)服務(wù)IBinder對象,然后自己生成這些系統(tǒng)服務(wù)的IBinder對象憨降,并放入sCache對象中父虑。
* onInstall方法可以理解為“安裝Fake服務(wù)的過程”
*/
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的基類,該類的作用主要是負(fù)責(zé)HookedMethodHandler的“載入”授药、“讀取”
*/
public abstract class BaseHookHandle {
//宿主的上下文
protected Context mHostContext;
//HookedMethodHandler的對象緩存集合
protected Map<String, HookedMethodHandler> sHookedMethodHandlers = new HashMap<String, HookedMethodHandler>(5);
public BaseHookHandle(Context hostContext) {
mHostContext = hostContext;
init();
}
//將所有HookedMethodHandler對象存入到sHookedMethodHandlers中
protected abstract void init();
//獲取sHookedMethodHandlers中所有key值
public Set<String> getHookedMethodNames(){
return sHookedMethodHandlers.keySet();
}
//根據(jù)sHookedMethodHandlers的key值獲取對應(yīng)的HookedMethodHandler對象
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;
/**
* 該類可以說整個Hook機制的核心士嚎,前面在介紹動態(tài)代理的時候呜魄,我們聲明了IShoppingHook類并實現(xiàn)InvocationHandler接口,
* 在其invoke方法中莱衩,通過發(fā)射機制調(diào)用被Hook的方法并在其調(diào)用前后分別調(diào)用了beforeHook和afterHook方法爵嗅。
* 那么本類的作用也是如此:在InvocationHandler的invoke方法需要調(diào)用的被Hook方法的過程,挪到doHookInner中實現(xiàn)笨蚁。
* 在真正調(diào)用被Hook的方法前后睹晒,做一些處理,以繞過系統(tǒng)限制括细!
* **/
public class HookedMethodHandler {
private static final String TAG = HookedMethodHandler.class.getSimpleName();
protected final Context mHostContext;
//Hook后的fake服務(wù)(IBinder對象)
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;
}
/**
* 在某個方法被調(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.
* 本類主要完成對系統(tǒng)ServiceManager類中的對象和方法進行hook操作锉试。
* 包括:1.從sCache對象中remove掉已經(jīng)存在的系統(tǒng)服務(wù)Binder對象
* 2.生成fake系統(tǒng)服務(wù)Binder對象,并加入到sCache對象中
*/
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 {
//通過反射獲取sCache對象
Object sCacheObj = FieldUtils.readStaticField(ServiceManagerCompat.Class(), "sCache");
//判斷sCacheObj類型是否是Map
if (sCacheObj instanceof Map) {
Map sCache = (Map) sCacheObj;
//通過Service Name獲取對應(yīng)系統(tǒng)服務(wù)的IBinder對象
Object Obj = sCache.get(mServiceName);
if (Obj != null && false) {
//FIXME 已經(jīng)有了怎么處理键痛?這里我們只是把原來的給remove掉,再添加自己的匾七。程序下次取用的時候就變成我們hook過的了絮短。
//但是這樣有缺陷。
//add by me
// 作者這個疑問是需要解決昨忆。因為系統(tǒng)原有的已經(jīng)被替換了丁频,等于后續(xù)其他app使用系統(tǒng)服務(wù)的時候都是被替換的IBinder對象
//雖然說沒有啥大影響,但是還是要做到善始善終邑贴。how to do席里?
throw new RuntimeException("Can not install binder hook for " + mServiceName);
} else {
//移除掉mServiceName對應(yīng)的系統(tǒng)服務(wù)IBinder對象
sCache.remove(mServiceName);
//這里又重新創(chuàng)建了對應(yīng)的系統(tǒng)服務(wù),注意這里不是被Hook過的拢驾,是通過反射調(diào)用ServiceManager的getService方法獲取
//到的奖磁。這里多這一步的目的,是獲取由系統(tǒng)創(chuàng)建的服務(wù)繁疤,因為上一步被我們remove調(diào)用的系統(tǒng)服務(wù)也有可能是被Hook過的咖为。
IBinder mServiceIBinder = ServiceManagerCompat.getService(mServiceName);
if (mServiceIBinder != null) {
/**
*這里出現(xiàn)一個MyServiceManager類,該類可以理解為類似ServiceManger的存在稠腊。
* 將原始的mServiceIBinder對象通過addOriginService加入到緩存中
* */
MyServiceManager.addOriginService(mServiceName, mServiceIBinder);
//以下使用Java動態(tài)代理來完成生成fake IBinder的過程
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 {
//這里比較簡單躁染,就不說明了!
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;
}
}
}
/**
* 前面有提到過BaseHookHandle主要是用來管理HookedMethodHandler對象的吞彤。
* 那么這里的ServiceManagerHookHandle類是handle IBinder中“queryLocalInterface”方法的hook
* 該類中有一個queryLocalInterface內(nèi)部類,繼承自HookedMethodHandler,前面有提到過饰恕,HookedMethodHandler類作用
* 主要是在調(diào)用被hook方法前后進行某些處理挠羔。例如這里就是在IBinder調(diào)用queryLocalInterface方法后進行處理:
* 設(shè)置被hook后的系統(tǒng)服務(wù)。
* queryLocalInterface返回的是IInterface對象(一定要注意IInterface和IBinder的區(qū)別埋嵌,
* 在MyServiceManager中針對這兩個對象進行了不同緩存)褥赊,那么在這里就是IServiceManager對象(IServiceManager繼承自IInterface);
* 花絮:DP中,作者寫了一個ServiceManagerBinderHook類莉恼,其實這個類原本是用來Hook這個IServiceManager對象然后fake。
* 估計后來發(fā)現(xiàn)通過IBinder的queryLocalInterface方法可以生成IServiceManager速那,就棄用了ServiceManagerBinderHook類俐银,所以在DP
* 中,這個類沒有被使用過6搜觥4废А!
*/
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過的系統(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這個三基類共同協(xié)作完成了偷天換日的操作踊餐。其實只要理解這個三個類的功能是什么,就很好理解DP中Hook的精髓臀稚!
小結(jié)
越深入閱讀DP源碼吝岭,就越佩服作者思路。雖然原理看起來很簡單吧寺,但是其中所付出時間可見一斑窜管。
這節(jié)筆記就暫時做到這里,因為版面已經(jīng)很長了稚机。希望各位不吝嗇你的喜歡幕帆!