Xposed框架分析

1. 概述

1.1 什么是hook

hook本质就是劫持函数调用,但由于处于linux用户态,每个进程都有自己独立的进程空间,所以必须先注入到所要hook的进程空间,修改其内存中的进程代码,替换其过程表的符号地址。

Android中一般通过ptrace函数附加进程,然后向远程进程注入so库,从而达到监控以及远程进程关键函数挂钩。

Hook的难点在于寻找函数的入口点、替换函数,这就涉及到函数的连接与加载机制。

Android中一般存在两种hook:sdk hook,ndk hook。native hook的难点在于理解ELF文件与学习ELF文件,Java层Hook则需要了解虚拟机的特性与java上的反射使用。另外还存在全局hook,即结合sdk hook和ndk hook,xposed就是一种典型的全局hook框架。

1.2 Xposed原理

Xposed是Android平台下的一个著名hook框架,其原理是通过修改替换/system/bin/app_process程序控制zygote进程,使app_process在启动过程中加载xposedBrider.jar包,从而完成对zygote进程以及其创建的虚拟机劫持。基于xposed框架可以制作出许多功能强大的模块,其在功能不冲突的情况下同时运作。

2. Xposed工程

Xposed源码地址为:https://github.com/rovo89
Xposed文件下载地址为:http://dl-xda.xposed.info/framework/

主要包括5部分:

  • Xposed:独立实现了一版Xposed版的zyogte,即生成用来替换/system/bin/app_process的可执行文件,该文件在系统启动时在init.rc中被调用,启动Zygote进程,init.zygote.rc中源码如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd
  • XposedBridge:Xposed框架的Java部分,编译输出为XposedBridge.jar,为开发者提供接口
  • android_art:Xposed框架的C++部分,对XposedBridge的补充
  • XposedTools:框架编译工具,因为Xposed和XposedBridge编译依赖于Android源码,而且还有一些定制化的东西。
  • XposedInstaller:Xposed插件管理和功能选择应用,界面如下图所示,其功能包括启动Xposed框架,下载和启用指定插件,或禁用插件等。
    Xposed框架分析_第1张图片

3. Xposed启动源码分析

3.1 Xposed启动Zygote

Xposed框架分析_第2张图片

3.1.1 main

init.zygote.rc中调用app_process,app_process的入口代码如下:

int main(int argc, char* const argv[])
{
    //......
    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }
    //......
    if (zygote) {
        //初始化xposed的一些参数设置,导入XposedBridge.jar
        isXposedLoaded = xposed::initialize(true, startSystemServer, NULL, argc, argv);
        //启动Zygote进程
        runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        isXposedLoaded = xposed::initialize(false, false, className, argc, argv);
        //启动普通进程
        runtimeStart(runtime, isXposedLoaded ? XPOSED_CLASS_DOTS_TOOLS : "com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        return 10;
    }
}
3.1.2 xposed::initialize
  • 初始化Xposed的一些参数,环境变量等,并将XposedBridge加入到JavaPath中
/** Initialize Xposed (unless it is disabled). */
bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]) {
#if !defined(XPOSED_ENABLE_FOR_TOOLS)
    if (!zygote)
        return false;
#endif
    // 查看设备是否解密
    if (isMinimalFramework()) {
        ALOGI("Not loading Xposed for minimal framework (encrypted device)");
        return false;
    }

    xposed->zygote = zygote;
    xposed->startSystemServer = startSystemServer;
    xposed->startClassName = className;
    xposed->xposedVersionInt = xposedVersionInt;

#if XPOSED_WITH_SELINUX
    xposed->isSELinuxEnabled   = is_selinux_enabled() == 1;
    xposed->isSELinuxEnforcing = xposed->isSELinuxEnabled && security_getenforce() == 1;
#else
    xposed->isSELinuxEnabled   = false;
    xposed->isSELinuxEnforcing = false;
#endif  // XPOSED_WITH_SELINUX
    //......

    if (startSystemServer) {
        if (!determineXposedInstallerUidGid() || !xposed::service::startAll()) {
            return false;
        }
        xposed::logcat::start();
#if XPOSED_WITH_SELINUX
    } else if (xposed->isSELinuxEnabled) {
        if (!xposed::service::startMembased()) {
            return false;
        }
#endif  // XPOSED_WITH_SELINUX
    }

#if XPOSED_WITH_SELINUX
    // Don't let any further forks access the Zygote service
    if (xposed->isSELinuxEnabled) {
        xposed::service::membased::restrictMemoryInheritance();
    }
#endif  // XPOSED_WITH_SELINUX

    // FIXME Zygote has no access to input devices, this would need to be check in system_server context
    if (zygote && !isSafemodeDisabled() && detectSafemodeTrigger(shouldSkipSafemodeDelay()))
        disableXposed();

    if (isDisabled() || (!zygote && shouldIgnoreCommand(argc, argv)))
        return false;
    // 将XposedBridge加入到JavaPath中
    return addJarToClasspath();
}
3.1.3 runtimeStart
  • 启动ART虚拟机
static void runtimeStart(AppRuntime& runtime, const char *classname, const Vector& options, bool zygote)
{
#if PLATFORM_SDK_VERSION >= 23
  runtime.start(classname, options, zygote);
#else
  //......
#endif
}
  • AppRuntime的父类是AndroidRuntime,其start函数的代码如下,主要功能包括启动虚拟机,并利用反射调用className.main()。这里className=XposedBridge。
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());

    static const String8 startSystemServer("start-system-server");

    /*
     * 'startSystemServer == true' 意味着runtime被废弃,并且不再从init.rc中启动
     * 所以我们打印除了启动事件
     */
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
        }
    }

    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /android does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);

    /* 开启虚拟机 */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * 注册android函数
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * 利用反射调用输入类的main方法,并输入相关的参数列表,此处包括两个参数,类名和选项字符串
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * 开启VM,该进程将称为VM的主要进程,并且一直运行直到VM退出
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}
3.1.3.1 AppRuntime.onVmCreated

Xposed重写了onVmCreated方法,

    virtual void onVmCreated(JNIEnv* env)
    {
        if (isXposedLoaded)
            xposed::onVmCreated(env);

        if (mClassName.isEmpty()) {
            return; // Zygote. Nothing to do here.
        }
        char* slashClassName = toSlashClassName(mClassName.string());
        mClass = env->FindClass(slashClassName);
        if (mClass == NULL) {
            ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
            env->ExceptionDescribe();
        }
        free(slashClassName);

        mClass = reinterpret_cast(env->NewGlobalRef(mClass));
    }
3.1.3.2 xposed::onVmCreated
  • 开启VM:xposed部分,主要功能包括确定当前运行时(dalvik或art),加载libxposed_art.so,初始化库文件
void onVmCreated(JNIEnv* env) {
    // 确定当前运行时,dalvik或art
    const char* xposedLibPath = NULL;
    if (!determineRuntime(&xposedLibPath)) {
        ALOGE("Could not determine runtime, not loading Xposed");
        return;
    }

    // 加载合适的 libxposed_*.so
    void* xposedLibHandle = dlopen(xposedLibPath, RTLD_NOW);
    if (!xposedLibHandle) {
        ALOGE("Could not load libxposed: %s", dlerror());
        return;
    }

    // Clear previous errors
    dlerror();

    // 初始化库文件
    bool (*xposedInitLib)(XposedShared* shared) = NULL;
    *(void **) (&xposedInitLib) = dlsym(xposedLibHandle, "xposedInitLib");
    if (!xposedInitLib)  {
        ALOGE("Could not find function xposedInitLib");
        return;
    }

#if XPOSED_WITH_SELINUX
    xposed->zygoteservice_accessFile = &service::membased::accessFile;
    xposed->zygoteservice_statFile   = &service::membased::statFile;
    xposed->zygoteservice_readFile   = &service::membased::readFile;
#endif  // XPOSED_WITH_SELINUX
    // 初始化XposedShared数据结构,并通过函数指针来调用xposed::onVmCreatedCommon
    if (xposedInitLib(xposed)) {
        xposed->onVmCreated(env);
    }
}
  • 通过库文件名称来确定运行时,dalvik或这art
static bool determineRuntime(const char** xposedLibPath) {
    FILE *fp = fopen("/proc/self/maps", "r");
    if (fp == NULL) {
        ALOGE("Could not open /proc/self/maps: %s", strerror(errno));
        return false;
    }

    bool success = false;
    char line[256];
    while (fgets(line, sizeof(line), fp) != NULL) {
        char* libname = strrchr(line, '/');
        if (!libname)
            continue;
        libname++;

        if (strcmp("libdvm.so\n", libname) == 0) {
            ALOGI("Detected Dalvik runtime");
            *xposedLibPath = XPOSED_LIB_DALVIK;
            success = true;
            break;

        } else if (strcmp("libart.so\n", libname) == 0) {
            ALOGI("Detected ART runtime");
            *xposedLibPath = XPOSED_LIB_ART;
            success = true;
            break;
        }
    }

    fclose(fp);
    return success;
}
3.1.3.3 xposedInitLib
  • 初始化XposedShared数据结构,并调用onVmCreatedCommon
bool xposedInitLib(xposed::XposedShared* shared) {
    xposed = shared;
    xposed->onVmCreated = &onVmCreatedCommon;
    return true;
}
  • 可以看到xposed->onVmCreated指向onVmCreatedCommon,所以3.1.3.2中调用的xposed->onVmCreated(env) <=> onVmCreatedCommon(env)
3.1.3.4 onVmCreatedCommon
void onVmCreatedCommon(JNIEnv* env) {
    //初始化XposedBridge环境和ZygoteService环境
    if (!initXposedBridge(env) || !initZygoteService(env)) {
        return;
    }
    // 开启虚拟机
    if (!onVmCreated(env)) {
        return;
    }

    xposedLoadedSuccessfully = true;
    return;
}
3.1.3.5 initXposedBridge
  • 主要功能是找到XposedBridge类,利用反射获取到地址,并注册XposedBridge的native函数
bool initXposedBridge(JNIEnv* env) {
    classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);
    if (classXposedBridge == NULL) {
        ALOGE("Error while loading Xposed class '%s':", CLASS_XPOSED_BRIDGE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    classXposedBridge = reinterpret_cast(env->NewGlobalRef(classXposedBridge));

    ALOGI("Found Xposed class '%s', now initializing", CLASS_XPOSED_BRIDGE);
    if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) {
        ALOGE("Could not register natives for '%s'", CLASS_XPOSED_BRIDGE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    // 初始化methodXposedBridgeHandleHookedMethod,其值为XposedBridge.handleHookedMethod的函数ID
    methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",
        "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
    if (methodXposedBridgeHandleHookedMethod == NULL) {
        ALOGE("ERROR: could not find method %s.handleHookedMethod(Member, int, Object, Object, Object[])", CLASS_XPOSED_BRIDGE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    return true;
}
  • 注册的native函数包括:
int register_natives_XposedBridge(JNIEnv* env, jclass clazz) {
    const JNINativeMethod methods[] = {
        NATIVE_METHOD(XposedBridge, hadInitErrors, "()Z"),
        NATIVE_METHOD(XposedBridge, getStartClassName, "()Ljava/lang/String;"),
        NATIVE_METHOD(XposedBridge, getRuntime, "()I"),
        NATIVE_METHOD(XposedBridge, startsSystemServer, "()Z"),
        NATIVE_METHOD(XposedBridge, getXposedVersion, "()I"),
        NATIVE_METHOD(XposedBridge, initXResourcesNative, "()Z"),
        NATIVE_METHOD(XposedBridge, hookMethodNative, "(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V"),
        NATIVE_METHOD(XposedBridge, setObjectClassNative, "(Ljava/lang/Object;Ljava/lang/Class;)V"),
        NATIVE_METHOD(XposedBridge, dumpObjectNative, "(Ljava/lang/Object;)V"),
        NATIVE_METHOD(XposedBridge, cloneToSubclassNative, "(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;"),
        NATIVE_METHOD(XposedBridge, removeFinalFlagNative, "(Ljava/lang/Class;)V"),
#if PLATFORM_SDK_VERSION >= 21
        NATIVE_METHOD(XposedBridge, invokeOriginalMethodNative,
            "!(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"),
        NATIVE_METHOD(XposedBridge, closeFilesBeforeForkNative, "()V"),
        NATIVE_METHOD(XposedBridge, reopenFilesAfterForkNative, "()V"),
#endif
#if PLATFORM_SDK_VERSION >= 24
        NATIVE_METHOD(XposedBridge, invalidateCallersNative, "([Ljava/lang/reflect/Member;)V"),
#endif
    };
    return env->RegisterNatives(clazz, methods, NELEM(methods));
}
3.1.3.6 initZygoteService
  • 主要功能是初始化classFileResult和constructorFileResult
bool initZygoteService(JNIEnv* env) {
    // 寻找类 de/robv/android/xposed/services/ZygoteService
    jclass zygoteServiceClass = env->FindClass(CLASS_ZYGOTE_SERVICE);
    if (zygoteServiceClass == NULL) {
        ALOGE("Error while loading ZygoteService class '%s':", CLASS_ZYGOTE_SERVICE);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    // 注册native函数
    if (register_natives_ZygoteService(env, zygoteServiceClass) != JNI_OK) {
        ALOGE("Could not register natives for '%s'", CLASS_ZYGOTE_SERVICE);
        env->ExceptionClear();
        return false;
    }
    //寻找类 de/robv/android/xposed/services/FileResult
    classFileResult = env->FindClass(CLASS_FILE_RESULT);
    if (classFileResult == NULL) {
        ALOGE("Error while loading FileResult class '%s':", CLASS_FILE_RESULT);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    classFileResult = reinterpret_cast<jclass>(env->NewGlobalRef(classFileResult));
    // 获取init的函数ID
    constructorFileResult = env->GetMethodID(classFileResult, "", "(JJ)V");
    if (constructorFileResult == NULL) {
        ALOGE("ERROR: could not find constructor %s(long, long)", CLASS_FILE_RESULT);
        logExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    return true;
}
  • 注册的native方法为
int register_natives_ZygoteService(JNIEnv* env, jclass clazz) {
    const JNINativeMethod methods[] = {
        NATIVE_METHOD(ZygoteService, checkFileAccess, "(Ljava/lang/String;I)Z"),
        NATIVE_METHOD(ZygoteService, statFile, "(Ljava/lang/String;)L" CLASS_FILE_RESULT ";"),
        NATIVE_METHOD(ZygoteService, readFile, "(Ljava/lang/String;)[B"),
    };
    return env->RegisterNatives(clazz, methods, NELEM(methods));
}
3.1.3.7 onVmCreated
  • 此函数在启动VM过程中很早阶段被调用,主要功能为获取invokeOriginalMethodNative函数地址,并设置native方法
bool onVmCreated(JNIEnv* env) {
    if (!initMemberOffsets(env))
        return false;

    jclass classMiuiResources = env->FindClass(CLASS_MIUI_RESOURCES);
    if (classMiuiResources != NULL) {
        ClassObject* clazz = (ClassObject*)dvmDecodeIndirectRef(dvmThreadSelf(), classMiuiResources);
        if (dvmIsFinalClass(clazz)) {
            ALOGD("Removing final flag for class '%s'", CLASS_MIUI_RESOURCES);
            clazz->accessFlags &= ~ACC_FINAL;
        }
    }
    env->ExceptionClear();

    Method* xposedInvokeOriginalMethodNative = (Method*) env->GetStaticMethodID(classXposedBridge, "invokeOriginalMethodNative",
        "(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
    if (xposedInvokeOriginalMethodNative == NULL) {
        ALOGE("ERROR: could not find method %s.invokeOriginalMethodNative(Member, int, Class[], Class, Object, Object[])", CLASS_XPOSED_BRIDGE);
        dvmLogExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }
    dvmSetNativeFunc(xposedInvokeOriginalMethodNative, XposedBridge_invokeOriginalMethodNative, NULL);

    objectArrayClass = dvmFindArrayClass("[Ljava/lang/Object;", NULL);
    if (objectArrayClass == NULL) {
        ALOGE("Error while loading Object[] class");
        dvmLogExceptionStackTrace();
        env->ExceptionClear();
        return false;
    }

    return true;
}

3.2 XposedBridge启动

下面我们来研究下调用XposedBridge.main之后的逻辑,其主要流程图如下。
Xposed框架分析_第3张图片

3.2.1 XposedBridge.main

XposedBridge.jar的入口函数,主要功能包括初始化资源,配置SELinux,加载模块等。

    protected static void main(String[] args) {
        // Initialize the Xposed framework and modules
        try {
            if (!hadInitErrors()) {
                initXResources();

                SELinuxHelper.initOnce();
                SELinuxHelper.initForProcess(null);

                runtime = getRuntime();
                XPOSED_BRIDGE_VERSION = getXposedVersion();

                if (isZygote) {
                    XposedInit.hookResources();
                    XposedInit.initForZygote();
                }

                XposedInit.loadModules();
            } else {
                Log.e(TAG, "Not initializing Xposed because of previous errors");
            }
        } catch (Throwable t) {
            Log.e(TAG, "Errors during Xposed initialization", t);
            disableHooks = true;
        }

        // Call the original startup code
        if (isZygote) {
            ZygoteInit.main(args);
        } else {
            RuntimeInit.main(args);
        }
    }
3.2.1.1 XposedBridge.initXResource
  • 初始化Xposed的资源,包括载入Dex文件,drawables文件,类加载器等
    private static void initXResources() throws IOException {
        // 创建 XResources父类
        Resources res = Resources.getSystem();
        File resDexFile = ensureSuperDexFile("XResources", res.getClass(), Resources.class);

        // 创建 XTypedArray父类
        Class taClass = TypedArray.class;
        try {
            TypedArray ta = res.obtainTypedArray(res.getIdentifier("preloaded_drawables", "array", "android"));
            taClass = ta.getClass();
            ta.recycle();
        } catch (Resources.NotFoundException nfe) {
            XposedBridge.log(nfe);
        }
        Runtime.getRuntime().gc();
        File taDexFile = ensureSuperDexFile("XTypedArray", taClass, TypedArray.class);

        // 为创建类注入一个ClassLoader,来作为XposedBridge ClassLoader的父类
        ClassLoader myCL =  XposedBridge.class.getClassLoader();
        String paths = resDexFile.getAbsolutePath() + File.pathSeparator + taDexFile.getAbsolutePath();
        PathClassLoader dummyCL = new PathClassLoader(paths, myCL.getParent());
        setObjectField(myCL, "parent", dummyCL);
    }
3.2.2 SELinuxHelper.initOnce

初始化sIsSELinuxEnabled 变量

static void initOnce() {
        try {
            sIsSELinuxEnabled = SELinux.isSELinuxEnabled();
        } catch (NoClassDefFoundError ignored) {}
    }
3.2.3 SELinuxHelper.initForProcess
  • 如果系统SELinux开启状态,启动新的ZygoteService,启动新的system_server
static void initForProcess(String packageName) {
        if (sIsSELinuxEnabled) {
            if (packageName == null) {  // Zygote
                sServiceAppDataFile = new ZygoteService();
            } else if (packageName.equals("android")) {  //system_server
                sServiceAppDataFile = BinderService.getService(BinderService.TARGET_APP);
            } else {  // app
                sServiceAppDataFile = new DirectAccessService();
            }
        } else {
            sServiceAppDataFile = new DirectAccessService();
        }
    }
3.2.4 XposedInit.hookResources
static void hookResources() throws Throwable {
        if (SELinuxHelper.getAppDataFileService().checkFileExists(BASE_DIR + "conf/disable_resources")) {
            Log.w(TAG, "Found " + BASE_DIR + "conf/disable_resources, not hooking resources");
            disableResources = true;
            return;
        }
        //初始化Resource的native方法
        if (!XposedBridge.initXResourcesNative()) {
            Log.e(TAG, "Cannot hook resources");
            disableResources = true;
            return;
        }

        /*
         * getTopLevelResources(a)
         *   -> getTopLevelResources(b)
         *     -> key = new ResourcesKey()
         *     -> r = new Resources()
         *     -> mActiveResources.put(key, r)
         *     -> return r
         */

        final Class classGTLR;
        final Class classResKey;
        final ThreadLocal latestResKey = new ThreadLocal<>();
        // 根据不同的SDK版本做不同的hook操作
        if (Build.VERSION.SDK_INT <= 18) {
            classGTLR = ActivityThread.class;
            classResKey = Class.forName("android.app.ActivityThread$ResourcesKey");
        } else {
            classGTLR = Class.forName("android.app.ResourcesManager");
            classResKey = Class.forName("android.content.res.ResourcesKey");
        }

        if (Build.VERSION.SDK_INT >= 24) {
            hookAllMethods(classGTLR, "getOrCreateResources", new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    //......
                    }
                }
            });
        } else {
            hookAllConstructors(classResKey, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    latestResKey.set(param.thisObject);
                }
            });

            hookAllMethods(classGTLR, "getTopLevelResources", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    latestResKey.set(null);
                }

                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    //......
                }
            });

            if (Build.VERSION.SDK_INT >= 19) {
                // This method exists only on CM-based ROMs
                hookAllMethods(classGTLR, "getTopLevelThemedResources", new XC_MethodHook() {
                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        String resDir = (String) param.args[0];
                        cloneToXResources(param, resDir);
                    }
                });
            }
        }

        // Invalidate callers of methods overridden by XTypedArray
        if (Build.VERSION.SDK_INT >= 24) {
            Set methods = getOverriddenMethods(XResources.XTypedArray.class);
            XposedBridge.invalidateCallersNative(methods.toArray(new Member[methods.size()]));
        }

        // 利用XTypedArrays代替TypedArrays
        hookAllConstructors(TypedArray.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                TypedArray typedArray = (TypedArray) param.thisObject;
                Resources res = typedArray.getResources();
                if (res instanceof XResources) {
                    XposedBridge.setObjectClass(typedArray, XResources.XTypedArray.class);
                }
            }
        });

        // 替换系统资源
        XResources systemRes = (XResources) XposedBridge.cloneToSubclass(Resources.getSystem(), XResources.class);
        systemRes.initObject(null);
        setStaticObjectField(Resources.class, "mSystem", systemRes);

        XResources.init(latestResKey);
    } 
  
3.2.5 XposedInit.initForZygote

为初始化Zygote而hook一些函数,这些函数都是启动Zygote进程时,需要调用的关键函数。包括
* nativeForkAndSpecialize
* nativeForkSystemServer
* handleBindApplication
* com.android.server.ServerThread.run/initAndLoop
* ActivityThread.systemMain
* LoadedApk()
* android.app.ApplicationPackageManager.getResourcesForApplication
同时还适配了一些手机系统。

static void initForZygote() throws Throwable {
    if (needsToCloseFilesForFork()) {
        XC_MethodHook callback = new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.closeFilesBeforeForkNative();
            }

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                XposedBridge.reopenFilesAfterForkNative();
            }
        };

        Class zygote = findClass("com.android.internal.os.Zygote", null);
        hookAllMethods(zygote, "nativeForkAndSpecialize", callback);
        hookAllMethods(zygote, "nativeForkSystemServer", callback);
    }

    final HashSet loadedPackagesInProcess = new HashSet<>(1);

    // normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
    findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            ActivityThread activityThread = (ActivityThread) param.thisObject;
            ApplicationInfo appInfo = (ApplicationInfo) getObjectField(param.args[0], "appInfo");
            String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
            SELinuxHelper.initForProcess(reportedPackageName);
            ComponentName instrumentationName = (ComponentName) getObjectField(param.args[0], "instrumentationName");
            if (instrumentationName != null) {
                Log.w(TAG, "Instrumentation detected, disabling framework for " + reportedPackageName);
                XposedBridge.disableHooks = true;
                return;
            }
            CompatibilityInfo compatInfo = (CompatibilityInfo) getObjectField(param.args[0], "compatInfo");
            if (appInfo.sourceDir == null)
                return;

            setObjectField(activityThread, "mBoundApplication", param.args[0]);
            loadedPackagesInProcess.add(reportedPackageName);
            LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
            XResources.setPackageNameForResDir(appInfo.packageName, loadedApk.getResDir());

            XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
            lpparam.packageName = reportedPackageName;
            lpparam.processName = (String) getObjectField(param.args[0], "processName");
            lpparam.classLoader = loadedApk.getClassLoader();
            lpparam.appInfo = appInfo;
            lpparam.isFirstApplication = true;
            XC_LoadPackage.callAll(lpparam);

            if (reportedPackageName.equals(INSTALLER_PACKAGE_NAME))
                hookXposedInstaller(lpparam.classLoader);
        }
    });

    // system_server initialization
    if (Build.VERSION.SDK_INT < 21) {
        findAndHookMethod("com.android.server.ServerThread", null,
                Build.VERSION.SDK_INT < 19 ? "run" : "initAndLoop", new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        SELinuxHelper.initForProcess("android");
                        loadedPackagesInProcess.add("android");

                        XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
                        lpparam.packageName = "android";
                        lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
                        lpparam.classLoader = XposedBridge.BOOTCLASSLOADER;
                        lpparam.appInfo = null;
                        lpparam.isFirstApplication = true;
                        XC_LoadPackage.callAll(lpparam);
                    }
                });
    } else if (startsSystemServer) {
        findAndHookMethod(ActivityThread.class, "systemMain", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                final ClassLoader cl = Thread.currentThread().getContextClassLoader();
                findAndHookMethod("com.android.server.SystemServer", cl, "startBootstrapServices", new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        SELinuxHelper.initForProcess("android");
                        loadedPackagesInProcess.add("android");

                        XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
                        lpparam.packageName = "android";
                        lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
                        lpparam.classLoader = cl;
                        lpparam.appInfo = null;
                        lpparam.isFirstApplication = true;
                        XC_LoadPackage.callAll(lpparam);

                        // Huawei
                        try {
                            findAndHookMethod("com.android.server.pm.HwPackageManagerService", cl, "isOdexMode", XC_MethodReplacement.returnConstant(false));
                        } catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {}

                        try {
                            String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
                            findAndHookMethod(className, cl, "dexEntryExists", String.class, XC_MethodReplacement.returnConstant(true));
                        } catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {}
                    }
                });
            }
        });
    }

    // when a package is loaded for an existing process, trigger the callbacks as well
    hookAllConstructors(LoadedApk.class, new XC_MethodHook() {
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            LoadedApk loadedApk = (LoadedApk) param.thisObject;

            String packageName = loadedApk.getPackageName();
            XResources.setPackageNameForResDir(packageName, loadedApk.getResDir());
            if (packageName.equals("android") || !loadedPackagesInProcess.add(packageName))
                return;

            if (!getBooleanField(loadedApk, "mIncludeCode"))
                return;

            XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
            lpparam.packageName = packageName;
            lpparam.processName = AndroidAppHelper.currentProcessName();
            lpparam.classLoader = loadedApk.getClassLoader();
            lpparam.appInfo = loadedApk.getApplicationInfo();
            lpparam.isFirstApplication = false;
            XC_LoadPackage.callAll(lpparam);
        }
    });

    findAndHookMethod("android.app.ApplicationPackageManager", null, "getResourcesForApplication",
            ApplicationInfo.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    ApplicationInfo app = (ApplicationInfo) param.args[0];
                    XResources.setPackageNameForResDir(app.packageName,
                            app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir);
                }
            });

    // MIUI
    if (findFieldIfExists(ZygoteInit.class, "BOOT_START_TIME") != null) {
        setStaticLongField(ZygoteInit.class, "BOOT_START_TIME", XposedBridge.BOOT_START_TIME);
    }

    // Samsung
    if (Build.VERSION.SDK_INT >= 24) {
        Class zygote = findClass("com.android.internal.os.Zygote", null);
        try {
            setStaticBooleanField(zygote, "isEnhancedZygoteASLREnabled", false);
        } catch (NoSuchFieldError ignored) {
        }
    }
}
3.2.6 XposedInit.loadModules

将modules.list文件中的所有Xposed相关的模块全部加载。

static void loadModules() throws IOException {
        final String filename = BASE_DIR + "conf/modules.list";
        BaseService service = SELinuxHelper.getAppDataFileService();
        if (!service.checkFileExists(filename)) {
            Log.e(TAG, "Cannot load any modules because " + filename + " was not found");
            return;
        }

        ClassLoader topClassLoader = XposedBridge.BOOTCLASSLOADER;
        ClassLoader parent;
        while ((parent = topClassLoader.getParent()) != null) {
            topClassLoader = parent;
        }

        InputStream stream = service.getFileInputStream(filename);
        BufferedReader apks = new BufferedReader(new InputStreamReader(stream));
        String apk;
        while ((apk = apks.readLine()) != null) {
            loadModule(apk, topClassLoader);
        }
        apks.close();
    }
3.2.6.1 XposedInit.loadModule

过程可以概述为:
1. 加载APK的dex文件,如需解压则解压后加载dex文件
2. 加载dex文件中的所有与Xposed相关的类,包括IXposedHookZygoteInit,IXposedHookLoadPackage,IXposedHookInitPackageResources,IXposedHookCmdInit

    private static void loadModule(String apk, ClassLoader topClassLoader) {
        Log.i(TAG, "Loading modules from " + apk);

        if (!new File(apk).exists()) {
            Log.e(TAG, "  File does not exist");
            return;
        }

        DexFile dexFile;
        try {
            dexFile = new DexFile(apk);
        } catch (IOException e) {
            Log.e(TAG, "  Cannot load module", e);
            return;
        }

        if (dexFile.loadClass(INSTANT_RUN_CLASS, topClassLoader) != null) {
            Log.e(TAG, "  Cannot load module, please disable \"Instant Run\" in Android Studio.");
            closeSilently(dexFile);
            return;
        }

        if (dexFile.loadClass(XposedBridge.class.getName(), topClassLoader) != null) {
            Log.e(TAG, "  Cannot load module:");
            Log.e(TAG, "  The Xposed API classes are compiled into the module's APK.");
            Log.e(TAG, "  This may cause strange issues and must be fixed by the module developer.");
            Log.e(TAG, "  For details, see: http://api.xposed.info/using.html");
            closeSilently(dexFile);
            return;
        }

        closeSilently(dexFile);

        ZipFile zipFile = null;
        InputStream is;
        try {
            zipFile = new ZipFile(apk);
            ZipEntry zipEntry = zipFile.getEntry("assets/xposed_init");
            if (zipEntry == null) {
                Log.e(TAG, "  assets/xposed_init not found in the APK");
                closeSilently(zipFile);
                return;
            }
            is = zipFile.getInputStream(zipEntry);
        } catch (IOException e) {
            Log.e(TAG, "  Cannot read assets/xposed_init in the APK", e);
            closeSilently(zipFile);
            return;
        }

        ClassLoader mcl = new PathClassLoader(apk, XposedBridge.BOOTCLASSLOADER);
        BufferedReader moduleClassesReader = new BufferedReader(new InputStreamReader(is));
        try {
            String moduleClassName;
            while ((moduleClassName = moduleClassesReader.readLine()) != null) {
                moduleClassName = moduleClassName.trim();
                if (moduleClassName.isEmpty() || moduleClassName.startsWith("#"))
                    continue;

                try {
                    Log.i(TAG, "  Loading class " + moduleClassName);
                    Class moduleClass = mcl.loadClass(moduleClassName);

                    if (!IXposedMod.class.isAssignableFrom(moduleClass)) {
                        Log.e(TAG, "    This class doesn't implement any sub-interface of IXposedMod, skipping it");
                        continue;
                    } else if (disableResources && IXposedHookInitPackageResources.class.isAssignableFrom(moduleClass)) {
                        Log.e(TAG, "    This class requires resource-related hooks (which are disabled), skipping it.");
                        continue;
                    }

                    final Object moduleInstance = moduleClass.newInstance();
                    if (XposedBridge.isZygote) {
                    //加载所有实现这些接口的类
                        if (moduleInstance instanceof IXposedHookZygoteInit) {
                            IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
                            param.modulePath = apk;
                            param.startsSystemServer = startsSystemServer;
                            ((IXposedHookZygoteInit) moduleInstance).initZygote(param);
                        }

                        if (moduleInstance instanceof IXposedHookLoadPackage)
                            XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));

                        if (moduleInstance instanceof IXposedHookInitPackageResources)
                            XposedBridge.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);
                        }
                    }
                } catch (Throwable t) {
                    Log.e(TAG, "    Failed to load class " + moduleClassName, t);
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "  Failed to load module from " + apk, e);
        } finally {
            closeSilently(is);
            closeSilently(zipFile);
        }
    }
