AndFix深入源码分析

AndFix是阿里出品的一款轻量级热修复框架,它可以在应用执行期间去修复那些有bug的方法。

它的实现思路其实并不复杂,首先它需要通过一个apkpatch工具来将一个有bug的apk和一个已经修复好了bug的apk进行对比,将修复好的方法提取出来,生成一个xxx.apatch文件,该文件实际上是一个压缩文件,它里面包含了修复好的一个.dex文件和该文件的一些描述信息文件META-INF,如图所示:
这里写图片描述
在META-INF中包含了一些签名信息和该patch的一些描述信息,其中最重要的就是这个PATCH.MF文件了,在修复的时候需要用到该文件。
这里写图片描述
PATCH.MF的内容形式如下,其中由一个Patch-Classes字段信息,在热修复的时候就是读取该字段的数据来找到需要被修复的类中的方法的。
这里写图片描述

有了补丁patch文件,AndFix通过JarFile这个类来加载该补丁文件,然后读取里面的PATCH.MF文件内容,从而获取到要修复的类和方法的名称,接着通过DexFile类来加载被修复好的dex文件,所有被修复好的方法都会被apkpatch工具打上一个MethodReplace注解,然后通过classloader将dex中的类加载到内存中,然后通过反射得到修复好的Method方法。

最后在native层将原来有bug的method的函数指针指向被修复好的method,从而达到了修复的目的。

对AndFix有了一个整体的认识后,下面从源码的角度来分析AndFix的实现机制。

源码分析

在使用时我们都是通过PatchManager这个类来调用的,PatchManager这个类并不复杂,这里就直接贴出分析后的源码

public class PatchManager {
    private static final String TAG = "PatchManager";
    //apkpatch工具生成的patch文件后缀名
    private static final String SUFFIX = ".apatch";
    //在本地保存的patch文件目录名称
    private static final String DIR = "apatch";
    //sharepreference文件名称
    private static final String SP_NAME = "_andfix_";
    //在sharepreference文件中保存的version的key
    private static final String SP_VERSION = "version";
    private final Context mContext;
    private final AndFixManager mAndFixManager;
    //保存patch文件的目录文件
    private final File mPatchDir;
    //所有patch文件集合,使用了Patch对象封装了每个patch文件
    private final SortedSet<Patch> mPatchs;
    //类加载器
    private final Map<String, ClassLoader> mLoaders;

    public PatchManager(Context context) {
        mContext = context;
        //创建了AndFixManager
        mAndFixManager = new AndFixManager(mContext);
        //保存patch文件的目录,在应用的file/apatch目录下
        mPatchDir = new File(mContext.getFilesDir(), DIR);
        //所有patch文件的集合
        mPatchs = new ConcurrentSkipListSet<Patch>();
        //累加器集合
        mLoaders = new ConcurrentHashMap<String, ClassLoader>();
    }

