转:http://blog.csdn.net/qxs965266509/article/details/50117137
Alibaba的AndFix热修复:
Alibaba-AndFix Bug热修复框架的使用
Alibaba-AndFix Bug热修复框架原理及源码解析
上一篇中已经介绍了Alibaba-Dexposed框架在线热补丁修复的使用 ,这篇主要是了解框架的原理和源码解析。
原理:
在Dalvik虚拟机下,主要是通过改变一个方法对象方法在Dalvik虚拟机中的定义来实现,具体做法就是将该方法的类型改变为Native并且将这个方法的实现链接到一个通用的Native Dispatch方法上。这个 Dispatch方法通过JNI回调到Java端的一个统一处理方法,最后在统一处理方法中调用before, after函数来实现AOP。在Art虚拟机上目前也是通过改变一个 ArtMethod的入口函数来实现。
在宿主项目的Application需要调用以下方法来判断手机是否支持Dexposed框架:
DexposedBridge.canDexposed(this);
canDexposed方法源码:
public static synchronized boolean canDexposed(Context context) { return !DeviceCheck.isDeviceSupport(context)?false:loadDexposedLib(context); }
可以看到,第一判断了机型是否支持,如果支持就加载lib文件。
DeviceCheck.isDeviceSupport()源码:
public static synchronized boolean isDeviceSupport(Context context) { boolean var2; try { if(!isCheckedDeviceSupport) { if(isDalvikMode() && isSupportSDKVersion() && !isX86CPU() && !isYunOS()) { isDeviceSupportable = true; return isDeviceSupportable; } isDeviceSupportable = false; return isDeviceSupportable; } var2 = isDeviceSupportable; } finally { Log.d("hotpatch", "device support is " + isDeviceSupportable + "checked" + isCheckedDeviceSupport); isCheckedDeviceSupport = true; } return var2; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
判断机型,主要判断的有是否是Dalvik虚拟机、sdk版本、是否是x86cpu架构、是否是YunOS系统。
loadDexposedLib加载lib的源码:
private static boolean loadDexposedLib(Context context) { try { if(VERSION.SDK_INT != 10 && VERSION.SDK_INT != 9) { if(VERSION.SDK_INT > 19) { System.loadLibrary("dexposed_l"); } else { System.loadLibrary("dexposed"); } } else { System.loadLibrary("dexposed2.3"); } return true; } catch (Throwable var2) { return false; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
根据sdk的不同版本加载不同的so文件。
以上仅是判断当然机型是否支持Dexposed框架的运行环境。
接下,就是对Dexposed的使用原理进行源码分析:
在上一篇提到,当加载补丁文件时,会扫描补丁文件中实现IPatch接口的所有的类。
IPatch定义如下:
public interface IPatch { void handlePatch(PatchParam var1) throws Throwable;}
就是说,修复bug的处理只能在handlePatch方法中实现。
官网也只提供了2种实现方式:
第一:
DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Activity instance = (Activity) param.thisObject; Bundle bundle = (Bundle) param.args[0]; Intent intent = new Intent(); XposedHelpers.setObjectField(param.thisObject, "mIntent", intent); if (bundle.containsKey("return")) param.setResult(null); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { XposedHelpers.callMethod(param.thisObject, "sampleMethod", 2); } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
第二:
DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { // Re-writing the method logic outside the original method context is a bit tricky but still viable. ... } });
调用的接口是相同的,只不过传递的回调接口不同。
第一种是在方法前后执行做一些处理,第二种就是直接把方法进行替换。
在这里,我们就重点看findAndHookMethod方法,跟着此方法追踪源码:
public static Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) { if(parameterTypesAndCallback.length != 0 && parameterTypesAndCallback[parameterTypesAndCallback.length - 1] instanceof XC_MethodHook) { XC_MethodHook callback = (XC_MethodHook)parameterTypesAndCallback[parameterTypesAndCallback.length - 1]; Method m = XposedHelpers.findMethodExact(clazz, methodName, parameterTypesAndCallback); Unhook unhook = hookMethod(m, callback); if(!(callback instanceof XC_MethodKeepHook) && !(callback instanceof XC_MethodKeepReplacement)) { ArrayList var6 = allUnhookCallbacks; synchronized(allUnhookCallbacks) { allUnhookCallbacks.add(unhook); } } return unhook; } else { throw new IllegalArgumentException("no callback defined"); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
hookMethod方法源码:
public static Unhook hookMethod(Member hookMethod, XC_MethodHook callback) { if(!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor)) { throw new IllegalArgumentException("only methods and constructors can be hooked"); } else { boolean newMethod = false; Map declaringClass = hookedMethodCallbacks; DexposedBridge.CopyOnWriteSortedSet callbacks; synchronized(hookedMethodCallbacks) { callbacks = (DexposedBridge.CopyOnWriteSortedSet)hookedMethodCallbacks.get(hookMethod); if(callbacks == null) { callbacks = new DexposedBridge.CopyOnWriteSortedSet(); hookedMethodCallbacks.put(hookMethod, callbacks); newMethod = true; } } callbacks.add(callback); if(newMethod) { Class declaringClass1 = hookMethod.getDeclaringClass(); int slot = runtime == 1?XposedHelpers.getIntField(hookMethod, "slot"):0; Class[] parameterTypes; Class returnType; if(hookMethod instanceof Method) { parameterTypes = ((Method)hookMethod).getParameterTypes(); returnType = ((Method)hookMethod).getReturnType(); } else { parameterTypes = ((Constructor)hookMethod).getParameterTypes(); returnType = null; } DexposedBridge.AdditionalHookInfo additionalInfo = new DexposedBridge.AdditionalHookInfo(callbacks, parameterTypes, returnType, (DexposedBridge.AdditionalHookInfo)null); hookMethodNative(hookMethod, declaringClass1, slot, additionalInfo); } callback.getClass(); return new Unhook(callback, hookMethod); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
hookMethodNative Native方法生命:
private static synchronized native void hookMethodNative(Member var0, Class<?> var1, int var2, Object var3);
Dalvik虚拟机的Native方法实现:
hookMethodNative Native层的代码实现:
static void com_taobao_android_dexposed_DexposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect, jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect); Method* method = dvmSlotToMethod(declaredClass, slot); if (method == NULL) if (dexposedIsHooked(method)) // Save a copy of the original method and other hook info DexposedHookInfo* hookInfo = (DexposedHookInfo*) calloc(1, sizeof(DexposedHookInfo)); memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct)); hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect)); hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect)); SET_METHOD_FLAG(method, ACC_NATIVE); method->insns = (const u2*) hookInfo; method->registersSize = method->insSize; method->outsSize = 0; if (PTR_gDvmJit != NULL) }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
method->nativeFunc = &dexposedCallHandler;
当虚拟机调用到这个存在bug的方法时就会调用这个Native方法:
dexposedCallHandler方法源码:
static void dexposedCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) { if (!dexposedIsHooked(method)) { dvmThrowNoSuchMethodError("could not find Dexposed original method - how did you even get here?"); return; } DexposedHookInfo* hookInfo = (DexposedHookInfo*) method->insns; Method* original = (Method*) hookInfo; Object* originalReflected = hookInfo->reflectedMethod; Object* additionalInfo = hookInfo->additionalInfo; const char* desc = &method->shorty[1]; Object* thisObject = NULL; size_t srcIndex = 0; size_t dstIndex = 0; if (!dvmIsStaticMethod(original)) { thisObject = (Object*) args[0]; srcIndex++; } ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass, strlen(method->shorty) - 1, ALLOC_DEFAULT); if (argsArray == NULL) { return; } while (*desc != '\0') { char descChar = *(desc++); JValue value; Object* obj; switch (descChar) { case 'Z': case 'C': case 'F': case 'B': case 'S': case 'I': value.i = args[srcIndex++]; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, self); break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(obj, self); break; case '[': case 'L': obj = (Object*) args[srcIndex++]; break; default: ALOGE("Unknown method signature description character: %c\n", descChar); obj = NULL; srcIndex++; } dexposedSetObjectArrayElement(argsArray, dstIndex++, obj); } JValue result; dvmCallMethod(self, dexposedHandleHookedMethod, NULL, &result, originalReflected, (int) original, additionalInfo, thisObject, argsArray); dvmReleaseTrackedAlloc((Object *)argsArray, self); if (dvmCheckException(self)) { return; } ClassObject* returnType = dvmGetBoxedReturnType(method); if (returnType->primitiveType == PRIM_VOID) { } else if (result.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowNullPointerException("null result when primitive expected"); } pResult->l = NULL; } else { if (!dvmUnboxPrimitive((Object *)result.l, returnType, pResult)) { dvmThrowClassCastException(((Object *)result.l)->clazz, returnType); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
这个方法主要就是调用Java的方法,实现调度。
调用的Java的方法是:
private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, Object thisObject, Object[] args) throws Throwable { AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj; Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot(); final int callbacksLength = callbacksSnapshot.length; if (callbacksLength == 0) { try { return invokeOriginalMethodNative(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; int beforeIdx = 0; do { try { ((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param); } catch (Throwable t) { log(t); param.setResult(null); param.returnEarly = false; continue; } if (param.returnEarly) { beforeIdx++; break; } } while (++beforeIdx < callbacksLength); if (!param.returnEarly) { try { param.setResult(invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args)); } catch (InvocationTargetException e) { param.setThrowable(e.getCause()); } } int afterIdx = beforeIdx - 1; do { Object lastResult = param.getResult(); Throwable lastThrowable = param.getThrowable(); try { ((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param); } catch (Throwable t) { DexposedBridge.log(t); if (lastThrowable == null) param.setResult(lastResult); else param.setThrowable(lastThrowable); } } while (--afterIdx >= 0); if (param.hasThrowable()) throw param.getThrowable(); else return param.getResult(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
这个方法里面了实现了调度机制,调用回调接口的方法和备份的Java方法。
本质上仍然是寻找被挂钩函数的 Method 结构体,将Method属性改为native ,然后对其成员 nativeFunc,
registersize 等进行赋值,其中 insns 成员保存了挂钩的详细信息。所有被挂钩的函数,其nativeFunc都赋值为 dexposedCallHandler 函数,该函数最终执行 XposedBridge.class 里的 handleHookedMethod 。 handleHookedMethod 寻找dexposed模块及dexposed框架调用 findAndHookMethod 注册的 before,after
函数,如果有,就执行,再通过invokeOriginalMethodNative 执行挂钩前函数。
MethodHookParam.thisObject:这个类的一个实例
MethodHookParam.args:用于传递被注入函数的所有参数
MethodHookParam.setResult:用于修改原函数调用的结果,如果在beforeHookedMethod回调函数中调用setResult,可以阻止对原函数的调用。但是如果有返回值的话仍然需要通过hook处理器进行return操作。
Art虚拟机的Native方法实现
static void com_taobao_android_dexposed_DexposedBridge_hookMethodNative( JNIEnv* env, jclass, jobject java_method, jobject, jint, jobject additional_info) { ScopedObjectAccess soa(env); art::Thread* self = art::Thread::Current(); jobject javaArtMethod = env->GetObjectField(java_method, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod); ArtMethod* method = soa.Decode<mirror::ArtMethod*>(javaArtMethod); LOG(INFO) << "dexposed: >>> hookMethodNative " << method << " " << PrettyMethod(method); EnableXposedHook(env, method, additional_info); }
EnableXposedHook:
static void EnableXposedHook(JNIEnv* env, ArtMethod* art_method, jobject additional_info) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { LOG(INFO) << "dexposed: >>> EnableXposedHook" << art_method << " " << PrettyMethod(art_method); if (dexposedIsHooked(art_method)) { return; } ScopedObjectAccess soa(env); ArtMethod* backup_method = down_cast<ArtMethod*>(art_method->Clone(soa.Self())); backup_method->SetAccessFlags(backup_method->GetAccessFlags() ); jobject reflect_method; if (art_method->IsConstructor()) { reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor); } else { reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Method); } env->SetObjectField(reflect_method, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, env->NewGlobalRef(soa.AddLocalReference<jobject>(backup_method))); DexposedHookInfo* hookInfo = reinterpret_cast<DexposedHookInfo*>(calloc(1, sizeof(DexposedHookInfo))); hookInfo->reflectedMethod = env->NewGlobalRef(reflect_method); hookInfo->additionalInfo = env->NewGlobalRef(additional_info); hookInfo->originalMethod = backup_method; jstring shorty = (jstring)env->GetObjectField(additional_info,additionalhookinfo_shorty_field); hookInfo->shorty = env->GetStringUTFChars(shorty, 0); LOG(INFO) << "dexposed: >>> EnableXposedHook shorty:" << hookInfo->shorty;#if PLATFORM_SDK_VERSION < 22 art_method->SetNativeMethod(reinterpret_cast<uint8_t *>(hookInfo));#else art_method->SetEntryPointFromJni(reinterpret_cast<void *>(hookInfo));#endif art_method->SetEntryPointFromQuickCompiledCode(GetQuickDexposedInvokeHandler()); art_method->SetAccessFlags((art_method->GetAccessFlags() & ~kAccNative) ); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
通过:
art_method->SetAccessFlags((art_method->GetAccessFlags()&~kAccNative)/| kAccXposedHookedMethod/);
art_method->SetEntryPointFromQuickCompiledCode(GetQuickDexposedInvokeHandler());
同样也是把把实现指向native方法实现调度机制来达到目的。
0 0