3.2.7 IXposedHookZygoteInit.initZygote
  • 一般在实现模块中重写该方法
public interface IXposedHookZygoteInit extends IXposedMod {
    /**
     * Zygote启动过程早期调用
     * @param startupParam 模块和启动进程的细节
     * @throws Throwable everything is caught, but will prevent further initialization of the module.
     */
    void initZygote(StartupParam startupParam) throws Throwable;
}
3.2.8 IXposedHookLoadPackage.handleLoadPackage
public interface IXposedHookLoadPackage extends IXposedMod {
    /**
     * 模块APP启动时调用该函数,它在before之前调用
     * {@link Application#onCreate} is called.
     * 此处自制模块可设置自己特殊的hooks
     *
     * @param lpparam app信息
     * @throws Throwable Everything the callback throws is caught and logged.
     */
    void handleLoadPackage(LoadPackageParam lpparam) throws Throwable;
}
3.2.9 IXposedHookPackageResources.handleInitPackageResources
public interface IXposedHookInitPackageResources extends IXposedMod {
    /**
     * APP资源初始化时调用,该函数可调用特殊的XResources类方法来代替资源
     * @param 资源参数信息
     * @throws Throwable Everything the callback throws is caught and logged.
     */
    void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable;

    /** @hide */
    final class Wrapper extends XC_InitPackageResources {
        private final IXposedHookInitPackageResources instance;
        public Wrapper(IXposedHookInitPackageResources instance) {
            this.instance = instance;
        }
        @Override
        public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
            instance.handleInitPackageResources(resparam);
        }
    }
}