    /**
     * 初始化函数,尽可能早的调用此方法来使用本地已经存在的patch去替换存在bug的方法
     * @param appVersion App version
     */
    public void init(String appVersion) {
        //本地不存在patchdir或者没有创建成功就退出
        if (!mPatchDir.exists() && !mPatchDir.mkdirs()) {// make directory fail
            Log.e(TAG, "patch dir create error.");
            return;
        //如果patchdir不是一个目录就退出
        } else if (!mPatchDir.isDirectory()) {// not directory
            mPatchDir.delete();
            return;
        }
        SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
                Context.MODE_PRIVATE);
        //本地保存的版本号
        String ver = sp.getString(SP_VERSION, null);
        //如果本地保存的版本号和传入进来的版本号不一致就删除本地所有的patch文件
        if (ver == null || !ver.equalsIgnoreCase(appVersion)) {
            cleanPatch();
            //更新本地保存的版本号
            sp.edit().putString(SP_VERSION, appVersion).commit();
        } else {
            //初始化所有的patch文件
            initPatchs();
        }
    }
    /**
    *加载本地所有的patch文件
    */
    private void initPatchs() {
        File[] files = mPatchDir.listFiles();
        for (File file : files) {
            addPatch(file);
        }
    }

    /**
     * 往mPatchs集合中添加一个Patch对象,该patch对象封装了patch文件
     * @param file
     * @return patch
     */
    private Patch addPatch(File file) {
        Patch patch = null;
        //如果patch文件后缀是.apatch才处理
        if (file.getName().endsWith(SUFFIX)) {
            try {
                使用Patch对象封装该patch文件
                patch = new Patch(file);
                //将patch对象添加到mPatchs集合中
                mPatchs.add(patch);
            } catch (IOException e) {
                Log.e(TAG, "addPatch", e);
            }
        }
        return patch;
    }

    /**
    *删除本地所有的patch文件
    */
    private void cleanPatch() {
        File[] files = mPatchDir.listFiles();
        for (File file : files) {
            //删除本地所有缓存的patch文件
            mAndFixManager.removeOptFile(file);
            if (!FileUtil.deleteFile(file)) {
                Log.e(TAG, file.getName() + " delete error.");
            }
        }
    }

    /**
     * 添加一个新的patch文件,一般我们从服务器上下载了一个新的修复好的patch文件,需要调用此方法
     * @param path patch path
     * @throws IOException
     */
    public void addPatch(String path) throws IOException {
        File src = new File(path);
        //这里会将传进来的文件进行拷贝,放入file/apatch目录下面
        File dest = new File(mPatchDir, src.getName());
        //如果传进来的patch文件不存在抛出异常
        if(!src.exists()){
            throw new FileNotFoundException(path);
        }
        //如果本地已经存在了就退出,在上面的init()方法中会自动处理本地已经存在的patch文件
        if (dest.exists()) {
            Log.d(TAG, "patch [" + path + "] has be loaded.");
            return;
        }
        //文件拷贝
        FileUtil.copyFile(src, dest);// copy to patch's directory
        //创建一个patch对象来封装传进来的文件
        Patch patch = addPatch(dest);
        if (patch != null) {
            //加载patch文件
            loadPatch(patch);
        }
    }

    /**
     * 清除本地所有的patch文件
     */
    public void removeAllPatch() {
        cleanPatch();
        SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
                Context.MODE_PRIVATE);
        sp.edit().clear().commit();
    }

    /**
     * 加载本地patch中执行的patchName那一部分的类,可以使用默认的classloader,也可以使用自定义的classloader
     *这里的patchName不是指那个patch的文件名称,至于这里的patchName是什么,等下在说Patch这个类的时候说
     * @param patchName patch name
     * @param classLoader classloader
     */
    public void loadPatch(String patchName, ClassLoader classLoader) {
        //将传进来的classloader放入mLoaders集合中
        mLoaders.put(patchName, classLoader);
        Set<String> patchNames;
        List<String> classes;
        //遍历本地所有的patch集合,找到每个patch中与指定patchName匹配的那部分类
        for (Patch patch : mPatchs) {
            patchNames = patch.getPatchNames();
            if (patchNames.contains(patchName)) {
                classes = patch.getClasses(patchName);
                //调用AndFixManager对有问题的类进行修复
                mAndFixManager.fix(patch.getFile(), classLoader, classes);
            }
        }
    }

    /**
     * 加载本地所有的patch进行修复
     */
    public void loadPatch() {
        mLoaders.put("*", mContext.getClassLoader());// wildcard
        Set<String> patchNames;
        List<String> classes;
        for (Patch patch : mPatchs) {
            patchNames = patch.getPatchNames();
            for (String patchName : patchNames) {
                classes = patch.getClasses(patchName);
                //所有的修复工作都是调用AndFixManager的fix()方法来进行修复的
                mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(),
                        classes);
            }
        }
    }

    /**
     * 加载一个特定的patch,在调用addPatch(String file)方法后会调用此方法来加载patch文件进行bug修复
     * @param patch
     *            patch
     */
    private void loadPatch(Patch patch) {
        //patch对象保存了要修复的类名,patchNames则是patch中保存了要修复的类集合的key
        Set<String> patchNames = patch.getPatchNames();
        ClassLoader cl;
        List<String> classes;
        for (String patchName : patchNames) {
            //只要调用了loadPatch()这个无参方法都使用的是系统默认的类加载器
            if (mLoaders.containsKey("*")) {
                cl = mContext.getClassLoader();
            } else {
                //如果调用了有参的loadPatch才有可能走到这里来
                cl = mLoaders.get(patchName);
            }
            if (cl != null) {
                //要修复的类的类名
                classes = patch.getClasses(patchName);
                //调用AndFixManager进行修复
                mAndFixManager.fix(patch.getFile(), cl, classes);
            }
        }
    }
}

上面遗留了一个问题就是patchName到底是什么鬼,在上面看了PATCH-MF这个文件的内容,实际上Patch这个类就是封装了PATCH-MF中的信息。下面就来看看Patch的源码,看了它就清楚这个PatchName了。

public class Patch implements Comparable<Patch> {
    //这几个字段实际上就是PATCH-MF文件中的几个属性名称
    private static final String ENTRY_NAME = "META-INF/PATCH.MF";
    private static final String CLASSES = "-Classes";
    private static final String PATCH_CLASSES = "Patch-Classes";
    private static final String CREATED_TIME = "Created-Time";
    private static final String PATCH_NAME = "Patch-Name";

    //patch文件
    private final File mFile;
    //对应Patch-Name这个字段信息
    private String mName;
    //对应Created-Time这个字段信息
    private Date mTime;
    //这里需要讲清楚,实际上一个patch文件中需要修复的类可能会有多个,所以是一个list集合
    //在PATCH-MF这个文件中保存了要修复的类,而保存它的字段就是以`"-Classes"`结尾的字段,比如这里的Patch-Classes就保存了要修复的类
    //当然还有其他的字段也可能会保存要修复的类,比如Name-Classes等,除了Patch-Classes,其他以`-Classes`结尾的字段我还没有看到过
    //所以这里需要一个Map集合,value的类型是一个List集合
    private Map<String, List<String>> mClassesMap;

    public Patch(File file) throws IOException {
        mFile = file;
        init();
    }

    /**
    *初始化,加载patch文件中的PATCH-MF内容
    */
    @SuppressWarnings("deprecation")
    private void init() throws IOException {
        JarFile jarFile = null;
        InputStream inputStream = null;
        try {
            //使用JarFile来加载apatch文件,apatch就是一个压缩文件
            jarFile = new JarFile(mFile);
            //获取apatch中的PATCH-MF文件
            JarEntry entry = jarFile.getJarEntry(ENTRY_NAME);
            //得到PATCH-MF文件的输入流
            inputStream = jarFile.getInputStream(entry);
            //使用Manifest解析PATCH-MF
            Manifest manifest = new Manifest(inputStream);
            //得到所有的属性
            Attributes main = manifest.getMainAttributes();
            //获取Patch-Name这个属性
            mName = main.getValue(PATCH_NAME);
            //patch文件的生成时间
            mTime = new Date(main.getValue(CREATED_TIME));

            mClassesMap = new HashMap<String, List<String>>();
            Attributes.Name attrName;
            String name;
            List<String> strings;
            //迭代所有的属性
            for (Iterator<?> it = main.keySet().iterator(); it.hasNext();) {
                attrName = (Attributes.Name) it.next();
                //属性的key
                name = attrName.toString();
                if (name.endsWith(CLASSES)) {
                    //属性的value,就是需要被修复的类,以","隔开
                    strings = Arrays.asList(main.getValue(attrName).split(","));
                    //如果属性的名称为Patch-Classes
                    if (name.equalsIgnoreCase(PATCH_CLASSES)) {
                        mClassesMap.put(mName, strings);
                    } else {
                        //如果属性的名称为xxx-Classes
                        mClassesMap.put(
                                //这里去掉了后面的"-Classes"
                                name.trim().substring(0, name.length() - 8),// remove "-Classes"
                                strings);
                    }
                }
            }
        } finally {
            if (jarFile != null) {
                jarFile.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        }

    }
    //返回Patch-Name这个属性值
    public String getName() {
        return mName;
    }

    public File getFile() {
        return mFile;
    }
    public Set<String> getPatchNames() {
        return mClassesMap.keySet();
    }

    public List<String> getClasses(String patchName) {
        return mClassesMap.get(patchName);
    }

    public Date getTime() {
        return mTime;
    }

    @Override
    public int compareTo(Patch another) {
        return mTime.compareTo(another.getTime());
    }
}

知道了PatchManager和Patch,继续往下走看看是如何实现的,先看AndFixManager这个类

public class AndFixManager {
    private static final String TAG = "AndFixManager";
    //patch文件的解压缓存路径,DexFile类需要用到该目录
    private static final String DIR = "apatch_opt";

