AndFix解析——(下)
我們接著分析阿里開源的AndFix庫, 上一篇分析了Patch類,這個類相當于我們提供補丁的容器,容器里有了東西,我們要對容器進行操作了, 于是開始了我們這次的分析。
在第二篇里,我們添了Patch類的那個坑,那么這篇文章我們就把最后兩個坑填一填,即loadPatch()方法和AndFixManager類。
在阿里給的Demo里,我們還有最后的loadPatch()方法沒有深入,所以先從loadPatch()方法開始。
PatchManager loadPatch()
public void loadPatch() {mLoaders.put("*", mContext.getClassLoader());// wildcardSet<String> patchNames;List<String> classes;for (Patch patch : mPatchs) {patchNames = patch.getPatchNames();for (String patchName : patchNames) {classes = patch.getClasses(patchName);mAndFixManager.fix(patch.getFile(),mContext.getClassLoader(), classes);}} }不知道大家是否還記得之前提到的mLoaders這個成員變量,隔了這么久,說實話我也忘記了,在這里我先帶大家一起回憶一下,?private final Map<String, ClassLoader> mLoaders;,mLoaders原來是儲存不同ClassLoader的Map啊。 好的,我們繼續向下進行,在第一篇,通過private的initPatchs()方法,和public的addPatch()方法,將Patch加入了mPatchs這個List, 所以,這里只要去遍歷這個List,來獲取不同的Patch,并對每個Patch做操作即可。 每個Patch,代表了一個.apatch的文件,getClasses(patchName)代表著這個patch的patchName對應的所有需要修改的類。?patchName從兩方面而來,一個是你用apkpatch.jar的時候使用-n選項指定或者默認的,另外一個方面是寫入Manifest的時候以?-Classes結尾的key,這個我暫時還沒有遇到過,遇到過的同學可以給我講講,抱歉博客暫時沒有評論功能,可以發郵件給我,airzhaoyn@gmail.com。
好了,這里講的差不多了,我們繼續深入。 可以看到,獲取了一個patchName對應的所有的需要修改的類后,就會調用AndFixManager類的fix(File, ClassLoader,List<String>)方法, 先來看看源碼
AndFixManager fix(File, ClassLoader,List)
/*** fix* * @param file* patch file* @param classLoader* classloader of class that will be fixed* @param classes* classes will be fixed*/ public synchronized void fix(File file, ClassLoader classLoader,List<String> classes) {//是否是支持的Android版本(在AndFixManager類初始化的時候會修改mSupport變量)if (!mSupport) {return;}//驗證這個文件的簽名和此應用是否一致if (!mSecurityChecker.verifyApk(file)) {// security check failreturn;}//這段代碼的源碼中的注釋很清楚,我就不寫了File optfile = new File(mOptDir, file.getName());boolean saveFingerprint = true;if (optfile.exists()) {// need to verify fingerprint when the optimize file exist,// prevent someone attack on jailbreak device with// Vulnerability-Parasyte.// btw:exaggerated android Vulnerability-Parasyte// http://secauo.com/Exaggerated-Android-Vulnerability-Parasyte.htmlif (mSecurityChecker.verifyOpt(optfile)) {saveFingerprint = false;} else if (!optfile.delete()) {return;}}//startfinal DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),optfile.getAbsolutePath(), Context.MODE_PRIVATE);if (saveFingerprint) {mSecurityChecker.saveOptSig(optfile);}ClassLoader patchClassLoader = new ClassLoader(classLoader) {@Overrideprotected Class<?> findClass(String className)throws ClassNotFoundException {Class<?> clazz = dexFile.loadClass(className, this);if (clazz == null&& className.startsWith("com.alipay.euler.andfix")) {return Class.forName(className);// annotation’s class// not found}if (clazz == null) {throw new ClassNotFoundException(className);}return clazz;}};//Enumerate the names of the classes in this DEX file.Enumeration<String> entrys = dexFile.entries();Class<?> clazz = null;while (entrys.hasMoreElements()) {String entry = entrys.nextElement();if (classes != null && !classes.contains(entry)) {continue;// skip, not need fix}clazz = dexFile.loadClass(entry, patchClassLoader);if (clazz != null) {fixClass(clazz, classLoader);}} }對這部分的源碼解析,從源碼中標注//start開始。 我們先看一下DexFile是什么,官方文檔這樣說:Manipulates DEX files. It is used primarily by class loaders.?就是主要被類加載器使用的操作Dex文件的類。 好了,我們可以繼續看源碼了,先獲取一個DexFile對象的,然后通過匿名內部類實現了一個ClassLoaders的子類,遍歷這個Dex文件中所有的類, 如果需要修改的類集合(即從PatchManager loadPatch()方法中傳過來的類集合)中在這個Dex文件中找到了一樣的類,則使用loadClass(String, ClassLoader)加載這個類, 然后調用fixClass(String, ClassLoader)修復這個類。
AndFixManager fixClass(Class<?>, ClassLoader)
private void fixClass(Class<?> clazz, ClassLoader classLoader) {//使用反射獲取這個類中所有的方法Method[] methods = clazz.getDeclaredMethods();//MethodReplace是這個庫自定義的Annotation,標記哪個方法需要被替換MethodReplace methodReplace;String clz;String meth;for (Method method : methods) {//找到被MethodReplace注解的方法methodReplace = method.getAnnotation(MethodReplace.class);if (methodReplace == null)continue;clz = methodReplace.clazz();meth = methodReplace.method();if (!isEmpty(clz) && !isEmpty(meth)) {replaceMethod(classLoader, clz, meth, method);}} }在源碼中基本把這個方法給解讀完了,接下來就要看看它調用的replaceMethod(ClassLoader, String,String, Method)方法
AndFixManager replaceMethod(ClassLoader, String,String, Method)
private void replaceMethod(ClassLoader classLoader, String clz,String meth, Method method) {//對每個類創建一個不會沖突的keyString key = clz + "@" + classLoader.toString();//mFixedClass是一個Map<String, Class<?>>,并被實例化為ConcurrentHashMap<>();Class<?> clazz = mFixedClass.get(key);if (clazz == null) {// class not loadClass<?> clzz = classLoader.loadClass(clz);// initialize target class and modify access flag of class’ fields to publicclazz = AndFix.initTargetClass(clzz);}if (clazz != null) {// initialize class OKmFixedClass.put(key, clazz);//獲取名為meth的方法Method src = clazz.getDeclaredMethod(meth,method.getParameterTypes());AndFix.addReplaceMethod(src, method);} }這里源代碼中的英文注釋(作者注釋)已經很清楚了,去看看調用的那個靜態方法AndFix.addReplaceMethod(src, method);
AndFix addReplaceMethod(Method, Method)
public static void addReplaceMethod(Method src, Method dest) {replaceMethod(src, dest);initFields(dest.getDeclaringClass()); }replaceMethod是一個native方法,聲明如下:?private static native void replaceMethod(Method dest, Method src);?由于暫時我對JNI不是很熟悉,所以這里就不分析了。 看看另外一個方法initFields(Class<?>)
/*** modify access flag of class’ fields to public* * @param clazz* class*/ private static void initFields(Class<?> clazz) {Field[] srcFields = clazz.getDeclaredFields();for (Field srcField : srcFields) {Log.d(TAG, "modify " + clazz.getName() + "." + srcField.getName()+ " flag:");setFieldFlag(srcField);} }這里英文注釋也很清楚了,只是其中調用了setFieldFlag(srcField);這個我們沒見過的方法, 聲明為private static native void setFieldFlag(Field field);?又是個JNI方法,暫時不進行分析。
到這里,對阿里AndFix庫Java層面上的代碼的分析就結束了,如果還想了解native層的代碼,我將會放在下一篇,敬請等待。
原文地址: http://yunair.github.io/blog/2015/10/23/AndFix-%E8%A7%A3%E6%9E%90(%E4%B8%8B).html
總結
以上是生活随笔為你收集整理的AndFix解析——(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AndFix解析——(中)
- 下一篇: Android上玩玩Hook?