3.3 hook过程函数调用分析

Xposed框架分析_第4张图片

3.3.1 IXposedHookLoadPackage.handleLoadPackage

此处与3.2.8节相同

3.3.2 XC_MothodHook.beforeHookedMethod
public abstract class XC_MethodHook extends XCallback {
    /**
     * 于被hook函数之前调用,一般在自制模块中重写
     * 可以使用{@link MethodHookParam#setResult}和{@link MethodHookParam#setThrowable}来阻止被hook函数的调用
     * 注意实现中不应调用{@code super(param)}
     *
     * @param param 被hook函数信息
     * @throws Throwable Everything the callback throws is caught and logged.
     */
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {}
}
3.3.3 XC_MothodHook.afterHookedMethod
public abstract class XC_MethodHook extends XCallback {
    /**
     * 于被hook函数之后调用,一般在自制模块中重写
     * 可以使用 {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable}来修改hook函数的返回值
     * 注意实现中不应调用{@code super(param)}
     *
     * @param param 被hook函数信息
     * @throws Throwable Everything the callback throws is caught and logged.
     */
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {}
}
3.3.4 XposedHelper.findAndHookMethod
  • 主要功能是构造回调类,并调用XposedBridge.hookMethod方法
     /**
     * 查询函数并hook它,最后有一个参数需为hook的回调函数
     * @param className 待hook的函数对应的类名
     * @param classLoader 类加载器,解析目标和类参数
     * @param methodName 目标函数名
     * @param parameterTypesAndCallback 目标函数的参数类型和回调
     * @throws NoSuchMethodError In case the method was not found.
     * @throws ClassNotFoundError In case the target class or one of the parameter types couldn't be resolved.
     * @return An object which can be used to remove the callback again.
     */

    public static XC_MethodHook.Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) {
        return findAndHookMethod(findClass(className, classLoader), methodName, parameterTypesAndCallback);
    }

    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);
    }
3.3.5 XposedBridge.hookMethod
主要功能是将回调加入到数组中,判断函数是否是第一次被hook,然后调用native层函数
    /**
     * hook任何带有特殊回调的函数(或构造函数),利用一些wrapper让实现更为简单
     *
     * @param hookMethod 被hook的方法
     * @param callback 回调
     * @return 一个可以用来删除hook的对象
     *
     * @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)
     * @see XposedHelpers#findAndHookMethod(Class, String, Object...)
     * @see #hookAllMethods
     * @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)
     * @see XposedHelpers#findAndHookConstructor(Class, Object...)
     * @see #hookAllConstructors
     */
    public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
        // 判断被hook的是否为普通函数或构造函数
        if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor)) {
            throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
        } else if (hookMethod.getDeclaringClass().isInterface()) {
            throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
        } else if (Modifier.isAbstract(hookMethod.getModifiers())) {
            throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
        }

        boolean newMethod = false;
        // 将callback添加到列表中
        CopyOnWriteSortedSet callbacks;
        synchronized (sHookedMethodCallbacks) {
            callbacks = sHookedMethodCallbacks.get(hookMethod);
            if (callbacks == null) {
                callbacks = new CopyOnWriteSortedSet<>();
                sHookedMethodCallbacks.put(hookMethod, callbacks);
                newMethod = true;
            }
        }
        callbacks.add(callback);
        // 如果该方法第一次被hook
        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) {
                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);
            // 将beforehook和afterhook加入additionalInfo中,并调用native层hook方法
            hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
        }

        return callback.new Unhook(hookMethod);
    }
3.3.5.1 XposedBridge.hookMethodNative

