Xposed学习二:实现机制

转:http://blog.csdn.net/a6624624/article/details/48138475

在上一篇我们学习了如何在AS中创建Xposed模块,本篇来分析下官方教程中redClock的实现原理。本系列文章基于version-51

[java]  view plain  copy
  1. <span style="font-size:18px;"><span style="font-size:18px;">public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {  
  2.         if(!lpparam.packageName.equals("com.android.systemui")) return;  
  3.         XposedBridge.log("we are in systemui !");  
  4.   
  5.         findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock"new XC_MethodHook() {  
  6.             @Override  
  7.             protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  
  8.                 super.beforeHookedMethod(param);  
  9.             }  
  10.   
  11.             @Override  
  12.             protected void afterHookedMethod(MethodHookParam param) throws Throwable {  
  13.                 TextView tv = (TextView) param.thisObject;  
  14.                 String text = tv.getText().toString();  
  15.                 tv.setText(text + ")");  
  16.                 tv.setTextColor(Color.RED);  
  17.             }  
  18.         });  
  19.     }</span></span>  
 上面的代码可以将原先在状态栏的时钟文本颜色变成红色,且在后面加")"。看下图:

               

主要的实现代码在findAndHookMethod函数中,查看函数定义:

findAndHookMethod:

——>findMethodExact(clazz,methodName,paramterClasses);

——>XposedBridge.hookMethod(method,callback);

先看findMethodExact,

[java]  view plain  copy
  1. <span style="font-size:18px;"><span style="font-size:18px;">public static Method findMethodExact(Class<?> clazz, String methodName, Class... parameterTypes) {  
  2.         StringBuilder sb = new StringBuilder(clazz.getName());  
  3.         sb.append('#');  
  4.         sb.append(methodName);  
  5.         sb.append(getParametersString(parameterTypes));  
  6.         sb.append("#exact");  
  7.     // sb = com.android.systemui.statusbar.policy.Clock#updateClock(参数1,参数2)#exact  
  8.           
  9.         String fullMethodName = sb.toString();  
  10.         Method e;</span></span>  
[java]  view plain  copy
  1. <span style="font-size:18px;"><span style="font-size:18px;"><span style="white-space:pre">    </span>//methodCache键值对存放fullMethodName,method对象  
  2.         if(methodCache.containsKey(fullMethodName)) {  
  3.             e = (Method)methodCache.get(fullMethodName);  
  4.             if(e == null) {  
  5.                 throw new NoSuchMethodError(fullMethodName);  
  6.             } else {  
  7.                 return e;  
  8.             }  
  9.         } else {  
  10.             try {  
  11.                 e = clazz.getDeclaredMethod(methodName, parameterTypes);  
  12.                 e.setAccessible(true);  
  13.                 methodCache.put(fullMethodName, e);  
  14.                 return e;  
  15.             } catch (NoSuchMethodException var6) {  
  16.                 methodCache.put(fullMethodName, (Object)null);  
  17.                 throw new NoSuchMethodError(fullMethodName);  
  18.             }  
  19.         }</span>  
  20.     }</span>  
       代码很简单就是要得到methodName在android中对应的函数对象,根据findAndHookMethod的参数得到字符串sb(格式参考注释行),用sb在methodCache这个hashMap查找有没有对应的method;若没有则根据methodName和parameterTypes利用getDeclaredMethod得到对应的method。看来重点是在XposedBridge.hookMethod:

[java]  view plain  copy
  1. <span style="font-size:18px;"><span style="font-size:18px;">public static Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {  
  2.         if(!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor)) {  
  3.             throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());  
  4.         } else if(hookMethod.getDeclaringClass().isInterface()) {  
  5.             throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());  
  6.         } else if(Modifier.isAbstract(hookMethod.getModifiers())) {  
  7.             throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());  
  8.         } else {  
  9.     //上面代码分步检查hookMethod的类型  
  10.     //else中的代码得到hookMethod对应的键值对  
  11.             boolean newMethod = false;  
  12.             Map declaringClass = sHookedMethodCallbacks;  
  13.             XposedBridge.CopyOnWriteSortedSet callbacks;  
  14.             synchronized(sHookedMethodCallbacks) {  
  15.                 callbacks = (XposedBridge.CopyOnWriteSortedSet)sHookedMethodCallbacks.get(hookMethod);  
  16.                 if(callbacks == null) {  
  17.                     callbacks = new XposedBridge.CopyOnWriteSortedSet();  
  18.                     sHookedMethodCallbacks.put(hookMethod, callbacks);  
  19.                     newMethod = true;  
  20.                 }  
  21.             }  
  22.               
  23.             callbacks.add(callback);  
  24.     //替换hookMehod的callbacks为callback,其实callback是存放的是hookMethod所有的callback,看定义:</span></span>  
[java]  view plain  copy
  1. <span style="font-size:18px;"><span style="white-space:pre">    </span>//<span style="font-size:18px;">final XposedBridge.CopyOnWriteSortedSet<XC_MethodHook> callbacks;  
  2.     //以上代码就是为hookMethod建立对应的callback list  
  3.     //sHookedMethodCallbacks存放hookMethod和callback  
  4.               
  5.             if(newMethod) {  
  6.                 Class declaringClass1 = hookMethod.getDeclaringClass();  
  7.                 int slot = XposedHelpers.getIntField(hookMethod, "slot");  
  8.                 Class[] parameterTypes;  
  9.                 Class returnType;  
  10.                 if(hookMethod instanceof Method) {  
  11.                     parameterTypes = ((Method)hookMethod).getParameterTypes();  
  12.                     returnType = ((Method)hookMethod).getReturnType();  
  13.                 } else {  
  14.                     parameterTypes = ((Constructor)hookMethod).getParameterTypes();  
  15.                     returnType = null;  
  16.                 }  
  17.         //以上代码得到method的参数和返回值,在AdditionalHookInfo下使用  
  18.         //把callback、method参数、method返回值汇总在AdditionalHookInfo类下  
  19.                 XposedBridge.AdditionalHookInfo additionalInfo = new XposedBridge.AdditionalHookInfo(callbacks, parameterTypes, returnType, (XposedBridge.AdditionalHookInfo)null);  
  20.                //本地函数在libxposed_dalvik.cpp  
  21.     <span style="color:#ff6666;">hookMethodNative</span>(hookMethod, declaringClass1, slot, additionalInfo);  
  22.             }  
  23.   
  24.             callback.getClass();  
  25.             return new Unhook(callback, hookMethod);  
  26.     //为callback绑定hookMthod  
  27.         }  
  28.     }</span></span>  

    上面乱七八糟的走了这么多,登记了2个hashmap{(fullMethodName,method对象),(hookmethod,callback)}。看来Java层只是管理这些结构并没有实质性的操作,进入native代码----Xposed.cpp:

[cpp]  view plain  copy
  1. <span style="font-size:18px;">//参数:reflectedMethodIndirect==>hookmethod,declaredClassIndirect==>hookmethod所在的类  
  2. //      slot==>slot,additionalInfoIndirect==>结构体包含callback、parameterTypes、returnType  
  3. void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,  
  4.             jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {  
  5.     // Usage errors?  
  6.     if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {  
  7.         dvmThrowIllegalArgumentException("method and declaredClass must not be null");  
  8.         return;  
  9.     }  
  10.   
  11.     // Find the internal representation of the method  
  12.     //获得dalvik中的classObject对象  
  13.     ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);  
  14.     //获得dalvik中的Method,被dalvik执行的函数体不同于java层的Method,位于Object.h  
  15.     Method* method = dvmSlotToMethod(declaredClass, slot);  
  16.     if (method == NULL) {  
  17.         dvmThrowNoSuchMethodError("Could not get internal representation for method");  
  18.         return;  
  19.     }  
  20.     //若method已被hook则直接返回  
  21.     if (isMethodHooked(method)) {  
  22.         // already hooked  
  23.         return;  
  24.     }  
  25.   
  26.     // Save a copy of the original method and other hook info  
  27.     XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));  
  28.     memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));  
  29.     hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));  
  30.     hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));  
  31.   
  32.     // Replace method with our own code  将method替换成我们自己的代码  
  33.     //设置method->accessFlags = ACC_NATIVE;表示method为native代码  
  34.     //下面几行代码都是为这行代码作补充   
  35.     //For a native method, we compute the size of the argument list,   
  36.     //and set "insSize" and "registerSize" equal to it.  
  37.     SET_METHOD_FLAG(method, ACC_NATIVE);  
  38.     //给method添加callback函数表示已被hooked  
  39.     <span style="color:#ff6666;">method->nativeFunc = &hookedMethodCallback;</span>  
  40.     //原本的insns中存放的是dex指令,现变为hookinfo为hookedMethodCallback准备  
  41.    <span style="color:#ff6666;"> method->insns = (const u2*) hookInfo;  
  42.     method->registersSize = method->insSize;  
  43.     method->outsSize = 0;</span>  
  44.   
  45.     if (PTR_gDvmJit != NULL) {  
  46.         // reset JIT cache  
  47.         char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));  
  48.         if (currentValue == 0 || currentValue == 1) {  
  49.             MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;  
  50.         } else {  
  51.             ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);  
  52.         }  
  53.     }  
  54.     </span>  
       代码主要的就是红色标注的,它将Method标为native code。dalvik虚拟机在执行Method时,则会直接调用其成员变量hookedMethodCallback执行。注意,这个时候已经改变了原本的Method的执行步骤了(Xposed在此刻觉醒啦啦啦)。看下面dalvik代码,/dalvik/vm/interp/Stack.c

[cpp]  view plain  copy
  1. <span style="font-size:18px;">void dvmCallMethodV(Thread* self, const Method* method, Object* obj,  
  2.     bool fromJni, JValue* pResult, va_list args)  
  3. {  
  4.     ......  
  5.   
  6.     if (<span style="color:#ff6666;">dvmIsNativeMethod</span>(method)) {  
  7.         TRACE_METHOD_ENTER(self, method);  
  8.         /* 
  9.          * Because we leave no space for local variables, "curFrame" points 
  10.          * directly at the method arguments. 
  11.          */  
  12.         (*method->nativeFunc)(self->curFrame, pResult, method, self);  
  13.         TRACE_METHOD_EXIT(self, method);  
  14.     } else {</span>  
[cpp]  view plain  copy
  1. <span style="font-size:18px;"><span style="white-space:pre">    </span>//这里是在Inter.cpp中直接解析method  
  2.         dvmInterpret(self, method, pResult);  
  3.     }  
  4.   
  5.     ......  
  6. }</span>  

一路走来感觉有点偏离主线类,回看下主题,在执行完 findAndHookMethod("com.Android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() );后。updateClock(hookmethod)的

accessflag = ACC_NATIVE,dalvik在执行updateClock方法时发现其为native code,则执行nativeFunc 函数体即

hookedMethodCallback。ok,找到"元凶"了,继续看代码:

[cpp]  view plain  copy
  1. <span style="font-size:18px;">/* This is called when a hooked method is executed. */  
  2. void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {  
  3. ......  
  4. <span style="white-space:pre">    </span>//call the Java handler function</span>  
[cpp]  view plain  copy
  1. <span style="font-size:18px;"><span style="white-space:pre">    </span>JValue result;  
  2.       
  3.     dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result,  
  4.         originalReflected, (int) original, additionalInfo, thisObject, argsArray);  
  5. ......  
  6. }</span>  
hookedMethodCallback 函数中主要是调用dvmCallMethod去执行xposedHandleHookedMethod,而xposedHandleHookedMethod是classXposedBridge里的handleHookedMethod方法。ok,重头戏来了

