Xposed筆記(一)Xposed初識.續

Xposed初識.續

original: http://www.bubuko.com/infodetail-647041.html

源碼分析
安裝xposed框架後,系統啓動,執行init, init 再調用 app_process 程序,由於這時候 app_process 已經被換了,所以app_process 啓動後先進入的是 xposedbridge.class 的 main 函數, 這個函數最後才進入標準的 zygoteInit.class 的 main 函數,在進入 zygote 之前,它調用了幾個函數,初始化了xposed框架,下面逐個分析。
1、initNative

Xposed.cpp (xposed):{"initNative", "()Z",(void*)de_robv_android_xposed_XposedBridge_initNative},
XposedBridge.java(xposedbridge\src\de\robv\android\xposed):   
if (initNative()) 
{
    XposedBridge.java (xposedbridge\src\de\robv\android\xposed):    
    private native static boolean initNative();

XposedBridge.class 的 initNative 是一個 native 函數,真正的實現在 Xposed.cpp 裏的 de_robv_android_xposed_XposedBridge_initNative

 de_robv_android_xposed_XposedBridge_initNative(JNIEnv*env, jclass clazz)
 xposedHandleHookedMethod = (Method*)env->GetStaticMethodID(xposedClass, "handleHookedMethod",
       "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

首先,從xposedClass(即XposedBridge.class)類獲取函數 handleHookedMethod, 這個函數是java函數,這裏獲取其對應的 Method 結構體指針,這樣就可以在 native 裏調用(jni的原理)。那麼,這個 handleHookedMethod是幹嘛的呢,這個函數就是最終執行的掛鉤函數,後面會分析。

Method* xposedInvokeOriginalMethodNative =(Method*) env->GetStaticMethodID(xposedClass,"invokeOriginalMethodNative",
       "(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
dvmSetNativeFunc(xposedInvokeOriginalMethodNative,de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative, NULL);

其次,從xposedClass(即XposedBridge.class)類獲取函數 invokeOriginalMethodNative函數的 Method 結構體指針,然後調用 dvmSetNativeFunc 爲這個java函數設置其 jni 實現 de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative , 這樣,調用 invokeOriginalMethodNative 函數其實執行的是後者。

objectArrayClass =dvmFindArrayClass("[Ljava/lang/Object;", NULL);
xresourcesClass =env->FindClass(XRESOURCES_CLASS);
   xresourcesClass =reinterpret_cast<jclass>(env->NewGlobalRef(xresourcesClass));
if (register_android_content_res_XResources(env)!= JNI_OK) {}
xresourcesTranslateResId =env->GetStaticMethodID(xresourcesClass, "translateResId",
       "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I");
xresourcesTranslateAttrId =env->GetStaticMethodID(xresourcesClass, "translateAttrId",
       "(Ljava/lang/String;Landroid/content/res/XResources;)I");

最後,獲取其他一些java類或函數的標識

2、initXbridgeZygote
XposedBridge.class main 函數第二個重要的函數是 initXbridgeZygote

// normal process initialization (for newActivity, Service, BroadcastReceiver etc.)
       findAndHookMethod(ActivityThread.class,"handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
           protected void beforeHookedMethod(MethodHookParam param) throwsThrowable {
       ...
}

首先,掛鉤了ActivityThread 類的 handleBindApplication 函數,這個函數是在android ams 系統創建新進程成功後在新進程內部調用的,掛鉤這個函數,可以在新進程創建後做一些事情
這裏調用了一個函數,實現了掛鉤 findAndHookMethod。這個函數定義在XposedHelpers.java

XposedHelpers.class

public static XC_MethodHook.UnhookfindAndHookMethod(Class<?> clazz, String methodName, Object...parameterTypesAndCallback) {
       if (parameterTypesAndCallback.length == 0 ||!(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceofXC_MethodHook))
           throw new IllegalArgumentException("no callback defined");

       XC_MethodHook callback = (XC_MethodHook)parameterTypesAndCallback[parameterTypesAndCallback.length-1];
       Method m = findMethodExact(clazz, methodName,getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));

       return XposedBridge.hookMethod(m,callback);
    }

這個函數找到類 clazz 的函數 methodName 所對應的 Method結構體,然後調用 XposedBridge 的 hookMethod 函數掛鉤它

XposedBridge.class

public static XC_MethodHook.UnhookhookMethod(Member hookMethod, XC_MethodHook callback) {
      ....

       boolean newMethod = false;
       CopyOnWriteSortedSet<XC_MethodHook> callbacks;
       synchronized (sHookedMethodCallbacks) {
           callbacks = sHookedMethodCallbacks.get(hookMethod);
           if (callbacks == null) {
                callbacks = newCopyOnWriteSortedSet<XC_MethodHook>();
               sHookedMethodCallbacks.put(hookMethod, callbacks);
                newMethod = true;
           }
       }
       callbacks.add(callback); // 先將被掛鉤的函數hookMethod 及掛鉤它的函數保存起來
       if (newMethod) {
           Class<?> declaringClass = hookMethod.getDeclaringClass();
           int slot = (int) 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;
           }

           AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
           hookMethodNative(hookMethod, declaringClass, slot, additionalInfo); // 最終調用 hookMethodNative 函數
} return callback.new Unhook(hookMethod); }

這個函數先將被掛鉤的函數hookMethod 及掛鉤它的 XC_MethodHook結構體保存起來,然後調用 hookMethodNative 函數
{“hookMethodNative”, “(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V”,(void*)de_robv_android_xposed_XposedBridge_hookMethodNative},

這個函數定義在xposed.cpp 裏

static voidde_robv_android_xposed_XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz,jobject reflectedMethodIndirect,
           jobject declaredClassIndirect, jint slot, jobjectadditionalInfoIndirect) {
   // Usage errors?
    if (declaredClassIndirect == NULL ||reflectedMethodIndirect == NULL) {
       dvmThrowIllegalArgumentException("method and declaredClass must notbe null");
       return;
    }

   // Find the internal representation of the method
   ClassObject* declaredClass = (ClassObject*)dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
   Method* method = dvmSlotToMethod(declaredClass, slot);
   if (method == NULL) {
       dvmThrowNoSuchMethodError("could not get internal representation for method");
       return;
    }

   if (xposedIsHooked(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
   SET_METHOD_FLAG(method, ACC_NATIVE);
    method->nativeFunc =&xposedCallHandler;
   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);
       }
    }
}

這個過程跟ADBI框架類似,先獲取要掛鉤的java函數的 Method 結構體指針,然後檢查一下是否已經被掛鉤了,如果是直接返回,否則,通過對Method 結構體進行賦值的方式,完成掛鉤
method->nativeFunc =&xposedCallHandler;
method->insns = (const u2*) hookInfo;
method->registersSize = method->insSize;
method->outsSize = 0;
這裏掛鉤函數全部使用 xposedCallHandler這個函數。掛鉤的詳細信息保存在 Method結構體的 insns 成員裏

xposed.cpp

static void xposedCallHandler(const u4*args, JValue* pResult, const Method* method, ::Thread* self) {
...
JValue result;
   dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result,
       originalReflected, (int) original, additionalInfo, thisObject,argsArray);
...
}

