android hook 框架 xposed 如何實現掛鈎


Android so注入-libinject2 簡介、編譯、運行

Android so注入-libinject2  如何實現so注入

Android so注入-Libinject 如何實現so注入

Android so注入掛鈎-Adbi 框架簡介、編譯、運行

Android so注入掛鈎-Adbi 框架如何實現so注入

Android so注入掛鈎-Adbi 框架如何實現so函數掛鈎

Android so注入掛鈎-Adbi 框架如何實現dalvik函數掛鈎

Android dalvik掛鈎-Xposed框架如何實現注入

Android dalvik掛鈎-Xposed框架如何實現掛鈎

 

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

一. 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

 

1. 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類或函數的標識

 

二, initXbridgeZygote 

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

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

首先,掛鈎了 ActivityThread 類的  handleBindApplication 函數,這個函數是在android ams 系統創建新進程成功后在新進程內部調用的,掛鈎這個函數,可以在新進程創建后做一些事情

這里調用了一個函數,實現了掛鈎  findAndHookMethod 。這個函數定義在 XposedHelpers.java

 

XposedHelpers.class

public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
        if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof XC_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.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
       ....

        boolean newMethod = false;
        CopyOnWriteSortedSet<XC_MethodHook> callbacks;
        synchronized (sHookedMethodCallbacks) {
            callbacks = sHookedMethodCallbacks.get(hookMethod);
            if (callbacks == null) {
                callbacks = new CopyOnWriteSortedSet<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 void de_robv_android_xposed_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
    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 Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,
            Object thisObject, Object[] args) throws Throwable {
        AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;

        if (disableHooks) {
            try {
                return invokeOriginalMethodNative(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 {
                return invokeOriginalMethodNative(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 exiting callback 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 unexpectedly exiting 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", 。。。

3. loadModules
/**
     * Try to load all modules defined in <code>BASE_DIR/conf/modules.list</code>
     */
    private static void loadModules(String startClassName) throws IOException {
        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 all classes defined
     * in <code>assets/xposed_init</code>.
     */
    @SuppressWarnings("deprecation")
    private static 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 (moduleInstance instanceof IXposedHookZygoteInit) { IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam(); param.modulePath = apk; ((IXposedHookZygoteInit) moduleInstance).initZygote(param); } if (moduleInstance instanceof IXposedHookLoadPackage) hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance)); if (moduleInstance instanceof IXposedHookInitPackageResources) hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance)); } else { if (moduleInstance instanceof IXposedHookCmdInit) { IXposedHookCmdInit.StartupParam param = new IXposedHookCmdInit.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資源實例化時執行掛鈎,同上


免責聲明!

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



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