这个函数在3.1.3.5章节中注册native方法,其对应的native实现为XposedBridge_hookMethodNative

    /**
     * 拦截每次特殊函数调用,并调用一个处理函数来替代
     * @param method 用来拦截的函数
     */
    private native synchronized static void hookMethodNative(Member method, Class declaringClass, int slot, Object additionalInfo);
3.3.6 XposedBridge_hookMethodNative

–>[Xposed][libxposed_art.cpp]

  • hookMethodNative的Native层实现
void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod,
            jobject, jint, jobject javaAdditionalInfo) {
    // Detect usage errors.
    ScopedObjectAccess soa(env);
    if (javaReflectedMethod == nullptr) {
#if PLATFORM_SDK_VERSION >= 23
        ThrowIllegalArgumentException("method must not be null");
#else
        ThrowIllegalArgumentException(nullptr, "method must not be null");
#endif
        return;
    }

    // 获取函数的ArtMethod,以hook
    ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);

    // hook函数
    artMethod->EnableXposedHook(soa, javaAdditionalInfo);
}
3.3.7 FromReflectedMethod

–>[xposed-android-art] [art_method.cc]

ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
                                          jobject jlr_method) {
  auto* abstract_method = soa.Decode<mirror::AbstractMethod*>(jlr_method);
  DCHECK(abstract_method != nullptr);
  return abstract_method->GetArtMethod();
}
3.3.8 EnableXposedHook

–>[xposed-android-art] [art_method.cc]

EnableXposedHook,会创建一个hookInfo 将original、before、after 方法存入,然后通过 SetEntryPointFromJni 方法将hookinfo 保存到ArtMethod 中。
在之后 通过SetEntryPointFromInterpreter 方法强制设置 java 方法为,本地机器码执行
通过SetEntryPointFromQuickCompiledCode 改变机器码执行时的函数入口为 art_quick_proxy_invoke_handler
到此函数的整个hook 流程就已经完成。

void ArtMethod::EnableXposedHook(ScopedObjectAccess& soa, jobject additional_info) {
  if (UNLIKELY(IsXposedHookedMethod())) {
    // 如果函数已被hook则返回
    return;
  } else if (UNLIKELY(IsXposedOriginalMethod())) {
    // Xposed功能未打开则返回
    ThrowIllegalArgumentException(StringPrintf("Cannot hook the method backup: %s", PrettyMethod(this).c_str()).c_str());
    return;
  }

  // 创建ArtMethod对象的备份
  auto* cl = Runtime::Current()->GetClassLinker();
  auto* linear_alloc = cl->GetAllocatorForClassLoader(GetClassLoader());
  ArtMethod* backup_method = cl->CreateRuntimeMethod(linear_alloc);
  backup_method->CopyFrom(this, cl->GetImagePointerSize());
   //添加设置 backup_method的flags 为kAccXposedOriginalMethod,表示其为hook方法的原方法
  backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod);


  // 创建一个函数或构造函数对象,以备份ArtMethod对象
  mirror::AbstractMethod* reflected_method;
  if (IsConstructor()) {
    reflected_method = mirror::Constructor::CreateFromArtMethod(soa.Self(), backup_method);
  } else {
    reflected_method = mirror::Method::CreateFromArtMethod(soa.Self(), backup_method);
  }
  reflected_method->SetAccessible<false>(true);


  //创建hookinfo,用于保存,hook的原方法,与其 before && after 方法
  XposedHookInfo* hook_info = reinterpret_cast<XposedHookInfo*>(linear_alloc->Alloc(soa.Self(), sizeof(XposedHookInfo)));
  hook_info->reflected_method = soa.Vm()->AddGlobalRef(soa.Self(), reflected_method);
  //添加hook的方法(before && after 方法)
  hook_info->additional_info = soa.Env()->NewGlobalRef(additional_info);
  //添加 backup_method(原方法)
  hook_info->original_method = backup_method;

  ScopedThreadSuspension sts(soa.Self(), kSuspended);
  jit::ScopedJitSuspend sjs;
  gc::ScopedGCCriticalSection gcs(soa.Self(),
                                  gc::kGcCauseXposed,
                                  gc::kCollectorTypeXposed);
  ScopedSuspendAll ssa(__FUNCTION__);

  cl->InvalidateCallersForMethod(soa.Self(), this);

  jit::Jit* jit = art::Runtime::Current()->GetJit();
  if (jit != nullptr) {
    jit->GetCodeCache()->MoveObsoleteMethod(this, backup_method);
  }

  SetEntryPointFromJniPtrSize(reinterpret_cast<uint8_t*>(hook_info), sizeof(void*));
  // 重新设置 EntryPointFromQuickCompiledCode 值为GetQuickProxyInvokeHandler 的返回值art_quick_proxy_invoke_handler
  //以此达到hook的目的
  SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());

  SetCodeItemOffset(0);

  // 调用获取flag
  const uint32_t kRemoveFlags = kAccNative | kAccSynchronized | kAccAbstract | kAccDefault | kAccDefaultConflict;
  SetAccessFlags((GetAccessFlags() & ~kRemoveFlags) | kAccXposedHookedMethod);

  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
  Runtime::Current()->GetThreadList()->ForEach(StackReplaceMethodAndInstallInstrumentation, this);
}

//GetQuickProxyInvokeHandler 用于获取xposed 框架下,函数以本地机器码执行时的首地址(即java层函数的执行从这个C 函数还是的)
static inline const void* GetQuickProxyInvokeHandler() {
    return reinterpret_cast<const void*>(art_quick_proxy_invoke_handler);
}

你可能感兴趣的:(android,hook)