前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >腾讯hotfix分析

腾讯hotfix分析

作者头像
用户2930595
发布2019-02-22 15:18:08
7570
发布2019-02-22 15:18:08
举报

在逆向腾讯某产品时,发现很多方法的前面都有这么一段代码:

代码语言:javascript
复制
 if(NotDoVerifyClasses.DO_VERIFY_CLASSES) {
     System.out.print(AntiLazyLoad.class);
 }

NotDoVerifyClasses和AntiLazyLoad在dex中居然找不到,这勾起我的兴趣。先来debug看看,到底是从哪里加载的这两个类:

ok,发现是从files下的verify.jar加载的,找到这个jar发编译一下:

代码语言:javascript
复制
package com.tencent;

public class AntiLazyLoad {
    public AntiLazyLoad() {
        super();
    }
}
代码语言:javascript
复制
package com.tencent;

public class NotDoVerifyClasses {
    public static boolean DO_VERIFY_CLASSES;

    static {
        NotDoVerifyClasses.DO_VERIFY_CLASSES = false;
    }

    public NotDoVerifyClasses() {
        super();
    }
}

代码很简单,感觉像是个桩,但这个有什么用?

从文件路径我们知道有hotfix,应该和热修复有关,深入研究一下。

我们先来看看另外一个问题:在前面的截图中,我们看到classloader是PathClassLoader,其pathList中居然被增加一个files目录下的jar,这个是怎么做到的?一般来说动态加载应该是DexClassLoader才对。

通过搜索verify.jar,发现了关键代码:

代码语言:javascript
复制
public static boolean inject(Context ctx, String dexPath, String arg5, String odexPath, String clazzName, boolean arg8) {
    boolean v0 = false;
    if(dexPath != null && (new File(dexPath).exists())) {
        if(DexUtil.isAliyunOs()) {
            try {
                DexUtil.injectInAliyunOs(ctx, dexPath, arg5, odexPath, clazzName, arg8);
                v0 = true;
            }
            catch(Throwable v0_1) {
                PatchLog.e("SystemClassLoaderInjector", "fail to inject", v0_1);
                throw v0_1;
            }
        }
        else if(!DexUtil.hasBaseDexClassLoader()) {
            try {
                DexUtil.injectBelowApiLevel14(ctx, dexPath, arg5, odexPath, clazzName, arg8);
                v0 = true;
            }
            catch(Throwable v0_1) {
                PatchLog.e("SystemClassLoaderInjector", "fail to inject", v0_1);
                throw v0_1;
            }
        }
        else {
            try {
                DexUtil.injectAboveEqualApiLevel14(ctx, dexPath, arg5, odexPath, clazzName, arg8);
                v0 = true;
            }
            catch(Throwable v0_1) {
                PatchLog.e("SystemClassLoaderInjector", "fail to inject", v0_1);
                throw v0_1;
            }
        }
    }

    return v0;
}

不同版本做了不同的Classloader注入方案,我是4.4的手机,对应看看injectAboveEqualApiLevel14

代码语言:javascript
复制
private static void injectAboveEqualApiLevel14(Context ctx, String dexPath, String libPath, String odexPath, String arg9, boolean arg10) {
    ClassLoader oldClassloader = ctx.getClassLoader();
    DexClassLoader dexClassLoader = new DexClassLoader(dexPath, odexPath, libPath, ctx.getClassLoader());
    Object v2 = DexUtil.combineArray(DexUtil.getDexElements(DexUtil.getPathList(oldClassloader)), 
            DexUtil.getDexElements(DexUtil.getPathList(dexClassLoader)), arg10);
    Object v0_1 = DexUtil.getPathList(oldClassloader);
    DexUtil.setField(v0_1, v0_1.getClass(), "dexElements", v2);
    if(!TextUtils.isEmpty(((CharSequence)arg9))) {
        dexClassLoader.loadClass(arg9);
        PatchLog.e("DexUtil", "load clas " + arg9 + " success ");
    }
}

原理弄明白了:先通过DexClassLoader加载files下的jar,然后反射获取其dexElements,然后合并到PathClassLoader的dexElements中,很巧妙的做法,其他注入方案不再详述。

上述这个过程在Application的attachBaseContext中就完成,即app一运行就会加载。也就是说,加入某个时候更新了files下的verify.jar,在下次启动app时,修改后的verify.jar代码就会被加载。 前面最开始看到有很多方法执行前就调用的桩,如果精心去设计NotDoVerifyClasses和AntiLazyLoad的代码,在方法体执行前就能被执行,确实可以做到热修复。

然而,其hotfix目录下不仅仅是上面的代码,上面也没有讲述其是如何从服务器端拉取热修复的代码的, hotfix下代码如下:

代码有点多,我耐着性子看完了,截图中的这一坨代码是为了做补丁管理的,其中就包括从服务端去拉取补丁,然后保存到files下的hotfix目录。假如服务端返回的补丁名字叫做verify,那么就会覆盖verify.jar,我想腾讯他们在推送热修复补丁时,肯定是推送这样一个补丁。

我记得阿里之前开源了一个修改自xposed的热修复框架:dexposed,其利用xposed的hook原理,可以做到hook任何一个java方法,效果也是不错的,但xposed因为很多适配问题,所以要产品化效果不太好。

腾讯的这种热修复方案是通过硬编码方式植入桩,假如植入方式能够工具化,也是一个不错的选择,至少没有适配问题。

关于上面的热修复原理,腾讯有官方介绍,发现自己之前有一些理解不到位的地方,原理参考: https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a&scene=1&srcid=1106Imu9ZgwybID13e7y2nEi#wechat_redirect

搜索了一下热修复,原来是2015年很火的一个技术(孤陋寡闻了),现在已经有很多成熟的方案,上述的hotfix只是其中之一,想了解更多可以自行去搜索。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.09.01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档