dexposed框架Android在线热修复
来源:互联网 发布:大学生单片机自学视频 编辑:程序博客网 时间:2024/04/30 09:21
如果是Webapp你可能最多花个1,2个小时紧急发布上线,但是app呢,打包,跪求市场发布几百个渠道,周末还发不了,app配置升级,你还不能配置
强制升级, 就算配置提示升级,用户心里肯定想前两天刚升级最新版,怎么又要升,而且升级要流量,这时候会很反感甚至卸载应用。所以安卓是否
有能力做到在线打补丁?dexposed给我们解决了这个问题。
1、淘宝Dexposed框架
喜欢刷机的人应该对xposed不陌生,Xposed框架是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它 可以制作出许多功
能强大的模块。其实Xposed安卓的一个开源框架,在github上的下载地址xposed,有兴趣的可以去研究下,dexPosed也是基于Xposed的。
他有几个典型的使用场景:
a. Classic AOP programming (aop编程)
b. Instrumentation (for testing, performance monitoring and etc.) 测试,性能监控
c. Online hot patch to fix critical, emergent or security bugs 线上打补丁,解决一些严重的,紧急的或者安全漏洞的bug。
d. SDK hooking for a better development experience
a、无侵入性,
b、使用Java代码编写,Aspectj使用脚本需要一定的学习成本
c、Aspectj有自己的编译器,需要编译下Aspectj代码,会注入他自己的代码
下面来看看如果写一些AOP的代码:
Attach a piece of code before and after all occurrences of Activity.onCreate(Bundle).
beforeHookedMethod和afterHookedMethod方法做一些具体的操作。我们可以看到,用dexposed可以实现不改变原函
数的执行,但是在原函数执行前后去做一些其他的额外处理,例如改变入参和返回值等等的一些事情。
Replace the original body of the target method. 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. ... } });
则是可以将原有要执行的函数替换成一个我们需要的新的执行函数。
这个框架目前对android 5.0系统支持度不高,不过框架更新的时间可以看出,持续维护中,不久将对art完全支持。他提供一个函数来检测你的android
系统是否支持 Dexposed,DexposedBridge.canDexposed(context)。你需要把补丁包打包成一个apk, 然后通过下面的代码来加载补丁包:
// Run taobao.patch apk public void runPatchApk() { if (android.os.Build.VERSION.SDK_INT == 21) { return; } if (!isSupport) { Log.d("dexposed", "This device doesn't support dexposed!"); return; } File cacheDir = getExternalCacheDir(); if (cacheDir != null) { String fullpath = cacheDir.getAbsolutePath() + File.separator + "patch.apk"; PatchResult result = PatchMain.load(this, fullpath, null); if (result.isSuccess()) { Log.e("Hotpatch", "patch success!"); } else { Log.e("Hotpatch", "patch error is " + result.getErrorInfo()); } } }
2.Dexposed原理
重点是搞清楚是怎样hook的。
1)首先通过ClassLoader把插件apk加载进入主工程
2)通过反射拿到具体的class类
3)DexposedBridge.findAndHookMethod定位到具体的方法中,对代码进行覆盖或者加入自定义功能
前面两点都是Java方法没什么可以说的,我这里重点分析下第三点:
a、DexposedBridge.findAndHookMethod()中调用了Xc_methdhook.hookMethos(),这个方法是通过传入的class,方法及签名去查找返回一个对应的Method。
然后把Method作为参数传入hookMethodNative(Method ...);
b、hookMethodNative是进行hook的主要方法,分析源码发现,它里面做了这些事情
1、把Java层的变量,类型转换成c能识别的指针类型;
// 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));
2、用我们自己的code代替,看到下面的nativeFun了吗,这个方法被dexposedCallHandler代替,也就是说被我们自己的回调方法代替了,这样我们就可以
随意的操作了。然后把要hook的对象参数保存到insns
// Replace method with our own code SET_METHOD_FLAG(method, ACC_NATIVE); method->nativeFunc = &dexposedCallHandler; method->insns = (const u2*) hookInfo; method->registersSize = method->insSize; method->outsSize = 0;
3、接下来我们去看看dexposedCallHandler做了哪些事情;
//赋值DexposedHookInfo* hookInfo = (DexposedHookInfo*) method->insns; Method* original = (Method*) hookInfo; Object* originalReflected = hookInfo->reflectedMethod; Object* additionalInfo = hookInfo->additionalInfo;
// call the Java handler function//对...这才是重点,dvmCallMethod方法回调了dexposedHandleHookedMethod方法,这个方法是在Java层实现的 JValue result; dvmCallMethod(self, dexposedHandleHookedMethod, NULL, &result, originalReflected, (int) original, additionalInfo, thisObject, argsArray); dvmReleaseTrackedAlloc((Object *)argsArray, self);
4、我们在去看看handleHookMethod做了什么,一看你就明朗了;
private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,Object thisObject, Object[] args) throws Throwable {AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;if (disableHooks) {try {return invokeOriginalMethodNative(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 {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;// call "before method" callbacksint beforeIdx = 0;do {try {((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);} catch (Throwable t) {DexposedBridge.log(t);// reset result (ignoring what the unexpectedly exiting callback did)param.setResult(null);param.returnEarly = false;continue;}if (param.returnEarly) {// skip remaining "before" callbacks and corresponding "after" callbacksbeforeIdx++;break;}} while (++beforeIdx < callbacksLength);// call original method if not requested otherwiseif (!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" callbacksint 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);// reset to last result (ignoring what the unexpectedly exiting callback did)if (lastThrowable == null)param.setResult(lastResult);elseparam.setThrowable(lastThrowable);}} while (--afterIdx >= 0);// returnif (param.hasThrowable())throw param.getThrowable();elsereturn param.getResult();}
判断是否hook成功,如果不成功执行invokeOriginalMethodNative,也就是执行原始函数;
成功则,这个函数查找被挂钩函数的挂钩 XC_MethodHook结构体,然后执行里边的 beforeHookedMethod函数,再通过 invokeOriginalMethodNative
执行挂钩前的原始函数,最后再执行 afterHookedMethod 函数。
findAndHookMethod 的实现就分析完了,
本质上仍然是寻找被挂钩函数的 Method 结构体,将Method属性改为native ,然后对其成员 nativeFunc,
registersize 等进行赋值,其中 insns 成员保存了挂钩的详细信息。所有被挂钩的函数,其nativeFunc都赋值为 dexposedCallHandler 函数,该函数最终执行 XposedBridge.class 里的 handleHookedMethod 。 handleHookedMethod 寻找dexposed模块及dexposed框架调用 findAndHookMethod 注册的 before,after
函数,如果有,就执行,再通过invokeOriginalMethodNative 执行挂钩前函数。
3、怎么使用dexposed
a、下载dexposed源码 dexposed
b、把下载的源码导入,只需要sample包,其中dexposedsample是有bug的主工程,patchsample为插件工程,(其他的两个为工具类源码,方便我们理解源码)
c、主工程配置gradle文件
apply plugin: 'com.android.application'android { compileSdkVersion 21 buildToolsVersion "21.1.2" defaultConfig { applicationId "com.taobao.dexposed" minSdkVersion 9 targetSdkVersion 21 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } android { packagingOptions { exclude 'AndroidManifest.xml' } } buildTypes { //Debug打开,使用测试环境接口 debug { } } lintOptions { abortOnError false } sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java'] res.srcDirs = ['src/main/res'] //路径根据自己的对应修改 } }}//我使用的是本地导入so包,task copyNativeLibs(type: Copy) { from fileTree(dir: 'libs', include: '*/*.so' ) into 'build/native-libs'}tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn copyNativeLibs }clean.dependsOn 'cleanCopyNativeLibs'tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask -> pkgTask.jniFolders = new HashSet<File>() pkgTask.jniFolders.add(new File(projectDir, 'libs'))}//---------------------------------//这段配置是把主工程打成jar包,这个jar包需要导入到patch工程中 task clearJar(type: Delete) { delete 'libs/sdk.jar'}task makeJar(type:org.gradle.api.tasks.bundling.Jar) { //指定生成的jar名 baseName 'sdk' //从哪里打包class文件 from('build/intermediates/classes/sample/debug/com') //打包到jar后的目录结构,根据自己工程需要写路径 into('com') //去掉不需要打包的目录和文件 exclude('test/', 'BuildConfig.class', 'R.class') //去掉R$开头的文件 exclude{ it.name.startsWith('R$');}}makeJar.dependsOn(clearJar, build)//---------------------------------------------dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile files('libs/dexposedbridge.jar')}patch工程gradle配置:
dependencies {// compile fileTree(dir: 'libs', include: ['*.jar']) provided files('libs/sdk.jar') provided files('libs/dexposedbridge.jar') provided files('libs/patchloader.jar') }
使用provided方式导入jar包,这样只会在编译时引用jar,不会把jar打包进patch.apk,防止与主工程引入包冲突;
用例代码就不贴了,自己去down一份
- dexposed框架Android在线热修复
- Alibaba-Dexposed框架在线热补丁修复的使用
- Alibaba-Dexposed框架在线热补丁修复的使用
- Alibaba-Dexposed框架在线热补丁修复的使用
- Android 在线热修复框架 AndFix 初步
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复
- Android RocooFix 热修复框架
- Android AndFix 热修复框架
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android AndFix 热修复框架
- Android热修复框架andfix
- Android RocooFix 热修复框架
- Android AndFix 热修复框架
- Java学习笔记:详解传值和传引用
- xcode APP 打包以及提交apple审核详细流程(新版本更新提交审核)
- MATLAB save函数使用
- 文章标题
- 集合, 数组,Comparable,Comparator
- dexposed框架Android在线热修复
- 在nodejs中使用富文本编辑器UEditor
- 杭电 HDU ACM 1225 Atlantis (线段树 扫描线 离散化 最基本)
- HDU 1025 DP+二分求解最长上升序列
- linux 文件操作 --copy_file
- Mysql replace 与 insert on duplicate效率分析
- 虚函数运行机制
- HDU 2577(How to Type)动态规划
- 本人常用资源整理(ing...)