Xposed笔记(一)Xposed初识.续

Xposed初识.续

original: http://www.bubuko.com/infodetail-647041.html

源码分析
安装xposed框架后,系统启动,执行init, init 再调用 app_process 程序,由于这时候 app_process 已经被换了,所以app_process 启动后先进入的是 xposedbridge.class 的 main 函数, 这个函数最后才进入标准的 zygoteInit.class 的 main 函数,在进入 zygote 之前,它调用了几个函数,初始化了xposed框架,下面逐个分析。
1、initNative

Xposed.cpp (xposed):{"initNative", "()Z",(void*)de_robv_android_xposed_XposedBridge_initNative},
XposedBridge.java(xposedbridge\src\de\robv\android\xposed):   
if (initNative()) 
{
    XposedBridge.java (xposedbridge\src\de\robv\android\xposed):    
    private native static boolean initNative();

XposedBridge.class 的 initNative 是一个 native 函数,真正的实现在 Xposed.cpp 里的 de_robv_android_xposed_XposedBridge_initNative

 de_robv_android_xposed_XposedBridge_initNative(JNIEnv*env, jclass clazz)
 xposedHandleHookedMethod = (Method*)env->GetStaticMethodID(xposedClass, "handleHookedMethod",
       "(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

首先,从xposedClass(即XposedBridge.class)类获取函数 handleHookedMethod, 这个函数是java函数,这里获取其对应的 Method 结构体指针,这样就可以在 native 里调用(jni的原理)。那么,这个 handleHookedMethod是干嘛的呢,这个函数就是最终执行的挂钩函数,后面会分析。

Method* xposedInvokeOriginalMethodNative =(Method*) env->GetStaticMethodID(xposedClass,"invokeOriginalMethodNative",
       "(Ljava/lang/reflect/Member;I[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
dvmSetNativeFunc(xposedInvokeOriginalMethodNative,de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative, NULL);

其次,从xposedClass(即XposedBridge.class)类获取函数 invokeOriginalMethodNative函数的 Method 结构体指针,然后调用 dvmSetNativeFunc 为这个java函数设置其 jni 实现 de_robv_android_xposed_XposedBridge_invokeOriginalMethodNative , 这样,调用 invokeOriginalMethodNative 函数其实执行的是后者。

objectArrayClass =dvmFindArrayClass("[Ljava/lang/Object;", NULL);
xresourcesClass =env->FindClass(XRESOURCES_CLASS);
   xresourcesClass =reinterpret_cast<jclass>(env->NewGlobalRef(xresourcesClass));
if (register_android_content_res_XResources(env)!= JNI_OK) {}
xresourcesTranslateResId =env->GetStaticMethodID(xresourcesClass, "translateResId",
       "(ILandroid/content/res/XResources;Landroid/content/res/Resources;)I");
xresourcesTranslateAttrId =env->GetStaticMethodID(xresourcesClass, "translateAttrId",
       "(Ljava/lang/String;Landroid/content/res/XResources;)I");

最后,获取其他一些java类或函数的标识

2、initXbridgeZygote
XposedBridge.class main 函数第二个重要的函数是 initXbridgeZygote

// normal process initialization (for newActivity, Service, BroadcastReceiver etc.)
       findAndHookMethod(ActivityThread.class,"handleBindApplication", "android.app.ActivityThread.AppBindData", new XC_MethodHook() {
           protected void beforeHookedMethod(MethodHookParam param) throwsThrowable {
       ...
}

首先,挂钩了ActivityThread 类的 handleBindApplication 函数,这个函数是在android ams 系统创建新进程成功后在新进程内部调用的,挂钩这个函数,可以在新进程创建后做一些事情
这里调用了一个函数,实现了挂钩 findAndHookMethod。这个函数定义在XposedHelpers.java

XposedHelpers.class

public static XC_MethodHook.UnhookfindAndHookMethod(Class<?> clazz, String methodName, Object...parameterTypesAndCallback) {
       if (parameterTypesAndCallback.length == 0 ||!(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceofXC_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);
    }

这个函数找到类 clazz 的函数 methodName 所对应的 Method结构体,然后调用 XposedBridge 的 hookMethod 函数挂钩它

XposedBridge.class

public static XC_MethodHook.UnhookhookMethod(Member hookMethod, XC_MethodHook callback) {
      ....

       boolean newMethod = false;
       CopyOnWriteSortedSet<XC_MethodHook> callbacks;
       synchronized (sHookedMethodCallbacks) {
           callbacks = sHookedMethodCallbacks.get(hookMethod);
           if (callbacks == null) {
                callbacks = newCopyOnWriteSortedSet<XC_MethodHook>();
               sHookedMethodCallbacks.put(hookMethod, callbacks);
                newMethod = true;
           }
       }
       callbacks.add(callback); // 先将被挂钩的函数hookMethod 及挂钩它的函数保存起来
       if (newMethod) {
           Class<?> declaringClass = hookMethod.getDeclaringClass();
           int slot = (int) getIntField(hookMethod, "slot");

           Class<?>[] parameterTypes;
           Class<?> returnType;
           if (hookMethod instanceof Method) {
                parameterTypes = ((Method)hookMethod).getParameterTypes();
                returnType = ((Method)hookMethod).getReturnType();
           } else {
                parameterTypes = ((Constructor<?>)hookMethod).getParameterTypes();
                returnType = null;
           }

           AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);
           hookMethodNative(hookMethod, declaringClass, slot, additionalInfo); // 最终调用 hookMethodNative 函数
} return callback.new Unhook(hookMethod); }

这个函数先将被挂钩的函数hookMethod 及挂钩它的 XC_MethodHook结构体保存起来,然后调用 hookMethodNative 函数
{“hookMethodNative”, “(Ljava/lang/reflect/Member;Ljava/lang/Class;ILjava/lang/Object;)V”,(void*)de_robv_android_xposed_XposedBridge_hookMethodNative},

这个函数定义在xposed.cpp 里

static voidde_robv_android_xposed_XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz,jobject reflectedMethodIndirect,
           jobject declaredClassIndirect, jint slot, jobjectadditionalInfoIndirect) { // Usage errors? if (declaredClassIndirect == NULL ||reflectedMethodIndirect == NULL) { dvmThrowIllegalArgumentException("method and declaredClass must notbe null"); return; }

   // Find the internal representation of the method
   ClassObject* declaredClass = (ClassObject*)dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
   Method* method = dvmSlotToMethod(declaredClass, slot);
   if (method == NULL) { dvmThrowNoSuchMethodError("could not get internal representation for method"); return; } if (xposedIsHooked(method)) { // already hooked return; } // Save a copy of the original method and other hook info XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));
   memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));
   hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(),env->NewGlobalRef(reflectedMethodIndirect));
   hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(),env->NewGlobalRef(additionalInfoIndirect));

   // Replace method with our own code
   SET_METHOD_FLAG(method, ACC_NATIVE);
    method->nativeFunc =&xposedCallHandler;
   method->insns = (const u2*) hookInfo;
   method->registersSize = method->insSize;
   method->outsSize = 0;

   if (PTR_gDvmJit != NULL) { // reset JIT cache char currentValue = *((char*)PTR_gDvmJit +MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull)); if (currentValue == 0 || currentValue == 1) { MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true; } else { ALOGE("Unexpected current value for codeCacheFull: %d",currentValue); }
    }
}

这个过程跟ADBI框架类似,先获取要挂钩的java函数的 Method 结构体指针,然后检查一下是否已经被挂钩了,如果是直接返回,否则,通过对Method 结构体进行赋值的方式,完成挂钩
method->nativeFunc =&xposedCallHandler;
method->insns = (const u2*) hookInfo;
method->registersSize = method->insSize;
method->outsSize = 0;
这里挂钩函数全部使用 xposedCallHandler这个函数。挂钩的详细信息保存在 Method结构体的 insns 成员里

xposed.cpp

static void xposedCallHandler(const u4*args, JValue* pResult, const Method* method, ::Thread* self) {
...
JValue result;
   dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result,
       originalReflected, (int) original, additionalInfo, thisObject,argsArray);
...
}

