美文网首页
Xposed 原理

Xposed 原理

作者: 唯一很无敌 | 来源:发表于2019-05-22 12:31 被阅读0次

    1、Zygote

    在 Android 系统中,App Process 都是由 Zygote Process fork 出来的,而 Zygote Process 是在 Init Process 的 main 方法中初始化的。Zygote Process 在启动时会创建一个 Dalvik Instance,每当它 fork 一个新的 App Process 时,都会将这个 Dalvik Instance 复制到新的 App Process 里面去,从而使得每一个 App Process 都有一个独立的 Dalvik Instance。这也是 Xposed 选择替换 App Process 的原因。

    Zygote Process 在启动的过程中,除了会创建一个 Dalvik Instance 之外,还会将 Java Runtime lib 加载到 Process 中来,以及为 Dalvik Instance 注册一些 Android 核心类的 JNI 方法。注意,一个应用程序进程被 Zygote Process fork 出来的时候,不仅会获得Zygote Process 中的 Dalvik Instance拷贝,还会与 Zygote 一起共享 Java Runtime lib。这也就是可以将 XposedBridge 这个 jar 包加载到每一个 Android 应用程序中的原因。XposedBridge 有一个私有的 Native(JNI)方法 hookMethodNative,这个方法也在 App Process 中使用。这个函数提供一个方法对象利用 Java 的 Reflection 机制来对内置方法覆写。

    2、Hook/Replace

    Xposed 框架中真正起作用的是对方法的hook。在 Repackage 技术中,如果要对 APK 做修改,则需要修改 Smali 代码中的指令。而另一种动态修改指令的技术需要在程序运行时基于匹配搜索来替换smali代码,但因为方法声明的多样性与复杂性,这种方法也比较复杂。

    在Android系统启动的时候,Zygote Process 加载 XposedBridge 将所有需要替换的 Method 通过 JNI 方法 hookMethodNative 指向 Native 方法 xposedCallHandler,xposedCallHandler 在转入 handleHookedMethod 这个 Java 方法执行用户规定的Hook Func。

    XposedBridge 这个 jar 包含有一个私有的本地方法:hookMethodNative,该方法在附加的 App Process 程序中也得到了实现。它将一个方法对象作为输入参数(你可以使用Java的反射机制来获取这个方法)并且改变 Dalvik 中对于该方法的定义。它将该方法的类型改变为 native 并且将这个方法的实现链接到它的本地的通用类的方法。换言之,当调用那个被hook的方法时候,通用的类方法会被调用而不会对调用者有任何的影响。在 hookMethodNative 的实现中,会调用 XposedBridge 中的 handleHookedMethod这个方法来传递参数。handleHookedMethod 这个方法类似于一个统一调度的 Dispatch 例程,其对应的底层的C++函数是xposedCallHandler。而handleHookedMethod实现里面会根据一个全局结构hookedMethodCallbacks来选择相应的hook函数,并调用他们的before, after函数。

    当多模块同时Hook一个方法的时候,Xposed会自动根据Module的优先级来排序,调用顺序如下:
    A.before -> B.before -> original method -> B.after -> A.after

    2、源码分析

    2.1、XposedHelpers.findAndHookMethod(...)

    // 返回的 XC_MethodHook.Unhook 用来停止 hook ,获取 hook 的回 method、XC_MethodHook
    // unhookMethod 中主要是将 XC_MethodHook 从 CopyOnWriteSortedSet<XC_MethodHook>() 中移除
    public static XC_MethodHook.Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) {
        // 1.通过 classLoader(传 null 则使用 ClassLoader.getSystemClassLoader() 的) 反射出 Class<?> 对象
        return findAndHookMethod(findClass(className, classLoader), methodName, parameterTypesAndCallback);
    }
    
    public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
        // 2.判断有没有传入 XC_MethodHook 回调
        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];
        // 3.通过 classLoader 反射出 Method 对象
        Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback));
        return XposedBridge.hookMethod(m, callback);
    }
    
    public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
        // 只能 hook Method 和 Constructor
        if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) {
            throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
        // 不能 hook 接口
        } else if (hookMethod.getDeclaringClass().isInterface()) {
            throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
        // 不能 hook 抽象类
        } else if (Modifier.isAbstract(hookMethod.getModifiers())) {
            throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
        }
        // CopyOnWriteSortedSet<XC_MethodHook>() 是通过内部通过数组实现的有序集合
        // 使用 method 作为 key 值将 CopyOnWriteSortedSet<XC_MethodHook>() 保存起来
        // 从而判断该 method 是否被 hook 过
        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);
        if (newMethod) {
            Class<?> declaringClass = hookMethod.getDeclaringClass();
            int slot;
            Class<?>[] parameterTypes;
            Class<?> returnType;
            // 
            if (runtime == RUNTIME_ART) {
                slot = 0;
                parameterTypes = null;
                returnType = null;
            } else if (hookMethod instanceof Method) {
                /*
                    正常情况是会走到这里
                    获取 Method 编号 slot
                    获取 Method 参数 parameterTypes
                    获取 Method 返回值 returnType
                */
                slot = getIntField(hookMethod, "slot");
                parameterTypes = ((Method) hookMethod).getParameterTypes();
                returnType = ((Method) hookMethod).getReturnType();
            } else {
                slot = getIntField(hookMethod, "slot");
                parameterTypes = ((Constructor<?>) hookMethod).getParameterTypes();
                returnType = null;
            }
            AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
            // 这个方法是 调用 Jni
            hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
        }
        return callback.new Unhook(hookMethod);
    }
    

    native 层主要做的操作是:
    1.将这个需要被hook的method做了一个备份
    2.将hook信息存起来,包括了原先那个被备份的方法
    3.替换函数入口点

    相关文章

      网友评论

          本文标题:Xposed 原理

          本文链接:https://www.haomeiwen.com/subject/tjeyzqtx.html