Andfix学习记录.docx
- 文档编号:11634182
- 上传时间:2023-03-29
- 格式:DOCX
- 页数:20
- 大小:276.31KB
Andfix学习记录.docx
《Andfix学习记录.docx》由会员分享,可在线阅读,更多相关《Andfix学习记录.docx(20页珍藏版)》请在冰豆网上搜索。
Andfix学习记录
Andfix学习记录
HowtoUse(官方)
InitializePatchManager,
`patchManager=newPatchManager(context);
patchManager.init(appversion);//currentversion`
Loadpatch,
`patchManager.loadPatch();`
Youshouldloadpatchasearlyaspossible,generally,intheinitializationphaseofyourapplication(suchasApplication.onCreate()).
Addpatch,
`patchManager.addPatch(path);//pathofthepatchfilethatwasdownloaded`
Whenanewpatchfilehasbeendownloaded,itwillbecomeeffectiveimmediatelybyaddPatch
还有一点就是混淆需要注意
`-keepclass*extendsjava.lang.annotation.Annotation
-keepclasseswithmembernamesclass*{
native
}
-keepclasscom.alipay.euler.andfix.**{*;}
`
命令输入有点麻烦,可以自己写一个win的脚本
apkpatch-f2.apk-t1.apk-o.-kfinance.keystore-p-a-e
这样基本可以照猫画虎折腾热更新了,当然不要忘记添加读写权限
` name="android.permission.WRITE_EXTERNAL_STORAGE"/> name="android.permission.READ_EXTERNAL_STORAGE"/>` 原理篇 andfix深入 补丁深入 andfix原理 andfix的核心原理就是方法替换在通过其apath工具给需要替换的方法加上注解@repleaceMethod,这样在执行时把有bug的方法替换成补丁文件中执行的方法。 (在Native层使用指针替换的方式替换bug的方法,从而达到修复bug的目的),具体过程如下图: 使用虚拟机的JarFile加载的补丁文件,读取PATCH.MF文件得到补丁类名称 获取补丁方法 使用DexFile读取patch文件的dex文件,获取后根据注解获取补丁方法 获取bug所在的方法 根据注解中获取到的类名和方法,使用ClassLaoder获取到class,然后根据反射得到bugMethod,并将其访问属性修改为public —————————————–Java层————————————————————- Native层替换方法 使用JNI来替换bug所在方法对象的属性来修复bug 简要类之间关系图 修复的具体过程为: 1)我们及时修复好bug之后,我们可以apkpatch工具将两个apk做一次对比,然后找出不同的部分。 生成的apatch了文件。 若果这个时候,我们把后缀改成zip再解压开,里面有一个dex文件。 反编译之后查看一下源码,里面就是被修复的代码所在的类文件,这些更改过的类都加上了一个_CF的后缀,并且变动的方法都被加上了一个叫@MethodReplace的annotation,通过clazz和method指定了需要替换的方法。 (后面补丁原理会说到) 2)客户端得到补丁文件后就会根据annotation来寻找需要替换的方法。 从AndFixManager.fix方法开始,客户端找到对应的需要替换的方法,然后在fix方法的具体实现中调用fixClass方法进行方法替换过程。 3)由JNI层完成方法的替换。 fixClass方法遍历补丁class里的方法,在jni层对所需要替换的方法进行一一替换。 (AndfixManager#replaceMethod) 源码解析 遵循使用时四步走: Step1: 初始化PatchManger `PatchManagerpatchManager=newPatchManager();` 参阅patchManager类源码——>AndfixManager其中包含了Compat兼容性检测类、SecurityChecker安全性检查类 `publicAndFixManager(Contextcontext){ mContext=context; //判断机型是否支持Andfix阿里的YunOs不支持 mSupport=Compat.isSupport(); if(mSupport){ //初始化签名判断类 mSecurityChecker=newSecurityChecker(mContext); mOptDir=newFile(mContext.getFilesDir(),DIR); //makedirectoryfail if(! mOptDir.exists()&&! mOptDir.mkdirs()){ mSupport=false; Log.e(TAG,"optdircreateerror."); }elseif(! mOptDir.isDirectory()){//notdirectory //如果不是目录则删除 mOptDir.delete(); mSupport=false; } } }` Step2: 使用PatchManger检查版本 `patchManager.init(apk版本)` 参阅patchManager#init——>Patch构造函数初始化init 主要是版本比对,记录版本号;根据版本号将patch清除或者加载到缓存中 参阅Patch#init `publicvoidinit(StringappVersion){ if(! mPatchDir.exists()&&! mPatchDir.mkdirs()){//makedirectoryfail Log.e(TAG,"patchdircreateerror."); return; }elseif(! mPatchDir.isDirectory()){//notdirectory mPatchDir.delete(); return; } SharedPreferencessp=mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);//缓存版本号 Stringver=sp.getString(SP_VERSION,null); if(ver==null||! ver.equalsIgnoreCase(appVersion)){ //根据传入版本号作对比,若不同,则删除本地的补丁文件 cleanPatch(); sp.edit().putString(SP_VERSION,appVersion).commit();//传入新的版本号 }else{ initPatchs();//初始化patch列表,把本地的patch加载到内存中 } } privatevoidinitPatchs(){ File[]files=mPatchDir.listFiles(); for(Filefile: files){ addPatch(file); } }` Patch文件的加载使用JarFile读取Patch文件,读取一些属性如patchname,createtime,其中如果本地保存了多个补丁,那么AndFix会按照补丁生成的时间顺序加载补丁。 具体是根据.apatch文件中的PATCH.MF的字段Created-Time。 step3: loadPatch `patchManager.loadPatch();` 参阅patchManager#loadPatch 提供了3个重载方法 `publicvoidloadPatch()//andfix初始化之后调用 privatevoidloadPatch(Patchpatch)//下载补丁完成后调用,addPatch(path) publicvoidloadPatch(StringpatchName,ClassLoaderclassLoader)//提供了自定义类加载器的实现 ` 这三个核心都是调用了publicsynchronizedvoidfix(Filefile,ClassLoaderclassLoader,Listclasses) 参看AndfixManager#fix `publicsynchronizedvoidfix(Filefile,ClassLoaderclassLoader, List if(! mSupport){ return; } //判断补丁的签名 if(! mSecurityChecker.verifyApk(file)){//securitycheckfail return; } try{ Fileoptfile=newFile(mOptDir,file.getName()); booleansaveFingerprint=true; if(optfile.exists()){ //needtoverifyfingerprintwhentheoptimizefileexist, //preventsomeoneattackonjailbreakdevicewith //Vulnerability-Parasyte. //btw: exaggeratedandroidVulnerability-Parasyte // //如果本地已经存在补丁文件,则校验指纹信息 if(mSecurityChecker.verifyOpt(optfile)){ saveFingerprint=false; }elseif(! optfile.delete()){ return; } } //加载patch文件中的dex finalDexFiledexFile=DexFile.loadDex(file.getAbsolutePath(), optfile.getAbsolutePath(),Context.MODE_PRIVATE); if(saveFingerprint){ mSecurityChecker.saveOptSig(optfile); } ClassLoaderpatchClassLoader=newClassLoader(classLoader){ //重写了ClassLoader的findClass方法 @Override protectedClass >findClass(StringclassName) throwsClassNotFoundException{ Class >clazz=dexFile.loadClass(className,this); if(clazz==null &&className.startsWith("com.alipay.euler.andfix")){ returnClass.forName(className);//annotation注解class //notfound } if(clazz==null){ thrownewClassNotFoundException(className); } returnclazz; } }; Enumeration Class >clazz=null; while(entrys.hasMoreElements()){ Stringentry=entrys.nextElement(); if(classes! =null&&! classes.contains(entry)){ continue;//skip,notneedfix } clazz=dexFile.loadClass(entry,patchClassLoader);//获取有bug的类文件 if(clazz! =null){ fixClass(clazz,classLoader);//核心- } } }catch(IOExceptione){ Log.e(TAG,"pacth",e); } }` fix——>fixclass——>replaceMethod——>Andfix#replaceMethod(Methoddest,Methodsrc)Native方法 `privatevoidfixClass(Class >clazz,ClassLoaderclassLoader){ //反射找到clazz中的所有方法 Method[]methods=clazz.getDeclaredMethods(); //MethodReplace的注解 MethodReplacemethodReplace; Stringclz; Stringmeth; for(Methodmethod: methods){ //遍历所有方法,找到有MethodReplace注解的方法,即需要替换的方法 methodReplace=method.getAnnotation(MethodReplace.class);//获取此方法的注解,有bug的方法生成patch的类中的方法都是有注解的 if(methodReplace==null) continue; clz=methodReplace.clazz();//获取注解中的clazz的值 meth=methodReplace.method();//获取注解中method的值 if(! isEmpty(clz)&&! isEmpty(meth)){ //找到需要替换的方法后调用replaceMethod替换方法 replaceMethod(classLoader,clz,meth,method); } } } ` `privatevoidreplaceMethod(ClassLoaderclassLoader,Stringclz, Stringmeth,Methodmethod){ try{ Stringkey=clz+"@"+classLoader.toString(); //根据key查找缓存中的数据,该缓存记录了已经被修复过得class Class >clazz=mFixedClass.get(key); if(clazz==null){//classnotload //找不到说明该class没有被修复过,则通过类加载器去加载 Class >clzz=classLoader.loadClass(clz); //initializetargetclass //通过C层改写accessFlag,把需要替换的类的所有方法(Field)改成了public clazz=AndFix.initTargetClass(clzz);//初始化targetclass } if(clazz! =null){//initializeclassOK mFixedClass.put(key,clazz); Methodsrc=clazz.getDeclaredMethod(meth, method.getParameterTypes());//根据反射拿到有bug的类的方法 //这里是调用了jni,art和dalvik分别执行不同的替换逻辑,在cpp进行实现 AndFix.addReplaceMethod(src,method);//替换方法src是有bug的方法,method是补丁方法 } }catch(Exceptione){ Log.e(TAG,"replaceMethod",e); } }` Natvie方法的分析见下面 前三步都是一开始初始化时候要做的,而最后一步第四步则是补丁下载好之后再做的 step4: 添加Patch `patchManager.addPatch(path)` 参阅PatchManager#addPatch,最终还是执行loadpatch appPatch——>copy到andfix默认的文件夹下——>执行loadPatch(补丁立即生效) `publicvoidaddPatch(Stringpath)throwsIOException{ Filesrc=newFile(path); Filedest=newFile(mPatchDir,src.getName()); if(! src.exists()){ thrownewFileNotFoundException(path); } if(dest.exists()){ Log.d(TAG,"patch["+path+"]hasbeloaded."); return; } //这一步很重要,通过这一步将你所下载保存的patch文件,copy到andfix自己默认的文件夹内存的data/data/apatch FileUtil.copyFile(src,dest);//copytopatch'sdirectory Patchpatch=addPatch(dest); if(patch! =null){ //加载patch补丁立即生效 loadPatch(patch); } }` 小结一下: 可以看出andfix的核心就是两大步 -java层实现加载补丁文件,安全验证等操作,然后根据补丁汇总的注解找到将要替换的方法,交给Native层去处理替换方法 -native层: 利用Javahook的技术来替换要修复的方法 附Native分析 在JNI目录下art和darvik文件中 andfix.cpp#replaceMethod——>art_method_replace.cpp(根据版本)——art_method_replace_5_0.cpp Dalvik Dalvik是Google公司自己设计用于Android平台的Java虚拟机。 Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。 它可以支持已转换为.dex(即DalvikExecutable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。 Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为一个独立的Linux进程执行。 独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。 ART Android操作系统已经成熟,Google的Android团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的Dalvik运行时。 Google开发者已经花了两年时间开发更快执行效率更高更省电的替代ART运行时。 ART代表AndroidRuntime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。 开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。 ART则完全改变了这套做法,在应用安装时就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)编译。 在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。 -优缺点 ART优点: 1、系统性能的显著提升。 2、应用启动更快、运行更快、体验更流畅、触感反馈更及时。 3、更长的电池续航能力。 4、支持更低的硬件。 ART缺点: 1、更大的存储空间占用,可能会增加10%-20%。 2、更长的应用安装时间。 总的来说ART的功效就是“空间换时间”。 其他重要函数 PatchManage#removeAllPatch() 这个函数是在PatchManage#init(viersin)verision不同时调用的方法一样,清空补丁目录文件,这在做保护的时候十分重要。 `publicvoidremoveAllPatch(){ cleanPatch(); SharedPreferencessp=mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); sp.edit().clear().commit(); }` 比如在laodPatch,包括初始化的时候patchManager.loadPatch()和patchManager.addPatch(其实也是调用loadpath) `publicvoidloadPatch(){ mLoaders.put("*",mContext.getClassLoader());//wildcard Set List for(Patchpatch: mPatchs){ patchNames=patch.getPatchNames(); for(StringpatchName: patchNames){ classes=patch.getClasses(patchName);//获取patch对用的class类集合 mAndFixMan
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Andfix 学习 记录