zoukankan      html  css  js  c++  java
  • 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资源实例化时执行挂钩,同上
  • 相关阅读:
    jquery模拟刮刮乐
    jq默认选中每项第一个
    让一个div水平且垂直居中
    ES6模块的import和export用法总结
    linux 标准目录
    spring 注解配置
    多线程下的两种单例写法
    java版二叉树算法实现
    JAVA版A星算法实现
    对于宫格地图寻最短路径的一个广度搜索算法
  • 原文地址:https://www.cnblogs.com/jiayy/p/4305164.html
Copyright © 2011-2022 走看看