可以看到,最終執行xposedHandleHookedMethod這個native函數,這個native函數前面 initNative 執行時,已經獲取了它的java實現,真正的實現在
XposedBridge.java

private static ObjecthandleHookedMethod(Member method, int originalMethodId, ObjectadditionalInfoObj,
           Object thisObject, Object[] args) throws Throwable {
       AdditionalHookInfo additionalInfo = (AdditionalHookInfo)additionalInfoObj;

       if (disableHooks) {
           try {
                returninvokeOriginalMethodNative(method, originalMethodId,additionalInfo.parameterTypes,
                        additionalInfo.returnType, thisObject,args);
           } catch (InvocationTargetException e) {
                throw e.getCause();
           }
       }

       Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
       final int callbacksLength = callbacksSnapshot.length;
       if (callbacksLength == 0) {
           try {
                returninvokeOriginalMethodNative(method, originalMethodId,additionalInfo.parameterTypes,
                       additionalInfo.returnType, thisObject, args);
           } catch (InvocationTargetException e) {
                throw e.getCause();
           }
       }

       MethodHookParam param = new MethodHookParam();
       param.method = method;
       param.thisObject = thisObject;
       param.args = args;

       // call "before method" callbacks
       int beforeIdx = 0;
       do {
           try {
                ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
           } catch (Throwable t) {
                XposedBridge.log(t);

                // reset result (ignoring what the unexpectedly exitingcallback did)
                param.setResult(null);
                param.returnEarly = false;
               continue;
           }

           if (param.returnEarly) {
                // skip remaining"before" callbacks and corresponding "after" callbacks
                beforeIdx++;
                break;
           }
       } while (++beforeIdx < callbacksLength);

       // call original method if not requested otherwise
       if (!param.returnEarly) {
           try {
                param.setResult(invokeOriginalMethodNative(method,originalMethodId,
                       additionalInfo.parameterTypes, additionalInfo.returnType,param.thisObject, param.args));
           } catch (InvocationTargetException e) {
                param.setThrowable(e.getCause());
           }
       }

       // call "after method" callbacks
       int afterIdx = beforeIdx - 1;
       do {
           Object lastResult = param.getResult();
           Throwable lastThrowable = param.getThrowable();

            try {
                ((XC_MethodHook)callbacksSnapshot[afterIdx]).afterHookedMethod(param);
           } catch (Throwable t) {
                XposedBridge.log(t);

                // reset to last result (ignoring what the unexpectedlyexiting callback did)
                if (lastThrowable == null)
                   param.setResult(lastResult);
                else
                   param.setThrowable(lastThrowable);
           }
       } while (--afterIdx >= 0);

       // return
       if (param.hasThrowable())
           throw param.getThrowable();
       else
           return param.getResult();
    }

