您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 订阅
  捐助
Xposed框架分析
 
  4984  次浏览      16
2019-3-1
 
编辑推荐:
本文来自于csdn,文章介绍了 Xposed原理、XposedBridge.main之后的逻辑、hook过程函数调用分析等相关内容。

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框架,下载和启用指定插件,或禁用插件等。

3. Xposed启动源码分析

3.1 Xposed启动Zygote

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<String8>& 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<String8>& 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<jclass>(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<jclass>(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, "<init>", "(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之后的逻辑,其主要流程图如下。

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<Object> 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<Method> 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进程时,需要调用的关键函数。包括

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<String> 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过程函数调用分析

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<XC_MethodHook> 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);
}
 
   
4984 次浏览       16
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程