    private final Context mContext;

    //保存被修复的类
    private static Map<String, Class<?>> mFixedClass = new ConcurrentHashMap<String, Class<?>>();

    //是否支持AndFix框架
    private boolean mSupport = false;

    //patch文件的签名校验
    private SecurityChecker mSecurityChecker;

    //缓存目录
    private File mOptDir;

    public AndFixManager(Context context) {
        mContext = context;
        //判断是否支持Andfix
        mSupport = Compat.isSupport();
        if (mSupport) {
            //初始化签名校验工具
            mSecurityChecker = new SecurityChecker(mContext);
            mOptDir = new File(mContext.getFilesDir(), DIR);
            //如果无法创建apatch_opt目录则不支持
            if (!mOptDir.exists() && !mOptDir.mkdirs()) {// make directory fail
                mSupport = false;
                Log.e(TAG, "opt dir create error.");
            //如果apatch_opt压根就不是目录则不支持
            } else if (!mOptDir.isDirectory()) {// not directory
                mOptDir.delete();
                mSupport = false;
            }
        }
    }

    /**
     * 清除一个patch
     * @param file patch file
     */
    public synchronized void removeOptFile(File file) {
        File optfile = new File(mOptDir, file.getName());
        if (optfile.exists() && !optfile.delete()) {
            Log.e(TAG, optfile.getName() + " delete error.");
        }
    }

    /**
     * 指定一个patch文件进行修复
     * @param patchPath patch path
     */
    public synchronized void fix(String patchPath) {
        fix(new File(patchPath), mContext.getClassLoader(), null);
    }

    /**
     * 根据指定的patch文件进行修复,classes中包含了要修复的类,如果为null则会遍历patch中dex文件的所有类进行修复
     * 
     * @param file patch file要修复的patch文件
     * @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) {
        //如果不支持则退出
        if (!mSupport) {
            return;
        }
        //如果签名不一致则退出,patch的签名需要和apk的签名一致
        if (!mSecurityChecker.verifyApk(file)) {// security check fail
            return;
        }

        try {
            File optfile = new File(mOptDir, file.getName());
            boolean saveFingerprint = true;
            if (optfile.exists()) {
                //如果文件已经存在需要校验MD5值,判断文件是否被修改防止被攻击
                if (mSecurityChecker.verifyOpt(optfile)) {
                    //指纹信息没有变化,不需要保存
                    saveFingerprint = false;
                } else if (!optfile.delete()) {
                    return;
                }
            }
            //使用DexFile加载patch文件
            final DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),
                    optfile.getAbsolutePath(), Context.MODE_PRIVATE);

            if (saveFingerprint) {
                //保存指纹信息
                mSecurityChecker.saveOptSig(optfile);
            }
            //自定义一个classloader来加载patch中的类,不能使用系统默认的classloader,否则会加载原来那个有问题的类,而且系统的classloader也找不到这个patch中的类,需要自定义类加载器
            ClassLoader patchClassLoader = new ClassLoader(classLoader) {
                @Override
                protected 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;
                }
            };
            //dex文件中所有的类文件
            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);
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "pacth", e);
        }
    }

    /**
     * 修复有问题的类
     * @param clazz class
     * @param classloader 该classloader用于加载有问题的类,不是上面自定义的那个classloader
     */
    private void fixClass(Class<?> clazz, ClassLoader classLoader) {
        //得到patch类中所有的方法
        Method[] methods = clazz.getDeclaredMethods();
        //被修复的方法上有一个MethodReplace的注解
        MethodReplace methodReplace;
        String clz;
        String meth;
        for (Method method : methods) {
            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);
            }
        }
    }

    /**
     * 在native中替换mathod
     * @param classLoader classloader 用于加载有问题的类
     * @param clz class 需要修复的类的名称
     * @param meth name of target method  需要修复的类的中的方法名称
     * @param method source method 修复好的方法对象
     */
    private void replaceMethod(ClassLoader classLoader, String clz,
            String meth, Method method) {
        try {
            String key = clz + "@" + classLoader.toString();
            Class<?> clazz = mFixedClass.get(key);
            if (clazz == null) {// class not load
                //找到有问题的class
                Class<?> clzz = classLoader.loadClass(clz);
                clazz = AndFix.initTargetClass(clzz);
            }
            if (clazz != null) {
                //将有问题的class放入map集合中
                mFixedClass.put(key, clazz);
                //找到有bug的method
                Method src = clazz.getDeclaredMethod(meth,
                        method.getParameterTypes());
                //开始替换
                AndFix.addReplaceMethod(src, method);
            }
        } catch (Exception e) {
            Log.e(TAG, "replaceMethod", e);
        }
    }

    private static boolean isEmpty(String string) {
        return string == null || string.length() <= 0;
    }
}