可以看到,最终执行xposedHandleHookedMethod这个native函数,这个native函数前面 initNative 执行时,已经获取了它的java实现,真正的实现在
XposedBridge.java

private static ObjecthandleHookedMethod(Member method, int originalMethodId, ObjectadditionalInfoObj,
           Object thisObject, Object[] args) throws Throwable {
       AdditionalHookInfo additionalInfo = (AdditionalHookInfo)additionalInfoObj;

       if (disableHooks) {
           try {
                returninvokeOriginalMethodNative(method, originalMethodId,additionalInfo.parameterTypes,
                        additionalInfo.returnType, thisObject,args);
           } catch (InvocationTargetException e) {
                throw e.getCause();
           }
       }

       Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();
       final int callbacksLength = callbacksSnapshot.length;
       if (callbacksLength == 0) {
           try {
                returninvokeOriginalMethodNative(method, originalMethodId,additionalInfo.parameterTypes,
                       additionalInfo.returnType, thisObject, args);
           } catch (InvocationTargetException e) {
                throw e.getCause();
           }
       }

       MethodHookParam param = new MethodHookParam();
       param.method = method;
       param.thisObject = thisObject;
       param.args = args;

       // call "before method" callbacks
       int beforeIdx = 0;
       do {
           try {
                ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
           } catch (Throwable t) {
                XposedBridge.log(t);

                // reset result (ignoring what the unexpectedly exitingcallback did)
                param.setResult(null);
                param.returnEarly = false;
               continue;
           }

           if (param.returnEarly) {
                // skip remaining"before" callbacks and corresponding "after" callbacks
                beforeIdx++;
                break;
           }
       } while (++beforeIdx < callbacksLength);

       // call original method if not requested otherwise
       if (!param.returnEarly) {
           try {
                param.setResult(invokeOriginalMethodNative(method,originalMethodId,
                       additionalInfo.parameterTypes, additionalInfo.returnType,param.thisObject, param.args));
           } catch (InvocationTargetException e) {
                param.setThrowable(e.getCause());
           }
       }

       // call "after method" callbacks
       int afterIdx = beforeIdx - 1;
       do {
           Object lastResult = param.getResult();
           Throwable lastThrowable = param.getThrowable();

            try {
                ((XC_MethodHook)callbacksSnapshot[afterIdx]).afterHookedMethod(param);
           } catch (Throwable t) {
                XposedBridge.log(t);

                // reset to last result (ignoring what the unexpectedlyexiting callback did)
                if (lastThrowable == null)
                   param.setResult(lastResult);
                else
                   param.setThrowable(lastThrowable);
           }
       } while (--afterIdx >= 0);

       // return
       if (param.hasThrowable())
           throw param.getThrowable();
       else
           return param.getResult();
    }

