Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
Xposed¿ò¼Ü·ÖÎö
 
  5895  次浏览      29
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

Ö÷Òª¹¦ÄÜÊdzõʼ»¯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

¨C>[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

¨C>[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

¨C>[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);
}
 
   
5895 ´Îä¯ÀÀ       29
Ïà¹ØÎÄÕÂ

Éî¶È½âÎö£ºÇåÀíÀôúÂë
ÈçºÎ±àд³öÓµ±§±ä»¯µÄ´úÂë
ÖØ¹¹-ʹ´úÂë¸ü¼ò½àÓÅÃÀ
ÍŶÓÏîÄ¿¿ª·¢"±àÂë¹æ·¶"ϵÁÐÎÄÕÂ
Ïà¹ØÎĵµ

ÖØ¹¹-¸ÄÉÆ¼ÈÓдúÂëµÄÉè¼Æ
Èí¼þÖØ¹¹v2
´úÂëÕû½àÖ®µÀ
¸ßÖÊÁ¿±à³Ì¹æ·¶
Ïà¹Ø¿Î³Ì

»ùÓÚHTML5¿Í»§¶Ë¡¢Web¶ËµÄÓ¦Óÿª·¢
HTML 5+CSS ¿ª·¢
ǶÈëʽC¸ßÖÊÁ¿±à³Ì
C++¸ß¼¶±à³Ì