DroidPlugin 之 Hook 原理

具體的流程是從右下角到中間再到左下角。

以Packagemanage的H ook為例:

具體的發(fā)起處理流程:

  • IPackageManagerHook 使用動態(tài)代理生成一個代理對象,這里的代理類用到了它的父類ProxyHook
  • ProxyHook 其實就是個動態(tài)代理類(ProxyHook)
    • 首先在初始化時
ProxyHook.java 
public ProxyHook(Context hostContext) {
        super(hostContext);
    }
Hook.java
 protected Hook(Context hostContext) {
        mHostContext = hostContext;
        mHookHandles = createHookHandle();
    }

在父類Hook的初始化中調(diào)用了createHookHandle,這個方法在具體的代理Hook中實現(xiàn),也就是IPackageManagerHook

   @Override
    protected BaseHookHandle createHookHandle() {
        return new IPackageManagerHookHandle(mHostContext);
    }

看下這里的初始化IPackageManagerHookHandle,它繼承自BaseHookHandle,在BaseHookHandle中的初始化中,調(diào)用了init方法,

    public BaseHookHandle(Context hostContext) {
        mHostContext = hostContext;
        init();
    }

這個init是在具體的HookHandle中實現(xiàn)的,這里是指IPackageManagerHookHandle

 @Override
    protected void init() {
        sHookedMethodHandlers.put("getPackageInfo", new getPackageInfo(mHostContext));
        sHookedMethodHandlers.put("getPackageUid", new getPackageUid(mHostContext));
        ...
        }

首先是這里的sHookedMethodHandlers 是一個Map對象,它是在BaseHookHandle中就聲明并初始化過了,它的key值是方法名,value值是具體的處理handler。后面new的類都是一個私有的類,這里大致看下getPackageInfo

private class getPackageInfo extends HookedMethodHandler {
        public getPackageInfo(Context context) {
            super(context);
        }

        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            //API 2.3,  4.01, 4.0.3_r1
            /*public PackageInfo getPackageInfo(String packageName, int flags) throws RemoteException;*/

            //API 4.1.1_r1, 4.2_r1, 4.3_r1, 4.4_r1, 5.0.2_r1
         /*public PackageInfo getPackageInfo(String packageName, int flags, int userId) throws RemoteException;*/
            if (args != null) {
                final int index0 = 0, index1 = 1;
                String packageName = null;
                if (args.length > index0) {
                    if (args[index0] != null && args[index0] instanceof String) {
                        packageName = (String) args[index0];
                    }
                }

                int flags = 0;
                if (args.length > index1) {
                    if (args[index1] != null && args[index1] instanceof Integer) {
                        flags = (Integer) args[index1];
                    }
                }

                if (packageName != null) {
                    PackageInfo packageInfo = null;
                    try {
                        packageInfo = PluginManager.getInstance().getPackageInfo(packageName, flags);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (packageInfo != null) {
                        setFakedResult(packageInfo);
                        return true;
                    } else {
                        Log.i(TAG, "getPackageInfo(%s) fail,pkginfo is null", packageName);
                    }
                }

            }
            return super.beforeInvoke(receiver, method, args);
        }
    }

繼承自HookedMethodHandler,在HookedMethodHandler這個基類中已經(jīng)實現(xiàn)了doHookInner方法,這個方法中會去依次嘗試調(diào)用beforeInvoke,反射invoke,afterInvoke。而beforeInvokeafterInvoke就是在上面的getPackageInfo這個私有類中去實現(xiàn)的。

到這里,可以大致理解,把所有的方法以及具體的處理handle都放在map中。

這里在清醒一下,上面都是初始化操作。還沒有到具體的調(diào)用地方。

  • 回到我們的ProxyHook類中,在代理類中會調(diào)用到invoke方法,
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    try {
        if (!isEnable()) {
            return method.invoke(mOldObj, args);
        }
        HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
        if (hookedMethodHandler != null) {
            return hookedMethodHandler.doHookInner(mOldObj, method, args);
        }
        return method.invoke(mOldObj, args);
    } 
      .....
    }

這里首先去調(diào)用Hook父類中的生成的mHookHandles去拿到他內(nèi)部Map中方法對應(yīng)的HookedMethodHandler

拿到處理Handler之后,再去調(diào)用它內(nèi)部的doHookInner方法,實現(xiàn)Hook。

  • 再往上一層 ,回到起點IPackageManagerHook 的onInstall 方法。
@Override
protected void onInstall(ClassLoader classLoader) throws Throwable {
    Object currentActivityThread = ActivityThreadCompat.currentActivityThread();
    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);
    PackageManager pm = mHostContext.getPackageManager();
    Object mPM = FieldUtils.readField(pm, "mPM");
    if (mPM != newPm) {
        FieldUtils.writeField(pm, "mPM", newPm);
    }
}

分析一下:

? 首先拿到當前的ActivityThread,將這個ActivityThread保存到ProxyHook中的OldObj,因為我們即將要生成這個對象的動態(tài)代理進行偷天換柱,所以要先把以前的保存下,以免失敗。

? 接著首先去拿到ActivityThread所有的接口,接著利用動態(tài)代理構(gòu)造一個代理對象,其中這里的invocationHandler用了this,其實是他的父類ProxyHook,具體可以看下ProxyHook的InVoke方法,這里就會到我們上面的解釋,這里其實是根據(jù)方法去分發(fā)具體的method代理實現(xiàn)。至此,可以拿到一個被代理后的PackageManager對象,并利用反射替換掉ActivityThread中的PackManager對象sPackageManager.

疑惑點:PackageManager這里為什么會有“mPM”這個對象,我暫時沒找到,后面回頭再看下。

這里舉了PackageManage 來分析,同樣的還有:

  • IActivityManagerHook 代理ActivityManager
  • IContentProviderHook 代理ContentProvider
  • IWindowSessionHook 具體不明

至此,我們可以看下包結(jié)構(gòu):

hook

? --- handle

? ---IPackageManagerHookHandle 具體的處理方法代理等

? ---proxy

? ---Proxy,PackageMangerHook 具體的代理管理類

Hook

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn):http://weishu.me/2016/03/07/understand-plugin-framework...
    朱立志閱讀 2,222評論 0 9
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,098評論 0 9
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,323評論 25 708
  • 文/樂樂的生活 天陰沉沉的,足有三四天了。 我看了看床上,妻子被大棉被裹得結(jié)實,像是作繭的蠶。我又看了看書房。吾家...
    樂樂的生活閱讀 459評論 5 4
  • 如果我們從一段關(guān)系中得到正結(jié)果,我們就會依賴自己的伴侶,不愿離開這段關(guān)系。因為它意味這自己的快樂和滿足。所以,一筆...
    玩兒_溫暖閱讀 238評論 0 0

友情鏈接更多精彩內(nèi)容