這個函數查找被掛鉤函數的掛鉤 XC_MethodHook結構體,然後執行裏邊的 beforeHookedMethod函數,再通過 invokeOriginalMethodNative執行掛鉤前的原始函數,最後再執行 afterHookedMethod 函數。

findAndHookMethod 的實現就分析完了,本質上仍然是尋找被掛鉤函數的 Method 結構體,將Method屬性改爲native ,然後對其成員 nativeFunc, registersize 等進行賦值,其中 insns 成員保存了掛鉤的詳細信息。所有被掛鉤的函數,其nativeFunc都賦值爲 xposedCallHandler 函數,該函數最終執行 XposedBridge.class 裏的 handleHookedMethod 。 handleHookedMethod 尋找 xposed模塊及xposed框架調用 findAndHookMethod 註冊的 before,after 函數,如果有,就執行,再通過invokeOriginalMethodNative 執行掛鉤前函數。

回到 initXbridgeZygote 函數,xposed 框架預先掛鉤的函數,除了 handleBindApplication外,還有
com.android.server.ServerThread 系統thread創建時觸發
hookAllConstructors(LoadedApk.class, new XC_MethodHook(){。。。} apk 包加載時觸發這個掛鉤
findAndHookMethod(“android.app.ApplicationPackageManager”, null,”getResourcesForApplication”, 。。。

  1. loadModules
/**
    * Try to load all modules defined in<code>BASE_DIR/conf/modules.list</code>
    */
   private static void loadModules(String startClassName) throwsIOException {
       BufferedReader apks = new BufferedReader(new FileReader(BASE_DIR +"conf/modules.list"));
       String apk;
       while ((apk = apks.readLine()) != null) {
           loadModule(apk, startClassName);
       }
       apks.close();
    }

xposed框架本身掛鉤的函數很少,真正的掛鉤由具體的xposed模塊實現,xposed模塊也是正常的app,安裝的時候註冊其掛鉤信息到xposed框架的modules.list 等配置裏。xposed框架初始化的時候, 加載這些模塊,並完成掛鉤函數

/**
    * Load a module from an APK by calling the init(String) method for allclasses defined
    * in assets/xposed_init.
    */
   @SuppressWarnings("deprecation")
    privatestatic void loadModule(String apk, String startClassName) {...
             while ((moduleClassName =moduleClassesReader.readLine()) != null) {// call the init(String) method of the module
                    final Object moduleInstance= moduleClass.newInstance();
                    if (startClassName == null){
                        if (moduleInstanceinstanceof IXposedHookZygoteInit) {
                           IXposedHookZygoteInit.StartupParam param = newIXposedHookZygoteInit.StartupParam();
                            param.modulePath =apk;
                            ((IXposedHookZygoteInit)moduleInstance).initZygote(param);
                        }

                        if (moduleInstanceinstanceof IXposedHookLoadPackage)
                            hookLoadPackage(newIXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));

                        if (moduleInstanceinstanceof IXposedHookInitPackageResources)
                           hookInitPackageResources(newIXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources)moduleInstance));
                    } else {
                        if (moduleInstanceinstanceof IXposedHookCmdInit) {
                           IXposedHookCmdInit.StartupParam param = newIXposedHookCmdInit.StartupParam();
                            param.modulePath =apk;
                           param.startClassName = startClassName;
                           ((IXposedHookCmdInit) moduleInstance).initCmdApp(param);
                        }
                    }
                }
}

loadModule 函數從配置文件裏讀取所有的模塊,實例化模塊類,xposed 提供了幾個接口類供xposed模塊繼承,不同的接口類對應不同的hook時機
IXposedHookZygoteInit zygote 初始化前就執行掛鉤,即loadModule執行時就掛鉤了
IXposedHookLoadPackage apk包加載的時候執行掛鉤,先將掛鉤函數保存起來,等加載apk函數執行後觸發callback (這裏的callback是xposed框架自己掛鉤的函數),再執行模塊註冊的掛鉤函數
IXposedHookInitPackageResources apk資源實例化時執行掛鉤,同上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章