再看AndFix这个类,发现它调用的都是native方法

public class AndFix {
    private static final String TAG = "AndFix";

    static {
        try {
            //加载so库,替换method方法在java层是没法完成的,只能从native中来入手
            Runtime.getRuntime().loadLibrary("andfix");
        } catch (Throwable e) {
            Log.e(TAG, "loadLibrary", e);
        }
    }

    private static native boolean setup(boolean isArt, int apilevel);

    private static native void replaceMethod(Method dest, Method src);

    private static native void setFieldFlag(Field field);

    /**
     * 调用native方法进行method方法修复
     * @param src source method
     * @param dest target method
     */
    public static void addReplaceMethod(Method src, Method dest) {
        try {
            replaceMethod(src, dest);
            initFields(dest.getDeclaringClass());
        } catch (Throwable e) {
            Log.e(TAG, "addReplaceMethod", e);
        }
    }

    /**
     * initialize the target class, and modify access flag of class’ fields to
     * public
     * 
     * @param clazz
     *            target class
     * @return initialized class
     */
    public static Class<?> initTargetClass(Class<?> clazz) {
        try {
            Class<?> targetClazz = Class.forName(clazz.getName(), true,
                    clazz.getClassLoader());

            initFields(targetClazz);
            return targetClazz;
        } catch (Exception e) {
            Log.e(TAG, "initTargetClass", e);
        }
        return null;
    }

    /**
     * 设置属性字段的accessable为true,即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);
        }
    }

    /**
     * 判断是否支持Andfix
     * @return true if initialize success
     */
    public static boolean setup() {
        try {
            //虚拟机的版本
            final String vmVersion = System.getProperty("java.vm.version");
            //如果版本号以2开头则是ART虚拟机
            boolean isArt = vmVersion != null && vmVersion.startsWith("2");
            //当前的系统版本
            int apilevel = Build.VERSION.SDK_INT;
            return setup(isArt, apilevel);
        } catch (Exception e) {
            Log.e(TAG, "setup", e);
            return false;
        }
    }
}

分析native的实现

AndFix的native是用C++实现的,不过代码并不多。

#include <jni.h>
#include <stdio.h>
#include <cassert>
#include "common.h"//这个头文件中只宏定义了LOGD LOGW LOGE这3个方法用于往logcat中打印日志
//AndFix这个类的路径
#define JNIREG_CLASS "com/alipay/euler/andfix/AndFix"

//Android中在4.4之前的虚拟机是dalvik,之后是art,不同的虚拟机,以及不同版本之间,替换方法都有差异
//dalvik
extern jboolean dalvik_setup(JNIEnv* env, int apilevel);
extern void dalvik_replaceMethod(JNIEnv* env, jobject src, jobject dest);
extern void dalvik_setFieldFlag(JNIEnv* env, jobject field);
//art
extern jboolean art_setup(JNIEnv* env, int apilevel);
extern void art_replaceMethod(JNIEnv* env, jobject src, jobject dest);
extern void art_setFieldFlag(JNIEnv* env, jobject field);
//是否是art虚拟机
static bool isArt;

/*
*该方法被AndFix调用,虚拟机加载动态库的时候会动态注册该方法
*/
static jboolean setup(JNIEnv* env, jclass clazz, jboolean isart,
        jint apilevel) {
    isArt = isart;
    LOGD("vm is: %s , apilevel is: %i", (isArt ? "art" : "dalvik"),
            (int )apilevel);
    //这里初始化需要对不同虚拟机和不同版本号分开处理
    if (isArt) {
        return art_setup(env, (int) apilevel);
    } else {
        return dalvik_setup(env, (int) apilevel);
    }
}

/*
*替换方法
*/
static void replaceMethod(JNIEnv* env, jclass clazz, jobject src,
        jobject dest) {
    //ART虚拟机和DALVIK虚拟机替换方法也是有点差别
    if (isArt) {
        art_replaceMethod(env, src, dest);
    } else {
        dalvik_replaceMethod(env, src, dest);
    }
}
//这里设置了属性字段accessable为true
static void setFieldFlag(JNIEnv* env, jclass clazz, jobject field) {
    if (isArt) {
        art_setFieldFlag(env, field);
    } else {
        dalvik_setFieldFlag(env, field);
    }
}
/*
 * 定义了一组native方法以供java层调用,它们在虚拟机加载动态库的时候被动态的注册进虚拟机中
 * 它是一个结构体数组,结构体每个参数分别为方法名称,方法签名信息和函数指针
 * 如果这里不明白的最好先了解一些jni的相关知识
 */
static JNINativeMethod gMethods[] = {
{ "setup", "(ZI)Z", (void*) setup }, 
{ "replaceMethod","(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V",(void*) replaceMethod }, 
{ "setFieldFlag","(Ljava/lang/reflect/Field;)V", (void*) setFieldFlag }, };

/*
 * 动态注册方法,这里会判断AndFix这个类是否存在
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods) {
    jclass clazz;
    //先找到AndFix这个类是否存在,如果不存在后续啥也干不了
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    //注册方法
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

/*
 * Register native methods for all classes we know about.
 */
static int registerNatives(JNIEnv* env) {
    if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
            sizeof(gMethods) / sizeof(gMethods[0])))
        return JNI_FALSE;

    return JNI_TRUE;
}

/*
 * 在加载动态so库的时候就会调用此方法,可以在该方法中做一些初始化
 *
 * Returns the JNI version on success, -1 on failure.
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    jint result = -1;
    //获取JNIEnv
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);
    //动态注册需要提供给java层调用的方法
    if (!registerNatives(env)) { //注册
        return -1;
    }
    /* success -- return valid version number */
    result = JNI_VERSION_1_4;

    return result;
}

接着来看真正替换方法的地方,这里以Dalvik为例,ART的每个版本都有差异。dalvik.h中主要定义了一堆结构体和几个变量,先看看这几个变量,其他的就不细讲。

//定义了2个函数指针,就是通过这两个方法来进行method的替换的,
//所以这里定义了2个函数指针,它们需要通过dlsym()函数来获取
typedef Object* (*dvmDecodeIndirectRef_func)(void* self, jobject jobj);
typedef void* (*dvmThreadSelf_func)();

dvmDecodeIndirectRef_func dvmDecodeIndirectRef_fnPtr;
dvmThreadSelf_func dvmThreadSelf_fnPtr;

jmethodID jClassMethod;

我们主要还是看看dalvik_method_replace.cpp这个源文件