这个函数查找被挂钩函数的挂钩 XC_MethodHook结构体,然后执行里边的 beforeHookedMethod函数,再通过 invokeOriginalMethodNative执行挂钩前的原始函数,最后再执行 afterHookedMethod 函数。

findAndHookMethod 的实现就分析完了,本质上仍然是寻找被挂钩函数的 Method 结构体,将Method属性改为native ,然后对其成员 nativeFunc, registersize 等进行赋值,其中 insns 成员保存了挂钩的详细信息。所有被挂钩的函数,其nativeFunc都赋值为 xposedCallHandler 函数,该函数最终执行 XposedBridge.class 里的 handleHookedMethod 。 handleHookedMethod 寻找 xposed模块及xposed框架调用 findAndHookMethod 注册的 before,after 函数,如果有,就执行,再通过invokeOriginalMethodNative 执行挂钩前函数。

回到 initXbridgeZygote 函数,xposed 框架预先挂钩的函数,除了 handleBindApplication外,还有
com.android.server.ServerThread 系统thread创建时触发
hookAllConstructors(LoadedApk.class, new XC_MethodHook(){。。。} apk 包加载时触发这个挂钩
findAndHookMethod(“android.app.ApplicationPackageManager”, null,”getResourcesForApplication”, 。。。

  1. loadModules
/** * Try to load all modules defined in<code>BASE_DIR/conf/modules.list</code> */
   private static void loadModules(String startClassName) throwsIOException {
       BufferedReader apks = new BufferedReader(new FileReader(BASE_DIR +"conf/modules.list"));
       String apk;
       while ((apk = apks.readLine()) != null) {
           loadModule(apk, startClassName);
       }
       apks.close();
    }

xposed框架本身挂钩的函数很少,真正的挂钩由具体的xposed模块实现,xposed模块也是正常的app,安装的时候注册其挂钩信息到xposed框架的modules.list 等配置里。xposed框架初始化的时候, 加载这些模块,并完成挂钩函数

/** * Load a module from an APK by calling the init(String) method for allclasses defined * in assets/xposed_init. */
   @SuppressWarnings("deprecation")
    privatestatic void loadModule(String apk, String startClassName) {...
             while ((moduleClassName =moduleClassesReader.readLine()) != null) {// call the init(String) method of the module
                    final Object moduleInstance= moduleClass.newInstance();
                    if (startClassName == null){
                        if (moduleInstanceinstanceof IXposedHookZygoteInit) {
                           IXposedHookZygoteInit.StartupParam param = newIXposedHookZygoteInit.StartupParam();
                            param.modulePath =apk;
                            ((IXposedHookZygoteInit)moduleInstance).initZygote(param);
                        }

                        if (moduleInstanceinstanceof IXposedHookLoadPackage)
                            hookLoadPackage(newIXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));

                        if (moduleInstanceinstanceof IXposedHookInitPackageResources)
                           hookInitPackageResources(newIXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources)moduleInstance));
                    } else {
                        if (moduleInstanceinstanceof IXposedHookCmdInit) {
                           IXposedHookCmdInit.StartupParam param = newIXposedHookCmdInit.StartupParam();
                            param.modulePath =apk;
                           param.startClassName = startClassName;
                           ((IXposedHookCmdInit) moduleInstance).initCmdApp(param);
                        }
                    }
                }
}

loadModule 函数从配置文件里读取所有的模块,实例化模块类,xposed 提供了几个接口类供xposed模块继承,不同的接口类对应不同的hook时机
IXposedHookZygoteInit zygote 初始化前就执行挂钩,即loadModule执行时就挂钩了
IXposedHookLoadPackage apk包加载的时候执行挂钩,先将挂钩函数保存起来,等加载apk函数执行后触发callback (这里的callback是xposed框架自己挂钩的函数),再执行模块注册的挂钩函数
IXposedHookInitPackageResources apk资源实例化时执行挂钩,同上

你可能感兴趣的:(Xposed笔记(一)Xposed初识.续)