[java]  view plain  copy
  1. <span style="font-size:18px;">private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, Object thisObject, Object[] args) throws Throwable {  
  2.         XposedBridge.AdditionalHookInfo additionalInfo = (XposedBridge.AdditionalHookInfo)additionalInfoObj;  
  3.         if(disableHooks) {  
  4.             try {  
  5.         //hook不使能,执行原method  
  6.                 return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, thisObject, args);  
  7.             ......  
  8.             }  
  9.         } else {  
  10.     /得到hookmethod的callback,在之前的XposedBridge.hookMethod中为callbacks添加了callback  
  11.             Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();  
  12.             int callbacksLength = callbacksSnapshot.length;  
  13.             if(callbacksLength == 0) {  
  14.         //hookmethod的callback为空,hooked无意义,执行原method  
  15.                 try {  
  16.                     return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, thisObject, args);  
  17.                 ......  
  18.                 }  
  19.             } else {  
  20.                 ......  
  21.                 do {  
  22.                     label65: {  
  23.                         try {  
  24.                             ((XC_MethodHook)callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);  
  25.                         } catch (Throwable var18) {  
  26.                             log(var18);  
  27.                             param.setResult((Object)null);  
  28.                             param.returnEarly = false;  
  29.                             break label65;  
  30.                         }  
  31.   
  32.                         if(param.returnEarly) {  
  33.                             ++beforeIdx;  
  34.                             break;  
  35.                         }  
  36.                     }  
  37.   
  38.                     ++beforeIdx;  
  39.         //hookmethod有几个callback就循环几次  
  40.                 } while(beforeIdx < callbacksLength);  
  41.   
  42.                 if(!param.returnEarly) {  
  43.                     try {  
  44.             //在beforeHookedMethod后执行原method  
  45.                         param.setResult(invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));  
  46.                     } catch (InvocationTargetException var16) {  
  47.                         param.setThrowable(var16.getCause());  
  48.                     }  
  49.                 }  
  50.   
  51.                 int afterIdx = beforeIdx - 1;  
  52.   
  53.                 do {  
  54.                     Object lastResult = param.getResult();  
  55.                     Throwable lastThrowable = param.getThrowable();  
  56.   
  57.                     try {  
  58.                         ((XC_MethodHook)callbacksSnapshot[afterIdx]).afterHookedMethod(param);  
  59.                     } catch (Throwable var17) {  
  60.                         log(var17);  
  61.                         if(lastThrowable == null) {  
  62.                             param.setResult(lastResult);  
  63.                         } else {  
  64.                             param.setThrowable(lastThrowable);  
  65.                         }  
  66.                     }  
  67.   
  68.                     --afterIdx;  
  69.                 } while(afterIdx >= 0);  
  70.                 ......  
  71.             }  
  72.         }  
  73.     }</span>  
别看handleHookedMethod代码老长了,其实它很单纯。

第一步:是否需要执行callback,否则直接执行原method,gameover;

第二步:执行callbacks里的beforeHookedMethod方法,有几个callback执行几次beforeHookedMethod;

第三步:执行原method;

第四步:执行callbacks里的afterHookedMethod方法,类同beforeHookedMethod。

需要注意的是如果method有多个callback,其beforeHookedMethod和afterHookedMethod执行顺序:

A1.before->A2.before->原method->A2.after->A1.after,也是蛮符合客观规律的嘛。

好,关于findAndHookMethod()函数也算是从上倒下看了个遍,但你上面添这么多代码是算怎么回事呢?下面就简单总结下罗:



好了,本篇就先这样吧,太长了也不好看。下篇再分析下其余枝节:

1 handleLoadPackage 怎么生效

2 dalvik层如何回到java层(数据类型如何回传)

3 XC_MethodHook中会执行原来的java函数体,如何执行;

其他想到再分析啰,大家也可以对上述执行流程的细节质疑,我们一起探讨。


参考资料:

1Xposed框架Java部分

2Dalvik虚拟机的运行过程分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值