//dalvik_method_replace.cpp
#include <time.h>
#include <stdlib.h>
#include <stddef.h>
#include <assert.h>

#include <stdbool.h>
#include <fcntl.h>
#include <dlfcn.h>

#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "dalvik.h"
#include "common.h"

/*
*该函数用于获取指定name的函数地址,实际上调用的是dlsysm()这个方法来获取
* @param hand 动态链接库的句柄
* @param name 函数名称
*/
static void* dvm_dlsym(void *hand, const char *name) {
    void* ret = dlsym(hand, name);
    char msg[1024] = { 0 };
    snprintf(msg, sizeof(msg) - 1, "0x%x", ret);
    LOGD("%s = %s\n", name, msg);
    return ret;
}

//初始化,主要是获取了两个函数指针
extern jboolean __attribute__ ((visibility ("hidden"))) dalvik_setup(
        JNIEnv* env, int apilevel) {
    //打开libvm.so这个库文件
    void* dvm_hand = dlopen("libdvm.so", RTLD_NOW);
    if (dvm_hand) {
        //获取_Z20dvmDecodeIndirectRefP6ThreadP8_jobject或者dvmDecodeIndirectRef这个函数的函数指针
        dvmDecodeIndirectRef_fnPtr = dvm_dlsym(dvm_hand,
                apilevel > 10 ?
                        "_Z20dvmDecodeIndirectRefP6ThreadP8_jobject" :
                        "dvmDecodeIndirectRef");
        //如果没有获取到返回false
        if (!dvmDecodeIndirectRef_fnPtr) {
            return JNI_FALSE;
        }
        //获取_Z13dvmThreadSelfv或者dvmThreadSelf这两个函数的函数指针
        dvmThreadSelf_fnPtr = dvm_dlsym(dvm_hand,
                apilevel > 10 ? "_Z13dvmThreadSelfv" : "dvmThreadSelf");
        if (!dvmThreadSelf_fnPtr) {
            return JNI_FALSE;
        }
        //获取getDeclaringClass这个方法的id,这里会调用java层的代码
        jclass clazz = env->FindClass("java/lang/reflect/Method");
        jClassMethod = env->GetMethodID(clazz, "getDeclaringClass",
                        "()Ljava/lang/Class;");

        return JNI_TRUE;
    } else {
        return JNI_FALSE;
    }
}

//替换方法
extern void __attribute__ ((visibility ("hidden"))) dalvik_replaceMethod(
        JNIEnv* env, jobject src, jobject dest) {
    //这里调用了java中Method这个类的方法,getDeclaringClass
    jobject clazz = env->CallObjectMethod(dest, jClassMethod);
    ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(
            dvmThreadSelf_fnPtr(), clazz);
    clz->status = CLASS_INITIALIZED;
    //获取java中传进来的src和dest的函数指针,就是修复前和修复后的Method
    Method* meth = (Method*) env->FromReflectedMethod(src);
    Method* target = (Method*) env->FromReflectedMethod(dest);
    LOGD("dalvikMethod: %s", meth->name);

    //这里就开始替换method中的数据了,这样就完成了bug方法的修复
    meth->accessFlags |= ACC_PUBLIC;
    meth->methodIndex = target->methodIndex;
    meth->jniArgInfo = target->jniArgInfo;
    meth->registersSize = target->registersSize;
    meth->outsSize = target->outsSize;
    meth->insSize = target->insSize;

    meth->prototype = target->prototype;
    meth->insns = target->insns;
    meth->nativeFunc = target->nativeFunc;
}

extern void dalvik_setFieldFlag(JNIEnv* env, jobject field) {
    Field* dalvikField = (Field*) env->FromReflectedField(field);
    dalvikField->accessFlags = dalvikField->accessFlags & (~ACC_PRIVATE)
            | ACC_PUBLIC;
    LOGD("dalvik_setFieldFlag: %d ", dalvikField->accessFlags);
}

ART的实现和Dalvik类似,都是替换Method中的内容,只是它们替换的内容有差别而已,这里就不说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值