• 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 (xposedbridgesrcde
    obvandroidxposed):    if (initNative()) {
    XposedBridge.java (xposedbridgesrcde
    obvandroidxposed):    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资源实例化时执行挂钩,同上
  • 相关阅读:
    动态传参
    函数的介绍
    文件的操作
    send email with formatted table
    minimize and close window with customed winform
    python algorithm
    something important about docker
    book list
    which language is suitable for what to do
    Find Duplicate Items in list fast
  • 原文地址:https://www.cnblogs.com/jiayy/p/4305164.html
Copyright © 2020-2023  润新知