Xposed學習二:實現機制


在上一篇我們學習了如何在AS中創建Xposed模塊,本篇來分析下官方教程中redClock的實現原理。本系列文章基於version-51

public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
        if(!lpparam.packageName.equals("com.android.systemui")) return;
        XposedBridge.log("we are in systemui !");

        findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                TextView tv = (TextView) param.thisObject;
                String text = tv.getText().toString();
                tv.setText(text + ")");
                tv.setTextColor(Color.RED);
            }
        });
    }

 

上面的代碼可以將原先在狀態欄的時鍾文本顏色變成紅色,且在后面加")"。看下圖:

 

               

主要的實現代碼在findAndHookMethod函數中,查看函數定義:

findAndHookMethod:

——>findMethodExact(clazz,methodName,paramterClasses);

——>XposedBridge.hookMethod(method,callback);

先看findMethodExact,

       代碼很簡單就是要得到methodName在android中對應的函數對象,根據findAndHookMethod的參數得到字符串sb(格式參考注釋行),用sb在methodCache這個hashMap查找有沒有對應的method;若沒有則根據methodName和parameterTypes利用getDeclaredMethod得到對應的method。

public static Method findMethodExact(Class<?> clazz, String methodName, Class... parameterTypes) {
        StringBuilder sb = new StringBuilder(clazz.getName());
        sb.append('#');
        sb.append(methodName);
        sb.append(getParametersString(parameterTypes));
        sb.append("#exact");
    // sb = com.android.systemui.statusbar.policy.Clock#updateClock(參數1,參數2)#exact
        
        String fullMethodName = sb.toString();
        Method e;
    //methodCache鍵值對存放fullMethodName,method對象
        if(methodCache.containsKey(fullMethodName)) {
            e = (Method)methodCache.get(fullMethodName);
            if(e == null) {
                throw new NoSuchMethodError(fullMethodName);
            } else {
                return e;
            }
        } else {
            try {
                e = clazz.getDeclaredMethod(methodName, parameterTypes);
                e.setAccessible(true);
                methodCache.put(fullMethodName, e);
                return e;
            } catch (NoSuchMethodException var6) {
                methodCache.put(fullMethodName, (Object)null);
                throw new NoSuchMethodError(fullMethodName);
            }
        }
    }

 

   看來重點在XposedBridge.hookMethod:

public static Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
        if(!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor)) {
            throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
        } else if(hookMethod.getDeclaringClass().isInterface()) {
            throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
        } else if(Modifier.isAbstract(hookMethod.getModifiers())) {
            throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
        } else {
    //上面代碼分步檢查hookMethod的類型
    //else中的代碼得到hookMethod對應的鍵值對
            boolean newMethod = false;
            Map declaringClass = sHookedMethodCallbacks;
            XposedBridge.CopyOnWriteSortedSet callbacks;
            synchronized(sHookedMethodCallbacks) {
                callbacks = (XposedBridge.CopyOnWriteSortedSet)sHookedMethodCallbacks.get(hookMethod);
                if(callbacks == null) {
                    callbacks = new XposedBridge.CopyOnWriteSortedSet();
                    sHookedMethodCallbacks.put(hookMethod, callbacks);
                    newMethod = true;
                }
            }
            
            callbacks.add(callback);
    //替換hookMehod的callbacks為callback,其實callback是存放的是hookMethod所有的callback,看定義:</span></span>
    final XposedBridge.CopyOnWriteSortedSet<XC_MethodHook> callbacks;
    //以上代碼就是為hookMethod建立對應的callback list
    //sHookedMethodCallbacks存放hookMethod和callback
            
            if(newMethod) {
                Class declaringClass1 = hookMethod.getDeclaringClass();
                int slot = XposedHelpers.getIntField(hookMethod, "slot");
                Class[] parameterTypes;
                Class returnType;
                if(hookMethod instanceof Method) {
                    parameterTypes = ((Method)hookMethod).getParameterTypes();
                    returnType = ((Method)hookMethod).getReturnType();
                } else {
                    parameterTypes = ((Constructor)hookMethod).getParameterTypes();
                    returnType = null;
                }
        //以上代碼得到method的參數和返回值,在AdditionalHookInfo下使用
        //把callback、method參數、method返回值匯總在AdditionalHookInfo類下
                XposedBridge.AdditionalHookInfo additionalInfo = new XposedBridge.AdditionalHookInfo(callbacks, parameterTypes, returnType, (XposedBridge.AdditionalHookInfo)null);
               //本地函數在libxposed_dalvik.cpp
       hookMethodNative(hookMethod, declaringClass1, slot, additionalInfo);
            }

            callback.getClass();
            return new Unhook(callback, hookMethod);
    //為callback綁定hookMthod
        }
    }

 

    上面亂七八糟的走了這么多,登記了2個hashmap{(fullMethodName,method對象),(hookmethod,callback)}。看來java層只是管理這些結構並沒有實質性的操作,進入native代碼----Xposed.cpp:

//參數:reflectedMethodIndirect==>hookmethod,declaredClassIndirect==>hookmethod所在的類
//        slot==>slot,additionalInfoIndirect==>結構體包含callback、parameterTypes、returnType
void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,
            jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {
    // Usage errors?
    if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {
        dvmThrowIllegalArgumentException("method and declaredClass must not be null");
        return;
    }

    // Find the internal representation of the method
    //獲得dalvik中的classObject對象
    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
    //獲得dalvik中的Method,被dalvik執行的函數體不同於java層的Method,位於Object.h
    Method* method = dvmSlotToMethod(declaredClass, slot);
    if (method == NULL) {
        dvmThrowNoSuchMethodError("Could not get internal representation for method");
        return;
    }
    //若method已被hook則直接返回
    if (isMethodHooked(method)) {
        // already hooked
        return;
    }

    // Save a copy of the original method and other hook info
    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));
    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));
    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));
    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

    // Replace method with our own code  將method替換成我們自己的代碼
    //設置method->accessFlags = ACC_NATIVE;表示method為native代碼
    //下面幾行代碼都是為這行代碼作補充 
    //For a native method, we compute the size of the argument list, 
    //and set "insSize" and "registerSize" equal to it.
    SET_METHOD_FLAG(method, ACC_NATIVE);
    //給method添加callback函數表示已被hooked
    method->nativeFunc = &hookedMethodCallback;
    //原本的insns中存放的是dex指令,現變為hookinfo為hookedMethodCallback准備
    method->insns = (const u2*) hookInfo;
    method->registersSize = method->insSize;
    method->outsSize = 0;

    if (PTR_gDvmJit != NULL) {
        // reset JIT cache
        char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));
        if (currentValue == 0 || currentValue == 1) {
            MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;
        } else {
            ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);
      

       代碼主要的就是紅色標注的,它將Method標為native code。dalvik虛擬機在執行Method時,則會直接調用其成員變量hookedMethodCallback執行。注意,這個時候已經改變了原本的Method的執行步驟了(Xposed在此刻覺醒啦啦啦)。看下面dalvik代碼,/dalvik/vm/interp/Stack.c

void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
    bool fromJni, JValue* pResult, va_list args)
{
    ......

    if (dvmIsNativeMethod(method)) {
        TRACE_METHOD_ENTER(self, method);
        /*
         * Because we leave no space for local variables, "curFrame" points
         * directly at the method arguments.
         */
        (*method->nativeFunc)(self->curFrame, pResult, method, self);
        TRACE_METHOD_EXIT(self, method);
    } else {</span>
    //這里是在Inter.cpp中直接解析method
        dvmInterpret(self, method, pResult);
    }

    ......
    }

   這一路走來感覺有點偏離主線類,回看下主題,在執行完 findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader,"updateClock", new XC_MethodHook() );后。updateClock(hookmethod)的accessflag = ACC_NATIVE,dalvik在執行updateClock方法時發現其為native code,則執行nativeFunc 函數體即hookedMethodCallback。ok,找到"元凶"了,繼續看代碼:

/* This is called when a hooked method is executed. */
void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {
    ......
    //call the Java handler function</span>
    JValue result;
    
    dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result,
        originalReflected, (int) original, additionalInfo, thisObject, argsArray);
    ......
}

 

  hookedMethodCallback函數中主要是調用dvmCallMethod去執行xposedHandleHookedMethod,而xposedHandleHookedMethod是classXposedBridge里的handleHookedMethod方法。ok,重頭戲來了
private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, Object thisObject, Object[] args) throws Throwable {
        XposedBridge.AdditionalHookInfo additionalInfo = (XposedBridge.AdditionalHookInfo)additionalInfoObj;
        if(disableHooks) {
            try {
        //hook不使能,執行原method
                return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, thisObject, args);
            ......
            }
        } else {
    /得到hookmethod的callback,在之前的XposedBridge.hookMethod中為callbacks添加了callback
            Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
            int callbacksLength = callbacksSnapshot.length;
            if(callbacksLength == 0) {
        //hookmethod的callback為空,hooked無意義,執行原method
                try {
                    return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, thisObject, args);
                ......
                }
            } else {
                ......
                do {
                    label65: {
                        try {
                            ((XC_MethodHook)callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
                        } catch (Throwable var18) {
                            log(var18);
                            param.setResult((Object)null);
                            param.returnEarly = false;
                            break label65;
                        }

                        if(param.returnEarly) {
                            ++beforeIdx;
                            break;
                        }
                    }

                    ++beforeIdx;
        //hookmethod有幾個callback就循環幾次
                } while(beforeIdx < callbacksLength);

                if(!param.returnEarly) {
                    try {
            //在beforeHookedMethod后執行原method
                        param.setResult(invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
                    } catch (InvocationTargetException var16) {
                        param.setThrowable(var16.getCause());
                    }
                }

                int afterIdx = beforeIdx - 1;

                do {
                    Object lastResult = param.getResult();
                    Throwable lastThrowable = param.getThrowable();

                    try {
                        ((XC_MethodHook)callbacksSnapshot[afterIdx]).afterHookedMethod(param);
                    } catch (Throwable var17) {
                        log(var17);
                        if(lastThrowable == null) {
                            param.setResult(lastResult);
                        } else {
                            param.setThrowable(lastThrowable);
                        }
                    }

                    --afterIdx;
                } while(afterIdx >= 0);
                ......
            }
        }
    }

 

別看handleHookedMethod代碼老長了,其實它很單純。

 

第一步:是否需要執行callback,否則直接執行原method,gameover;

第二步:執行callbacks里的beforeHookedMethod方法,有幾個callback執行幾次beforeHookedMethod;

第三步:執行原method;

第四步:執行callbacks里的afterHookedMethod方法,類同beforeHookedMethod。

需要注意的是如果method有多個callback,其beforeHookedMethod和afterHookedMethod執行順序:

A1.before->A2.before->原method->A2.after->A1.after,也是蠻符合客觀規律的嘛。

好,關於findAndHookMethod()函數也算是從上倒下看了個遍,但你上面添這么多代碼是算怎么回事呢?下面就簡單總結下羅:



好了,本篇就先這樣吧,太長了也不好看。下篇再分析下其余枝節:

1 handleLoadPackage 怎么生效

2 dalvik層如何回到java層(數據類型如何回傳)

3 XC_MethodHook中會執行原來的java函數體,如何執行;

其他想到再分析啰,大家也可以對上述執行流程提問,我們一起探討。


參考資料:

1 Xposed框架Java部分

2 Dalvik虛擬機的運行過程分析

版權聲明:本文為博主原創文章,未經博主允許不得轉載。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM