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框架。
Xposed是Android平台下的一个著名hook框架,其原理是通过修改替换/system/bin/app_process程序控制zygote进程,使app_process在启动过程中加载xposedBrider.jar包,从而完成对zygote进程以及其创建的虚拟机劫持。基于xposed框架可以制作出许多功能强大的模块,其在功能不冲突的情况下同时运作。
Xposed源码地址为:https://github.com/rovo89
Xposed文件下载地址为:http://dl-xda.xposed.info/framework/
主要包括5部分:
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
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;
}
}
/** 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();
}
static void runtimeStart(AppRuntime& runtime, const char *classname, const Vector& options, bool zygote)
{
#if PLATFORM_SDK_VERSION >= 23
runtime.start(classname, options, zygote);
#else
//......
#endif
}
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
ALOGD(">>>>>> START %s uid %d <<<<<<\n",
className != NULL ? className : "(unknown)", getuid());
static const String8 startSystemServer("start-system-server");
/*
* 'startSystemServer == true' 意味着runtime被废弃,并且不再从init.rc中启动
* 所以我们打印除了启动事件
*/
for (size_t i = 0; i < options.size(); ++i) {
if (options[i] == startSystemServer) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
}
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* 开启虚拟机 */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
/*
* 注册android函数
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
/*
* 利用反射调用输入类的main方法,并输入相关的参数列表,此处包括两个参数,类名和选项字符串
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/*
* 开启VM,该进程将称为VM的主要进程,并且一直运行直到VM退出
*/
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
}
Xposed重写了onVmCreated方法,
virtual void onVmCreated(JNIEnv* env)
{
if (isXposedLoaded)
xposed::onVmCreated(env);
if (mClassName.isEmpty()) {
return; // Zygote. Nothing to do here.
}
char* slashClassName = toSlashClassName(mClassName.string());
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
env->ExceptionDescribe();
}
free(slashClassName);
mClass = reinterpret_cast(env->NewGlobalRef(mClass));
}
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);
}
}
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;
}
bool xposedInitLib(xposed::XposedShared* shared) {
xposed = shared;
xposed->onVmCreated = &onVmCreatedCommon;
return true;
}
void onVmCreatedCommon(JNIEnv* env) {
//初始化XposedBridge环境和ZygoteService环境
if (!initXposedBridge(env) || !initZygoteService(env)) {
return;
}
// 开启虚拟机
if (!onVmCreated(env)) {
return;
}
xposedLoadedSuccessfully = true;
return;
}
bool initXposedBridge(JNIEnv* env) {
classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);
if (classXposedBridge == NULL) {
ALOGE("Error while loading Xposed class '%s':", CLASS_XPOSED_BRIDGE);
logExceptionStackTrace();
env->ExceptionClear();
return false;
}
classXposedBridge = reinterpret_cast(env->NewGlobalRef(classXposedBridge));
ALOGI("Found Xposed class '%s', now initializing", CLASS_XPOSED_BRIDGE);
if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) {
ALOGE("Could not register natives for '%s'", CLASS_XPOSED_BRIDGE);
logExceptionStackTrace();
env->ExceptionClear();
return false;
}
// 初始化methodXposedBridgeHandleHookedMethod,其值为XposedBridge.handleHookedMethod的函数ID
methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",
"(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
if (methodXposedBridgeHandleHookedMethod == NULL) {
ALOGE("ERROR: could not find method %s.handleHookedMethod(Member, int, Object, Object, Object[])", CLASS_XPOSED_BRIDGE);
logExceptionStackTrace();
env->ExceptionClear();
return false;
}
return true;
}
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));
}
bool initZygoteService(JNIEnv* env) {
// 寻找类 de/robv/android/xposed/services/ZygoteService
jclass zygoteServiceClass = env->FindClass(CLASS_ZYGOTE_SERVICE);
if (zygoteServiceClass == NULL) {
ALOGE("Error while loading ZygoteService class '%s':", CLASS_ZYGOTE_SERVICE);
logExceptionStackTrace();
env->ExceptionClear();
return false;
}
// 注册native函数
if (register_natives_ZygoteService(env, zygoteServiceClass) != JNI_OK) {
ALOGE("Could not register natives for '%s'", CLASS_ZYGOTE_SERVICE);
env->ExceptionClear();
return false;
}
//寻找类 de/robv/android/xposed/services/FileResult
classFileResult = env->FindClass(CLASS_FILE_RESULT);
if (classFileResult == NULL) {
ALOGE("Error while loading FileResult class '%s':", CLASS_FILE_RESULT);
logExceptionStackTrace();
env->ExceptionClear();
return false;
}
classFileResult = reinterpret_cast<jclass>(env->NewGlobalRef(classFileResult));
// 获取init的函数ID
constructorFileResult = env->GetMethodID(classFileResult, "" , "(JJ)V");
if (constructorFileResult == NULL) {
ALOGE("ERROR: could not find constructor %s(long, long)", CLASS_FILE_RESULT);
logExceptionStackTrace();
env->ExceptionClear();
return false;
}
return true;
}
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));
}
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;
}
下面我们来研究下调用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);
}
}
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);
}
初始化sIsSELinuxEnabled 变量
static void initOnce() {
try {
sIsSELinuxEnabled = SELinux.isSELinuxEnabled();
} catch (NoClassDefFoundError ignored) {}
}
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();
}
}
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
为初始化Zygote而hook一些函数,这些函数都是启动Zygote进程时,需要调用的关键函数。包括
* nativeForkAndSpecialize
* nativeForkSystemServer
* handleBindApplication
* com.android.server.ServerThread.run/initAndLoop
* ActivityThread.systemMain
* LoadedApk()
* android.app.ApplicationPackageManager.getResourcesForApplication
同时还适配了一些手机系统。
static void initForZygote() throws Throwable {
if (needsToCloseFilesForFork()) {
XC_MethodHook callback = new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.closeFilesBeforeForkNative();
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.reopenFilesAfterForkNative();
}
};
Class> zygote = findClass("com.android.internal.os.Zygote", null);
hookAllMethods(zygote, "nativeForkAndSpecialize", callback);
hookAllMethods(zygote, "nativeForkSystemServer", callback);
}
final HashSet loadedPackagesInProcess = new HashSet<>(1);
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
ActivityThread activityThread = (ActivityThread) param.thisObject;
ApplicationInfo appInfo = (ApplicationInfo) getObjectField(param.args[0], "appInfo");
String reportedPackageName = appInfo.packageName.equals("android") ? "system" : appInfo.packageName;
SELinuxHelper.initForProcess(reportedPackageName);
ComponentName instrumentationName = (ComponentName) getObjectField(param.args[0], "instrumentationName");
if (instrumentationName != null) {
Log.w(TAG, "Instrumentation detected, disabling framework for " + reportedPackageName);
XposedBridge.disableHooks = true;
return;
}
CompatibilityInfo compatInfo = (CompatibilityInfo) getObjectField(param.args[0], "compatInfo");
if (appInfo.sourceDir == null)
return;
setObjectField(activityThread, "mBoundApplication", param.args[0]);
loadedPackagesInProcess.add(reportedPackageName);
LoadedApk loadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
XResources.setPackageNameForResDir(appInfo.packageName, loadedApk.getResDir());
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
lpparam.packageName = reportedPackageName;
lpparam.processName = (String) getObjectField(param.args[0], "processName");
lpparam.classLoader = loadedApk.getClassLoader();
lpparam.appInfo = appInfo;
lpparam.isFirstApplication = true;
XC_LoadPackage.callAll(lpparam);
if (reportedPackageName.equals(INSTALLER_PACKAGE_NAME))
hookXposedInstaller(lpparam.classLoader);
}
});
// system_server initialization
if (Build.VERSION.SDK_INT < 21) {
findAndHookMethod("com.android.server.ServerThread", null,
Build.VERSION.SDK_INT < 19 ? "run" : "initAndLoop", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
SELinuxHelper.initForProcess("android");
loadedPackagesInProcess.add("android");
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
lpparam.packageName = "android";
lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
lpparam.classLoader = XposedBridge.BOOTCLASSLOADER;
lpparam.appInfo = null;
lpparam.isFirstApplication = true;
XC_LoadPackage.callAll(lpparam);
}
});
} else if (startsSystemServer) {
findAndHookMethod(ActivityThread.class, "systemMain", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
final ClassLoader cl = Thread.currentThread().getContextClassLoader();
findAndHookMethod("com.android.server.SystemServer", cl, "startBootstrapServices", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
SELinuxHelper.initForProcess("android");
loadedPackagesInProcess.add("android");
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
lpparam.packageName = "android";
lpparam.processName = "android"; // it's actually system_server, but other functions return this as well
lpparam.classLoader = cl;
lpparam.appInfo = null;
lpparam.isFirstApplication = true;
XC_LoadPackage.callAll(lpparam);
// Huawei
try {
findAndHookMethod("com.android.server.pm.HwPackageManagerService", cl, "isOdexMode", XC_MethodReplacement.returnConstant(false));
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {}
try {
String className = "com.android.server.pm." + (Build.VERSION.SDK_INT >= 23 ? "PackageDexOptimizer" : "PackageManagerService");
findAndHookMethod(className, cl, "dexEntryExists", String.class, XC_MethodReplacement.returnConstant(true));
} catch (XposedHelpers.ClassNotFoundError | NoSuchMethodError ignored) {}
}
});
}
});
}
// when a package is loaded for an existing process, trigger the callbacks as well
hookAllConstructors(LoadedApk.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
LoadedApk loadedApk = (LoadedApk) param.thisObject;
String packageName = loadedApk.getPackageName();
XResources.setPackageNameForResDir(packageName, loadedApk.getResDir());
if (packageName.equals("android") || !loadedPackagesInProcess.add(packageName))
return;
if (!getBooleanField(loadedApk, "mIncludeCode"))
return;
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
lpparam.packageName = packageName;
lpparam.processName = AndroidAppHelper.currentProcessName();
lpparam.classLoader = loadedApk.getClassLoader();
lpparam.appInfo = loadedApk.getApplicationInfo();
lpparam.isFirstApplication = false;
XC_LoadPackage.callAll(lpparam);
}
});
findAndHookMethod("android.app.ApplicationPackageManager", null, "getResourcesForApplication",
ApplicationInfo.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
ApplicationInfo app = (ApplicationInfo) param.args[0];
XResources.setPackageNameForResDir(app.packageName,
app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir);
}
});
// MIUI
if (findFieldIfExists(ZygoteInit.class, "BOOT_START_TIME") != null) {
setStaticLongField(ZygoteInit.class, "BOOT_START_TIME", XposedBridge.BOOT_START_TIME);
}
// Samsung
if (Build.VERSION.SDK_INT >= 24) {
Class> zygote = findClass("com.android.internal.os.Zygote", null);
try {
setStaticBooleanField(zygote, "isEnhancedZygoteASLREnabled", false);
} catch (NoSuchFieldError ignored) {
}
}
}
将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();
}
过程可以概述为:
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);
}
}
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;
}
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;
}
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.2.8节相同
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 {}
}
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 {}
}
/**
* 查询函数并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);
}
主要功能是将回调加入到数组中,判断函数是否是第一次被hook,然后调用native层函数
/**
* hook任何带有特殊回调的函数(或构造函数),利用一些wrapper让实现更为简单
*
* @param hookMethod 被hook的方法
* @param callback 回调
* @return 一个可以用来删除hook的对象
*
* @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)
* @see XposedHelpers#findAndHookMethod(Class, String, Object...)
* @see #hookAllMethods
* @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)
* @see XposedHelpers#findAndHookConstructor(Class, Object...)
* @see #hookAllConstructors
*/
public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {
// 判断被hook的是否为普通函数或构造函数
if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor>)) {
throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());
} else if (hookMethod.getDeclaringClass().isInterface()) {
throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());
} else if (Modifier.isAbstract(hookMethod.getModifiers())) {
throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());
}
boolean newMethod = false;
// 将callback添加到列表中
CopyOnWriteSortedSet callbacks;
synchronized (sHookedMethodCallbacks) {
callbacks = sHookedMethodCallbacks.get(hookMethod);
if (callbacks == null) {
callbacks = new CopyOnWriteSortedSet<>();
sHookedMethodCallbacks.put(hookMethod, callbacks);
newMethod = true;
}
}
callbacks.add(callback);
// 如果该方法第一次被hook
if (newMethod) {
Class> declaringClass = hookMethod.getDeclaringClass();
int slot;
Class>[] parameterTypes;
Class> returnType;
if (runtime == RUNTIME_ART) {
slot = 0;
parameterTypes = null;
returnType = null;
} else if (hookMethod instanceof Method) {
slot = getIntField(hookMethod, "slot");
parameterTypes = ((Method) hookMethod).getParameterTypes();
returnType = ((Method) hookMethod).getReturnType();
} else {
slot = getIntField(hookMethod, "slot");
parameterTypes = ((Constructor>) hookMethod).getParameterTypes();
returnType = null;
}
AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
// 将beforehook和afterhook加入additionalInfo中,并调用native层hook方法
hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);
}
return callback.new Unhook(hookMethod);
}
这个函数在3.1.3.5章节中注册native方法,其对应的native实现为XposedBridge_hookMethodNative
/**
* 拦截每次特殊函数调用,并调用一个处理函数来替代
* @param method 用来拦截的函数
*/
private native synchronized static void hookMethodNative(Member method, Class> declaringClass, int slot, Object additionalInfo);
–>[Xposed][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;
}
// 获取函数的ArtMethod,以hook
ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);
// hook函数
artMethod->EnableXposedHook(soa, javaAdditionalInfo);
}
–>[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();
}
–>[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);
}