-
-
[原创]ART/Dalvik环境下Xposed实现异同分析
-
2021-4-29 19:17 2439
-
写了一部分,先占个坑,后续补完
1. ART/Dalvik
环境异同
2. Xposed
底层实现逻辑变更
了解到ART/Dalvik
的异同之后,我们再来看看我们的主题,当Android虚拟机
从Dalvik
到ART
,Xposed
针对这种变化对其底层实现逻辑做了哪些变更呢?
首先从源头入手,Xposed
框架在编译app_main
文件时做了系统适配处理,针对不同版本的系统使用不同的app_main
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # Android.mk # PLATFORM_SDK_VERSION字段 # 参考xref: /build/core/version_defaults.mk # PLATFORM_VERSION := 7.1.2 # PLATFORM_SDK_VERSION := 25 # 根据系统版本不同,7以上的版本使用app_main2.cpp来编译 ifeq ( 1 ,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \> = 21 ))) LOCAL_SRC_FILES : = app_main2.cpp LOCAL_MULTILIB : = both LOCAL_MODULE_STEM_32 : = app_process32_xposed LOCAL_MODULE_STEM_64 : = app_process64_xposed else LOCAL_SRC_FILES : = app_main.cpp LOCAL_MODULE_STEM : = app_process_xposed endif ########################################################## # Library for Dalvik-/ART-specific functions ########################################################## # 针对libxposed_xxx.cpp文件也是同样的道理 ifeq ( 1 ,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \> = 21 ))) include frameworks / base / cmds / xposed / ART.mk else include frameworks / base / cmds / xposed / Dalvik.mk endif |
那么下面我们分析下这两份文件的实现
2.1 app_main
文件的实现变更
可以看到,在两份文件中针对AppRuntime
类的修改大致是相同的,同样都是调用了xposed::onVmCreated
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | / / xposed.cpp / * * Load the libxposed_ * .so library for the currently active runtime. * / / / 同样都是调用了xposed.cpp的onVmCreated函数 void onVmCreated(JNIEnv * env) { / / Determine the currently active runtime / / 初始化xposed lib路径 const char * xposedLibPath = NULL; / / 轮询 / proc / self / maps结果,查看系统到底是art还是dalvik再决定赋值 / / xposed.h / / #define XPOSED_LIB_DALVIK XPOSED_LIB_DIR "libxposed_dalvik.so" / / #define XPOSED_LIB_ART XPOSED_LIB_DIR "libxposed_art.so" if (!determineRuntime(&xposedLibPath)) { ALOGE( "Could not determine runtime, not loading Xposed" ); return ; } / / 通过dlopen so文件,so文件由之前的determineRuntime函数配置 / / Load the suitable libxposed_ * .so for it void * xposedLibHandle = dlopen(xposedLibPath, RTLD_NOW); if (!xposedLibHandle) { ALOGE( "Could not load libxposed: %s" , dlerror()); return ; } / / Clear previous errors dlerror(); / / Initialize the library / / dlsym 执行so文件中xposedInitLib函数,so文件就是上面被赋值的文件 bool ( * xposedInitLib)(XposedShared * shared) = NULL; * (void * * ) (&xposedInitLib) = dlsym(xposedLibHandle, "xposedInitLib" ); if (!xposedInitLib) { ALOGE( "Could not find function xposedInitLib" ); return ; } / / xposedInitLib函数如下,赋予xposed的onVmCreated为onVmCreatedCommon / / 也就是libxposed_common.cpp的onVmCreatedCommon方法 / / bool xposedInitLib(XposedShared * shared) { / / xposed = shared; / / xposed - >onVmCreated = &onVmCreatedCommon; / / return true; / / } #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 / / 方法返回true,执行onVmCreatedCommon方法,到目前为止,流程都是相同的 if (xposedInitLib(xposed)) { xposed - >onVmCreated(env); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | / / libxposed_common.cpp / / 两份文件公有部分 void onVmCreatedCommon(JNIEnv * env) { / / 判断xposed_bridge.jar是否正常载入以及zygote服务启动是否正常 if (!initXposedBridge(env) || !initZygoteService(env)) { return ; } / / 判断虚拟机是否正常启动,这里调用onVmCreated就是libxposed_xx.so文件中自己的onVmCreated函数了,这里应该就是第一个变化点 if (!onVmCreated(env)) { return ; } / / 多重判断之后,返回xposed已正确安装标识 xposedLoadedSuccessfully = true; return ; } bool initXposedBridge(JNIEnv * env) { / / #define CLASS_XPOSED_BRIDGE "de/robv/android/xposed/XposedBridge" / / 通过FindClass获取XposedBridge类,判断xposed_bridge类是否正常加载 classXposedBridge = env - >FindClass(CLASS_XPOSED_BRIDGE); if (classXposedBridge = = NULL) { ALOGE( "Error while loading Xposed class '%s':" , CLASS_XPOSED_BRIDGE); logExceptionStackTrace(); env - >ExceptionClear(); return false; } / / PS:这里没有理解做了什么 classXposedBridge = reinterpret_cast<jclass>(env - >NewGlobalRef(classXposedBridge)); ALOGI( "Found Xposed class '%s', now initializing" , CLASS_XPOSED_BRIDGE); / / xposed_bridge的函数是否通过RegisterNatives动态注册成功 if (register_natives_XposedBridge(env, classXposedBridge) ! = JNI_OK) { ALOGE( "Could not register natives for '%s'" , CLASS_XPOSED_BRIDGE); logExceptionStackTrace(); env - >ExceptionClear(); return false; } / / 判断通过jni获取handleHookedMethod函数是否成功 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; } int register_natives_XposedBridge(JNIEnv * env, jclass clazz) { / / 动态注册XposedBridge相关的函数,形式如XposedBridge_hookMethodNative 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" ), / / 关键函数,之后在findandhook中会涉及 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)); } |
2.1.1 onVmCreated
函数变更
到目前为止可以看到关于xposed::onVmCreated
的调用流程最终走到了两份文件自己的onVmCreated
方法,下面来看看这两个方法的差异
2.1.1.1 Dalvik
时期的onVmCreated
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | / / libxposed_dalvik.cpp / * * Called very early during VM startup. * / bool onVmCreated(JNIEnv * env) { if (!initMemberOffsets(env)) return false; / / #define CLASS_MIUI_RESOURCES "android/content/res/MiuiResources" 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(); / / 关键点 / / 获取invokeOriginalMethodNative方法的jmethodId / / 将invokeOriginalMethodNative的native方法关联到XposedBridge_invokeOriginalMethodNative方法,为后续调用的时候使用 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; } void XposedBridge_invokeOriginalMethodNative(const u4 * args, JValue * pResult, const Method * method, ::Thread * self ) { / / 获取method Method * meth = (Method * ) args[ 1 ]; if (meth = = NULL) { / / 反射获取 meth = dvmGetMethodFromReflectObj(( Object * ) args[ 0 ]); if (isMethodHooked(meth)) { meth = (Method * ) meth - >insns; } } ArrayObject * params = (ArrayObject * ) args[ 2 ]; ClassObject * returnType = (ClassObject * ) args[ 3 ]; Object * thisObject = ( Object * ) args[ 4 ]; / / null for static methods ArrayObject * argList = (ArrayObject * ) args[ 5 ]; / / invoke the method / / 通过dvmInvokeMethod执行方法,dvmInvokeMethod是在虚拟机中执行java方法的方法,可以理解为c语言执行java方法 pResult - >l = dvmInvokeMethod(thisObject, meth, argList, params, returnType, true); return ; } |
Dalvik
版本的onVmCreated
函数做的是为Java层invokeOriginalMethodNative
设置Native方法,将原本的方法转成native
的XposedBridge_invokeOriginalMethodNative
方法,再回调Java
层的方法
2.1.1.2 ART
时期的onVmCreated
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | / / libxposed_art.cpp / * * Called very early during VM startup. * / bool onVmCreated(JNIEnv * ) { / / 没有针对Resource的操作 / / TODO: Handle CLASS_MIUI_RESOURCES? / / 为ArtMethod的两个属性赋值,而这个ArtMethod定义来自于Xposed的android_art / / classXposedBridge和methodXposedBridgeHandleHookedMethod被赋值是在libxposed_common的initXposedBridge函数中 / / classXposedBridge = env - >FindClass(CLASS_XPOSED_BRIDGE); / / methodXposedBridgeHandleHookedMethod = env - >GetStaticMethodID(classXposedBridge, "handleHookedMethod" , "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;" ); ArtMethod::xposed_callback_class = classXposedBridge; ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod; return true; } |
ART
版本的onVmCreated
函数做的是为ArtMethod
赋值,classXposedBridge
和methodXposedBridgeHandleHookedMethod
都是之前在initXposedBridge
函数中设置好的
现在关于onVmCreated
的函数变更已经分析好了,下面正式开始分析main
函数
2.1.2 统一的initialize
函数流程
同样都是调用xposed::initialize
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | / / xposed.cpp / * * 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结构体赋值 xposed - >zygote = zygote; xposed - >startSystemServer = startSystemServer; xposed - >startClassName = className; xposed - >xposedVersionInt = xposedVersionInt; / / 针对selinux做的操作 #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) { xposed::logcat::printStartupMarker(); } else if (zygote) { / / TODO Find a better solution for this / / Give the primary Zygote process a little time to start first. / / This also makes the log easier to read, as logs for the two Zygotes are not mixed up. sleep( 10 ); } / / 打印rom信息 printRomInfo(); if (startSystemServer) { / / 在Android中一个UID的对应的就是一个可执行的程序,对于普通的程序其UID就是对应与GID,程序在Android系统留存期间,其UID不变。 / / xposed::service::startAll() fork子进程启动system context service和app context service / / 启动xposed_service_system xposed_service_app if (!determineXposedInstallerUidGid() || !xposed::service::startAll()) { return false; } / / fork调用xposed logcat进程,负责打印xposed日志 xposed::logcat::start(); #if XPOSED_WITH_SELINUX } else if (xposed - >isSELinuxEnabled) { / / 启动xposed_zygote_service 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; / / 把jar包加入到classpath当中 return addJarToClasspath(); } / * * Add XposedBridge.jar to the Java classpath. * / bool addJarToClasspath() { ALOGI( "-----------------" ); / / Do we have a new version and are (re)starting zygote? Then load it! / * FIXME if you can if (xposed - >startSystemServer && access(XPOSED_JAR_NEWVERSION, R_OK) = = 0 ) { ALOGI( "Found new Xposed jar version, activating it" ); if (rename(XPOSED_JAR_NEWVERSION, XPOSED_JAR) ! = 0 ) { ALOGE( "Could not move %s to %s" , XPOSED_JAR_NEWVERSION, XPOSED_JAR); return false; } } * / if (access(XPOSED_JAR, R_OK) = = 0 ) { / / #define XPOSED_JAR "/system/framework/XposedBridge.jar" 将xposed_bridge的jar加入classpath if (!addPathToEnv( "CLASSPATH" , XPOSED_JAR)) return false; ALOGI( "Added Xposed (%s) to CLASSPATH" , XPOSED_JAR); return true; } else { ALOGE( "ERROR: Could not access Xposed jar '%s'" , XPOSED_JAR); return false; } } |
xposed::initialize
函数主要做了些初始化的工作:为xposed
结构体赋值、启动各个子线程、最后将xposed_bridge.jar
引入,执行完xposed::initialize
函数之后会返回一个isXposedLoaded
标识,会根据这个标识决定是否具体传入的包名,之前步骤执行正确的话传入的包名应该是"de.robv.android.xposed.XposedBridge"
,来看看具体的runtime.start
函数是怎样执行的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | / / frameworks / base / core / jni / AndroidRuntime.cpp / / runtime.start()函数 void AndroidRuntime::start(const char * className, const Vector<String8>& options, bool zygote) { ALOGD( ">>>>>> START %s uid %d <<<<<<\n" , className ! = NULL ? className : "(unknown)" , getuid()); static const String8 startSystemServer( "start-system-server" ); / * * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. * / 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); / * start the virtual machine * / JniInvocation jni_invocation; / / 加载虚拟机的核心库 / / 没有具体分析,加载的是libart.so / libdalvik.so jni_invocation.Init(NULL); JNIEnv * env; / / 创建虚拟机 if (startVm(&mJavaVM, &env, zygote) ! = 0 ) { return ; } / / 这个函数被xposed替换 onVmCreated(env); / * * Register android functions. * / / / 注册andorid native函数 if (startReg(env) < 0 ) { ALOGE( "Unable to register all android natives\n" ); return ; } / * * We want to call main() with a String array with arguments in it. * At present we have two arguments, the class name and an option string. * Create an array to hold them. * / 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); } / * * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. * / char * slashClassName = toSlashClassName(className); / / 获取字符串对应的类,此时类为xposed_bridge类,由于在initialize方法中已经被加入到classpath中,因此可以获取到 jclass startClass = env - >FindClass(slashClassName); if (startClass = = NULL) { ALOGE( "JavaVM unable to locate class '%s'\n" , slashClassName); / * keep going * / } else { / / 获取类的main方法jmethodID 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 { / / 执行main函数 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" ); } |
runtime.start
函数调用的是AndroidRuntime
的start
函数,关键点在于startVm
(虚拟机的创建)、onVmCreated
(虚拟机创建完成之后的初始化),CallStaticVoidMethod main
(反射调用类的main函数),onVmCreated
已经分析过了,下面分析下XposedBridge
的main
函数
2.1.2.1 反射调用XposedBridge.main
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | / / XposedBridge / XposedBridge.java @SuppressWarnings ( "deprecation" ) protected static void main(String[] args) { / / Initialize the Xposed framework and modules / / XposedBridge的入口 try { if (!hadInitErrors()) { / / 获取资源文件 initXResources(); SELinuxHelper.initOnce(); SELinuxHelper.initForProcess(null); runtime = getRuntime(); XPOSED_BRIDGE_VERSION = getXposedVersion(); / / 三个关键步骤 if (isZygote) { / / 针对资源文件的hook XposedInit.hookResources(); / / 针对方法的hook XposedInit.initForZygote(); } / / 加载hook模块列表的模块 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 / / 最后回到了基本的zygote启动流程,不影响正常的启动,之前的操作都是为了hook的初始化 if (isZygote) { ZygoteInit.main(args); } else { RuntimeInit.main(args); } } |
XposedBridge.main
函数关键在于三个函数的调用:
hookResources
initForZygote
loadModules
我们重点关注下initForZygote
和loadModules
两个方法
2.1.2.2 XposedInit.initForZygote
我们分成几部分来看
第一部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | / / XposedBridge / XposedInit.java if (needsToCloseFilesForFork()) { / / 匿名内部类实例化XC_MethodHook抽象类 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); / / hook nativeForkAndSpecialize / / nativeForkAndSpecialize是Zygote的forkAndSpecialize的底层实现,调用fork()创建新进程,设置新进程的主线程 id ,重置gc性能数据,设置信号处理函数等功能 hookAllMethods(zygote, "nativeForkAndSpecialize" , callback); / / hook nativeForkSystemServer / / forkSystemServer是由Zygote的forkSystemServer的底层实现,调用fork()创建system server服务 hookAllMethods(zygote, "nativeForkSystemServer" , callback); } |
nativeForkAndSpecialize
和nativeForkSystemServer
同样都是利用Zygote
利用fork
来创建子进程的,我们看下Xposed
框架会对这两个方法做什么操作
closeFilesBeforeForkNative
和reopenFilesAfterForkNative
这两个方法同属于native
,属于XposedBridge
的native
方法一般都是在initXposedBridge
中动态注册的,方法是存在于各自的libxposed_xxx.so
文件当中,不过这两个方法只存在于高版本系统中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | / / libxposed_common.cpp #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 / / libxposed_art.cpp / / 不太清楚做了什么 void XposedBridge_closeFilesBeforeForkNative(JNIEnv * , jclass) { gClosedFdTable = FileDescriptorTable::Create(); } void XposedBridge_reopenFilesAfterForkNative(JNIEnv * , jclass) { gClosedFdTable - >Reopen(); delete gClosedFdTable; gClosedFdTable = NULL; } |
第二部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | / / normal process initialization ( for new Activity, Service, BroadcastReceiver etc.) / / hook ActivityThread.handleBindApplication()方法的hook findAndHookMethod(ActivityThread. class , "handleBindApplication" , "android.app.ActivityThread.AppBindData" , new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { / / 获取activityThread ActivityThread activityThread = (ActivityThread) param.thisObject; / / 获取AppBindData的appInfo ApplicationInfo appInfo = (ApplicationInfo) getObjectField(param.args[ 0 ], "appInfo" ); String reportedPackageName = appInfo.packageName.equals( "android" ) ? "system" : appInfo.packageName; SELinuxHelper.initForProcess(reportedPackageName); / / 获取AppBindData的instrumentationName 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 ; / / 设置mBoundApplication属性为AppBindData setObjectField(activityThread, "mBoundApplication" , param.args[ 0 ]); loadedPackagesInProcess.add(reportedPackageName); / / 通过activityThread获取当前已加载的apk LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo); XResources.setPackageNameForResDir(appInfo.packageName, loadedApk.getResDir()); / / 打包成LoadPackageParam对象 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; / / 针对lpparam的所有callback函数进行调用 XC_LoadPackage.callAll(lpparam); if (reportedPackageName.equals(INSTALLER_PACKAGE_NAME)) hookXposedInstaller(lpparam.classLoader); } }); / / XCallback.java public static void callAll(Param param) { if (param.callbacks = = null) throw new IllegalStateException( "This object was not created for use with callAll" ); / / 调用每个参数的callbacks数组每个对象的call方法 for ( int i = 0 ; i < param.callbacks.length; i + + ) { try { ((XCallback) param.callbacks[i]).call(param); } catch (Throwable t) { XposedBridge.log(t); } } } / / XC_LoadPackage.java @Override protected void call(Param param) throws Throwable { / / 也就是调用callbacks数组每个对象的handleLoadPackage方法 if (param instanceof LoadPackageParam) handleLoadPackage((LoadPackageParam) param); } |
这一部分的主要逻辑是针对ActivityThread
的handleBindApplication
方法做了hook
操作,具体的操作就是设置ActivityThread
的mBoundApplication
的值,并实例化LoadPackageParam
进行赋值,这里的LoadPackageParam
实例也就是我们在开发过程中的lpparam
了
1 | public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable |
我们从代码中已经知道了当lpparam
封装好之后会调用它的callbacks
数组每个对象的handleLoadPackage
方法,那这个数组是什么时候被赋值的呢?从代码中可以发现数组是XposedBridge.sLoadedPackageCallbacks
,它在下面这段代码中被赋值
1 2 3 4 5 6 7 | / / XposedBridge / XposedBridge.java public static void hookLoadPackage(XC_LoadPackage callback) { synchronized (sLoadedPackageCallbacks) { sLoadedPackageCallbacks.add(callback); } } |
hookLoadPackage
是在loadModules
方法中被调用,这个部分我们在loadModules
的时候再说
第三部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | / / system_server initialization / / 主要是对system_server做处理 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); } }); } }); } |
这一部分和上一部分道理相同,只是Hook
点的不同,一个是针对App
启动过程中,一个是针对system server
2.1.2.3 XposedInit.loadModules
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | / / XposedBridge / XposedInit 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 ; } / / public static final ClassLoader BOOTCLASSLOADER = ClassLoader.getSystemClassLoader(); / / 获取classloader 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; / / 每行读取apk,调用loadMudle() while ((apk = apks.readLine()) ! = null) { loadModule(apk, topClassLoader); } apks.close(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | / / XposedBridge / XposedInit private static void loadModule(String apk, ClassLoader topClassLoader) { Log.i(TAG, "Loading modules from " + apk); / / 判断Xposed模块是否存在 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); / / 获取模块中的assets / xposed_init,也就是我们开发过程中指定Xposed入口的文件 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); / / 读取assets / xposed_init文件 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 { / / 获取Xposed模块入口类名 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) / / 调用内部类IXposedHookLoadPackage.Wrapper构造方法生成XC_LoadPackage类,就可以被hookLoadPackage调用 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); } } } |
对于loadModules
的流程我们也大致了解了
那么,对于xposed::initialize
函数的分析就到这里,到目前为止,我们梳理完了Xposed
框架的启动流程以及它在启动过程中做了哪些工作,当然,我们的主题是ART/Dalvik
环境下Xposed
框架实现的异同,我们在app_main
文件变更这个步骤只发现了它们对于onVmCreated
的实现做了改变
2.2 findAndHookMethod
的实现变更
从刚才的initForZygote
函数中,我们可以发现使用了大量的findAndHookMethod
,当然,这个函数也是我们在接触Xposed
框架的时候最先学会的函数,标准的模板调用是这么来使用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | / / use.java import android.app.Application; import android.content.Context; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; / / 继承IXposedHookLoadPackage接口类 public class XposedHook implements IXposedHookLoadPackage { @Override / / 重写handleLoadPackage方法 public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { XposedHelpers.findAndHookMethod( Application. class , "attach" , Context. class , / / 匿名内部类实现XC_MethodHook回调函数 new XC_MethodHook() { @Override protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable { super .afterHookedMethod(param); } } ); } } |
我们跟踪下findAndHookMethod
的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | / / XpsoedBridge / XposedHelper.java public static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object ... parameterTypesAndCallback) { / / 没有回调函数或是回调函数的类型不是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 ]; / / 通过类、方法名,参数准确定位需要hook的方法 Method m = findMethodExact(clazz, methodName, getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback)); / / 调用XposedBridge.hookMethod,传入方法和回调函数 return XposedBridge.hookMethod(m, callback); } public static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) { / / private static final HashMap<String, Method> methodCache = new HashMap<>(); String fullMethodName = clazz.getName() + '#' + methodName + getParametersString(parameterTypes) + "#exact" ; if (methodCache.containsKey(fullMethodName)) { Method method = methodCache.get(fullMethodName); if (method = = null) throw new NoSuchMethodError(fullMethodName); return method; } / / 反射获取方法 try { Method method = clazz.getDeclaredMethod(methodName, parameterTypes); method.setAccessible(true); methodCache.put(fullMethodName, method); return method; } catch (NoSuchMethodException e) { methodCache.put(fullMethodName, null); throw new NoSuchMethodError(fullMethodName); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | / / XpsoedBridge / XpsoedBridge.java 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()); / / 函数的类不能是接口类,可以寻找接口类的实现类,hook实现类的函数 } 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; CopyOnWriteSortedSet<XC_MethodHook> callbacks; / / sHookedMethodCallbacks保存回调函数的cow set 的 map / / 考虑针对某一个method有多个回调函数 / / private static final Map <Member, CopyOnWriteSortedSet<XC_MethodHook>> sHookedMethodCallbacks = new HashMap<>(); synchronized (sHookedMethodCallbacks) { callbacks = sHookedMethodCallbacks.get(hookMethod); if (callbacks = = null) { callbacks = new CopyOnWriteSortedSet<>(); 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) { 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); / / 调用hookMethodNative hookMethodNative(hookMethod, declaringClass, slot, additionalInfo); } return callback.new Unhook(hookMethod); } / / native函数,动态注册在initXposedBridge函数调用时,有两个版本的libxposed_xx实现 private native synchronized static void hookMethodNative(Member method, Class<?> declaringClass, int slot, Object additionalInfo); |
2.2.1 Dalvik
时期的hookMethodNative
先来看看Dalvik
时期的,XposedBridge_hookMethodNative
实现在libxposed_dalvik.cpp
文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | / / libxposed_dalvik.cpp void 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 / / dvmDecodeIndirectRef 将间接引用jobject转换为对象引用 Object * ClassObject * declaredClass = (ClassObject * ) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect); / / dvmSlotToMethod 根据偏移量,从ClassLoader中获取函数指针 Method * method = dvmSlotToMethod(declaredClass, slot); if (method = = NULL) { dvmThrowNoSuchMethodError( "Could not get internal representation for method" ); return ; } if (isMethodHooked(method)) { / / already hooked return ; } / / Save a copy of the original method and other hook info / / hookInfo属性配置 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 / / 修改method变成native方法 SET_METHOD_FLAG(method, ACC_NATIVE); / / 赋予method对象的native方法为hookedMethodCallback method - >nativeFunc = &hookedMethodCallback; / / method的insns数组保存method信息 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); } } } void hookedMethodCallback(const u4 * args, JValue * pResult, const Method * method, ::Thread * self ) { if (!isMethodHooked(method)) { dvmThrowNoSuchMethodError( "Could not find Xposed original method - how did you even get here?" ); return ; } / / 获取属性 XposedHookInfo * hookInfo = (XposedHookInfo * ) method - >insns; Method * original = (Method * ) hookInfo; Object * originalReflected = hookInfo - >reflectedMethod; Object * additionalInfo = hookInfo - >additionalInfo; / / convert / box arguments const char * desc = &method - >shorty[ 1 ]; / / [ 0 ] is the return type . Object * thisObject = NULL; size_t srcIndex = 0 ; size_t dstIndex = 0 ; / / for non - static methods determine the "this" pointer if (!dvmIsStaticMethod(original)) { thisObject = ( Object * ) args[ 0 ]; srcIndex + + ; } ArrayObject * argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method - >shorty) - 1 , ALLOC_DEFAULT); if (argsArray = = NULL) { return ; } while ( * desc ! = '\0' ) { char descChar = * (desc + + ); JValue value; Object * obj; switch (descChar) { case 'Z' : case 'C' : case 'F' : case 'B' : case 'S' : case 'I' : value.i = args[srcIndex + + ]; obj = ( Object * ) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, self ); break ; case 'D' : case 'J' : value.j = dvmGetArgLong(args, srcIndex); srcIndex + = 2 ; obj = ( Object * ) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, self ); break ; case '[' : case 'L' : obj = ( Object * ) args[srcIndex + + ]; break ; default: ALOGE( "Unknown method signature description character: %c" , descChar); obj = NULL; srcIndex + + ; } setObjectArrayElement(argsArray, dstIndex + + , obj); } / / call the Java handler function / / JValue result; / / dvmCallMethod调用函数methodXposedBridgeHandleHookedMethod / / methodXposedBridgeHandleHookedMethod在initXposedBridge时候已经被赋值了,值为XposedBridge.handleHookedMethod函数 dvmCallMethod( self , (Method * ) methodXposedBridgeHandleHookedMethod, NULL, &result, originalReflected, ( int ) original, additionalInfo, thisObject, argsArray); dvmReleaseTrackedAlloc(argsArray, self ); / / exceptions are thrown to the caller if (dvmCheckException( self )) { return ; } / / return result with proper type ClassObject * returnType = dvmGetBoxedReturnType(method); if (returnType - >primitiveType = = PRIM_VOID) { / / ignored } else if (result.l = = NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowNullPointerException( "null result when primitive expected" ); } pResult - >l = NULL; } else { if (!dvmUnboxPrimitive(result.l, returnType, pResult)) { dvmThrowClassCastException(result.l - >clazz, returnType); } } } 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(); } } / / 获取callback函数的副本 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(); } } / / 获取method的参数 MethodHookParam param = new MethodHookParam(); param.method = method; param.thisObject = thisObject; param.args = args; / / 执行@beforeHookedMethod的函数 / / 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 { / / invokeOriginalMethodNative也是native函数 param.setResult(invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args)); } catch (InvocationTargetException e) { param.setThrowable(e.getCause()); } } / / 执行@afterHookedMethod的函数 / / 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(); } |
invokeOriginalMethodNative
是native
函数,想想它是在什么时候变成native
函数的呢?
在onVmCreated
函数执行的时候,回顾一下
1 2 3 4 5 6 7 8 9 | 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); |
通过dvmSetNativeFunc
将xposedInvokeOriginalMethodNative
的真正执行函数变为xposedInvokeOriginalMethodNative
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void dvmSetNativeFunc(Method * method, DalvikBridgeFunc func, const u2 * insns) { ClassObject * clazz = method - >clazz; assert (func ! = NULL); / * just open up both; easier that way * / dvmLinearReadWrite(clazz - >classLoader, clazz - >virtualMethods); dvmLinearReadWrite(clazz - >classLoader, clazz - >directMethods); if (insns ! = NULL) { / * update both, ensuring that "insns" is observed first * / method - >insns = insns; android_atomic_release_store((int32_t) func, (volatile int32_t * )(void * ) &method - >nativeFunc); } else { / * only update nativeFunc * / 没有insns数组,设置method的nativeFunc方法 method - >nativeFunc = func; } dvmLinearReadOnly(clazz - >classLoader, clazz - >virtualMethods); dvmLinearReadOnly(clazz - >classLoader, clazz - >directMethods); } |
xposedInvokeOriginalMethodNative
方法我们之前也分析过,主要是通过dvmInvokeMethod
来反射调用Java
层的方法
2.2.2 知识点插入:Dalvik
虚拟机执行Java方法流程
2.2.3 ART
时期的hookMethodNative
分析完了Dalvik
时期的hookMethodNative
流程,下面来看看ART
时期的hookMethodNative
做了哪些改变,省略掉相同的步骤,我们直接看libxposed_art.cpp
的XposedBridge_hookMethodNative
函数实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | / / libxposed_art.cpp 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 ; } / / Get the ArtMethod of the method to be hooked. ArtMethod * artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod); / / Hook the method artMethod - >EnableXposedHook(soa, javaAdditionalInfo); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | / / android_art / art_method.cc void ArtMethod::EnableXposedHook(ScopedObjectAccess& soa, jobject additional_info) { if (UNLIKELY(IsXposedHookedMethod())) { / / Already hooked return ; } else if (UNLIKELY(IsXposedOriginalMethod())) { / / This should never happen ThrowIllegalArgumentException(StringPrintf( "Cannot hook the method backup: %s" , PrettyMethod(this).c_str()).c_str()); return ; } / / Create a backup of the ArtMethod object / / 获取classloader auto * cl = Runtime::Current() - >GetClassLinker(); auto * linear_alloc = cl - >GetAllocatorForClassLoader(GetClassLoader()); / / 获取method ArtMethod * backup_method = cl - >CreateRuntimeMethod(linear_alloc); / / 备份并标记 backup_method - >CopyFrom(this, cl - >GetImagePointerSize()); backup_method - >SetAccessFlags(backup_method - >GetAccessFlags() | kAccXposedOriginalMethod); / / Create a Method / Constructor object for the backup ArtMethod object 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); / / Save extra information in a separate structure, stored instead of the native method / / hookInfo结构体属性配置,与dalvik版本类似 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_info - >additional_info = soa.Env() - >NewGlobalRef(additional_info); hook_info - >original_method = backup_method; ScopedThreadSuspension sts(soa.Self(), kSuspended); / / 停止JIT即时编译 jit::ScopedJitSuspend sjs; / / 防止死锁 gc::ScopedGCCriticalSection gcs(soa.Self(), gc::kGcCauseXposed, gc::kCollectorTypeXposed); ScopedSuspendAll ssa(__FUNCTION__); / / 取消所有调用 cl - >InvalidateCallersForMethod(soa.Self(), this); / / 去除JIT编译的结果 jit::Jit * jit = art::Runtime::Current() - >GetJit(); if (jit ! = nullptr) { jit - >GetCodeCache() - >MoveObsoleteMethod(this, backup_method); } / / 将hook_info存到entry_point_from_jni这个指针 SetEntryPointFromJniPtrSize(reinterpret_cast<uint8_t * >(hook_info), sizeof(void * )); / / 替换函数入口点entry_point_from_quick_compiled_code_为自己的GetQuickProxyInvokeHandler SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); / / 设置函数在CodeItem偏移 SetCodeItemOffset( 0 ); / / Adjust access flags. 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); } |
关键点在于函数的入口点的替换,由entry_point_from_quick_compiled_code_
替换成了GetQuickProxyInvokeHandler
看看GetQuickProxyInvokeHandler
的实现
1 2 3 4 5 6 7 | / / android_art / runtime / entrypoints / quick / runtime_asm_entrypoints.h / / Return the address of quick stub code for handling transitions into the proxy invoke handler. extern "C" void art_quick_proxy_invoke_handler(); static inline const void * GetQuickProxyInvokeHandler() { return reinterpret_cast<const void * >(art_quick_proxy_invoke_handler); } |
GetQuickProxyInvokeHandler
的具体逻辑由art_quick_proxy_invoke_handler
方法来实现
从汇编文件入手
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | / * android_art\runtime\arch\arm\quick_entrypoints_arm.S * / / * * Called by managed code that is attempting to call a method on a proxy class . On entry * r0 holds the proxy method and r1 holds the receiver; r2 and r3 may contain arguments. The * frame size of the invoked proxy method agrees with a ref and args callee save frame. * / .extern artQuickProxyInvokeHandler ENTRY art_quick_proxy_invoke_handler SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0 mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP / * 调用了artQuickProxyInvokeHandler函数 * / blx artQuickProxyInvokeHandler @ (Method * proxy method, receiver, Thread * , SP) ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ / / Tear down the callee - save frame. Skip arg registers. add sp, #(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE) .cfi_adjust_cfa_offset - (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME cbnz r2, 1f @ success if no exception is pending vmov d0, r0, r1 @ store into fpr, for when it's a fpr return ... bx lr @ return on success 1 : DELIVER_PENDING_EXCEPTION END art_quick_proxy_invoke_handler |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | / / android_art / quick_trampoline_entrypoints.cc extern "C" uint64_t artQuickProxyInvokeHandler( ArtMethod * proxy_method, mirror:: Object * receiver, Thread * self , ArtMethod * * sp) SHARED_REQUIRES(Locks::mutator_lock_) { / / 判断是否是Xposed hook的函数,通过method标记来识别 / / Xposed / / bool IsXposedHookedMethod() { / / return (GetAccessFlags() & kAccXposedHookedMethod) ! = 0 ; / / } const bool is_xposed = proxy_method - >IsXposedHookedMethod(); if (!is_xposed) { DCHECK(proxy_method - >IsRealProxyMethod()) << PrettyMethod(proxy_method); DCHECK(receiver - >GetClass() - >IsProxyClass()) << PrettyMethod(proxy_method); } / / Ensure we don't get thread suspension until the object arguments are safely in jobjects. const char * old_cause = self - >StartAssertNoThreadSuspension( "Adding to IRT proxy object arguments" ); / / Register the top of the managed stack, making stack crawlable. DCHECK_EQ(( * sp), proxy_method) << PrettyMethod(proxy_method); self - >VerifyStack(); / / Start new JNI local reference state. JNIEnvExt * env = self - >GetJniEnv(); ScopedObjectAccessUnchecked soa(env); ScopedJniEnvLocalRefState env_state(env); / / Create local ref. copies of proxy method and the receiver. const bool is_static = proxy_method - >IsStatic(); jobject rcvr_jobj = is_static ? nullptr : soa.AddLocalReference<jobject>(receiver); / / Placing arguments into args vector and remove the receiver. ArtMethod * non_proxy_method = proxy_method - >GetInterfaceMethodIfProxy(sizeof(void * )); CHECK(is_xposed || !non_proxy_method - >IsStatic()) << PrettyMethod(proxy_method) << " " << PrettyMethod(non_proxy_method); std::vector<jvalue> args; uint32_t shorty_len = 0 ; const char * shorty = non_proxy_method - >GetShorty(&shorty_len); BuildQuickArgumentVisitor local_ref_visitor(sp, is_static, shorty, shorty_len, &soa, &args); local_ref_visitor.VisitArguments(); if (!is_static) { DCHECK_GT(args.size(), 0U ) << PrettyMethod(proxy_method); args.erase(args.begin()); } if (is_xposed) { jmethodID proxy_methodid = soa.EncodeMethod(proxy_method); self - >EndAssertNoThreadSuspension(old_cause); / / 调用InvokeXposedHandleHookedMethod方法 JValue result = InvokeXposedHandleHookedMethod(soa, shorty, rcvr_jobj, proxy_methodid, args); local_ref_visitor.FixupReferences(); return result.GetJ(); } / / Convert proxy method into expected interface method. ArtMethod * interface_method = proxy_method - >FindOverriddenMethod(sizeof(void * )); DCHECK(interface_method ! = nullptr) << PrettyMethod(proxy_method); DCHECK(!interface_method - >IsRealProxyMethod()) << PrettyMethod(interface_method); self - >EndAssertNoThreadSuspension(old_cause); jobject interface_method_jobj = soa.AddLocalReference<jobject>( mirror::Method::CreateFromArtMethod(soa.Self(), interface_method)); / / All naked Object * s should now be in jobjects, so its safe to go into the main invoke code / / that performs allocations. JValue result = InvokeProxyInvocationHandler(soa, shorty, rcvr_jobj, interface_method_jobj, args); / / Restore references which might have moved. local_ref_visitor.FixupReferences(); return result.GetJ(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | / / android_art / entrypoint_utils.cc JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char * shorty, jobject rcvr_jobj, jmethodID method, std::vector<jvalue>& args) { / / Build argument array possibly triggering GC. soa.Self() - >AssertThreadSuspensionIsAllowable(); jobjectArray args_jobj = nullptr; const JValue zero; int32_t target_sdk_version = Runtime::Current() - >GetTargetSdkVersion(); / / Do not create empty arrays unless needed to maintain Dalvik bug compatibility. if (args.size() > 0 || (target_sdk_version > 0 && target_sdk_version < = 21 )) { args_jobj = soa.Env() - >NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, nullptr); if (args_jobj = = nullptr) { CHECK(soa.Self() - >IsExceptionPending()); return zero; } for (size_t i = 0 ; i < args.size(); + + i) { if (shorty[i + 1 ] = = 'L' ) { jobject val = args.at(i).l; soa.Env() - >SetObjectArrayElement(args_jobj, i, val); } else { JValue jv; jv.SetJ(args.at(i).j); mirror:: Object * val = BoxPrimitive(Primitive::GetType(shorty[i + 1 ]), jv); if (val = = nullptr) { CHECK(soa.Self() - >IsExceptionPending()); return zero; } soa.Decode<mirror::ObjectArray<mirror:: Object > * >(args_jobj) - > Set <false>(i, val); } } } / / 获取hook_info的数据 const XposedHookInfo * hook_info = soa.DecodeMethod(method) - >GetXposedHookInfo(); / / Call XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, / / Object thisObject, Object [] args) jvalue invocation_args[ 5 ]; invocation_args[ 0 ].l = hook_info - >reflected_method; invocation_args[ 1 ].i = 1 ; invocation_args[ 2 ].l = hook_info - >additional_info; invocation_args[ 3 ].l = rcvr_jobj; invocation_args[ 4 ].l = args_jobj; jobject result = / / 调用ArtMethod::xposed_callback_class类的ArtMethod::xposed_callback_method方法 / / 之前在onVmCreated中赋值过 / / ArtMethod::xposed_callback_class = classXposedBridge; / / ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod; / / 最终调用的是XposedBridge的HandleHookedMethod方法 soa.Env() - >CallStaticObjectMethodA(ArtMethod::xposed_callback_class, ArtMethod::xposed_callback_method, invocation_args); / / Unbox the result if necessary and return it. if (UNLIKELY(soa.Self() - >IsExceptionPending())) { return zero; } else { if (shorty[ 0 ] = = 'V' || (shorty[ 0 ] = = 'L' && result = = nullptr)) { return zero; } / / This can cause thread suspension. size_t pointer_size = Runtime::Current() - >GetClassLinker() - >GetImagePointerSize(); mirror::Class * result_type = soa.DecodeMethod(method) - >GetReturnType(true / * resolve * / , pointer_size); mirror:: Object * result_ref = soa.Decode<mirror:: Object * >(result); JValue result_unboxed; if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) { DCHECK(soa.Self() - >IsExceptionPending()); return zero; } return result_unboxed; } } |
回到handleHookedMethod
方法之后就和dalvik
的执行逻辑一样了
2.2.4 知识点插入:ART
虚拟机执行Java方法流程
3. 参考
[1] https://bbs.pediy.com/thread-257844.htm
[2] https://blog.csdn.net/zjx839524906/article/details/81046844
看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~