android热补丁初探

android热补丁初探----HotFixDemo

1.先来个代码结构

2.热补丁的文章有好多了,我这里就不赘述了。

来两个链接

QQ空间 的链接

https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a&scene=1&srcid=1106Imu9ZgwybID13e7y2nEi#wechat_redirect

这还有另一片相关的文章

http://blog.csdn.net/lmj623565791/article/details/49883661

理论的学习还是要很多的,不然各种搞不懂,不知道都是什么东西,大概有哪些步骤也是不清楚的。

hotfix github地址:

https://github.com/dodola/HotFix

3.这里大概总结下有哪些主要的步骤:

1.android编译生成apk后,apk解压后会有一个classes.dex 文件,android系统在加载这个dex文件时,是可以加载多个dex文件的。

2.我们可以再编译一个dex文件传到用户正在使用的app中,

3.加载到APP指定的目录,/data/data/包名/app_文件夹名/

4.然后将这个dex也加载到系统中,执行最新的dex中的class代码,把最原始的类覆盖掉。


4.其中比较关键的技术点

1.加载dex的时候,加载了多个是有加载顺序的,需要把后传上去的补丁包先去查找其中的类。

如: 原始dex  中的  Bug.class

         补丁dex 中的 Bug.class

这两个需要设置成加载补丁包中Bug.class先加载


2.android在编译应用的时候,默认是只加载原始dex的,如果dex中有对应的类Bug.class  就会在这个类上打个标志CLASS_ISPREVERIFIED,

Bug.class  被打上这个标志后,这个类就不会在去找其他的dex中的Bug.class了(编译系统优化产生的这个标志)。

怎么样才能不打这个标志呢?

 hotfix中采用的做法是:在类加载的时候,在类的构造方法中加载另外一个dex文件。就是demo中的hackdex_dex.jar,这个实际是个dex文件。

这个类在应用初始化的时候Application中执行加载这个dex.,在对应的其他所有类中的构造方法中都加载这个中间dex其中的一个类即可,不用做事情。

就是为了消除这个CLASS_ISPREVERIFIED标志,这样就不打标了。

注意:打补丁这里,有三个dex文件,

第一个是原始apk中带的classes.dex文件,

第二个是这个解除打标用的dex文件,这个dex文件中,有一个空的类的就ok。

第三个就是补丁的dex文件。

此步骤的实现相对麻烦些:

要在gradle中处理一些东西,即在编译apk的时候要将所有要打补丁的类的构造方法中打上一句加载第二个dex文件的话

例如:System.out.print(SecondDexClass.class);

注意:这句话不能在系统初始化的自定义Application中执行。(为什么这里不说了,去其他的博客看一下吧,要有理论支撑)

所以这要对gradle进行编程,hotfix demo中已经有写了,可以参考。

3.就是从服务器或者手机sd卡的某个位置获得补丁dex文件,然后执行加载。


理论就大概介绍到这里。

这个是代码地址:

http://download.csdn.net/detail/methods2011/9444827


下面对代码进行简单的介绍

1.首先要有bug类,打印的bug class000就是需要补丁 改变的

public class BugClass {
    public String bug() {
        return "bug class000";
    }
}
2.然后系统初始化的Application,

这里进行加载了第二个dex文件

dodola.hackdex.AntilazyLoad
这个类为第二个dex中的唯一的一个空类

public class HotfixApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "hackdex_dex.jar");
        Utils.prepareDex(this.getApplicationContext(), dexPath, "hackdex_dex.jar");
        HotFix.patch(this, dexPath.getAbsolutePath(), "dodola.hackdex.AntilazyLoad");
        try {
            this.getClassLoader().loadClass("dodola.hackdex.AntilazyLoad");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

 

3.这个是在Activity中执行的代码片段

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
    //准备补丁,assert里拷贝到dex    File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "modify_dex.jar");
    Utils.prepareDex(this.getApplicationContext(), dexPath, "modify_dex.jar");
    //                DexInjector.inject(dexPath.getAbsolutePath(), defaultDexOptPath, "dodola.hotfix
    // .BugClass");
    HotFix.patch(this, dexPath.getAbsolutePath(), "com.method.hotfixdemo_01.BugClass");
    return true;
}
if (id == R.id.action_test) {
    LoadBugClass bugClass = new LoadBugClass();
    Toast.makeText(this, "测试调用方法:" + bugClass.getBugString(), Toast.LENGTH_SHORT).show();
    return true;
}

modify_dex.jar是加载的补丁dex文件其中只有一个bugClass文件,其中反编译的



如果补丁成功的话:会toast 这个。。。

4.我把另外两个dex(非原始apk带的)放到了assets中。



 

5.有一个groovy模块,用于gradle编译补丁类用的。

build.gradle中的设置是这样的

apply plugin: 'groovy'

repositories {
    mavenCentral()
}

dependencies {
    compile gradleApi()
    compile localGroovy()
    compile 'org.javassist:javassist:3.18.+'
}

注意最上面一行的这个:apply plugin: 'groovy'

这个下来大致就是可以跑的有bug的app了。


然后是补丁的打包的过程。

打完包的名称是:

modify_dex.jar
实际是一个dex文件

1:将BugClass类修复下


然后找到对应编译后的BugClass.class,要反编译下看看这个文件更新了没,


之后新建一个文件夹,将这个类对应的包名都弄对,然后将这个BugClass.class文件放入其中。


modify.jar就是 用命令行执行   jar -cvf modify.jar *

modify_dex.jar 使用android\sdk\build-tools\23.0.2\dx.bat  文件进行编译的

dx --dex --output=modify_dex.jar modify.jar

之后这个dex就可以最为补丁送到正在使用的app中了,代码自行补脑了。

然后就是代码中的执行这个:

File dexPath = new File(getDir("dex", Context.MODE_PRIVATE), "modify_dex.jar");
Utils.prepareDex(this.getApplicationContext(), dexPath, "modify_dex.jar");
//                DexInjector.inject(dexPath.getAbsolutePath(), defaultDexOptPath, "dodola.hotfix
// .BugClass");
HotFix.patch(this, dexPath.getAbsolutePath(), "com.method.hotfixdemo_01.BugClass");
然后这个bug就被解决了。

ok!



说明: =================================================================== 1). dex 转 jar 用的 enjarify,比目前常用的 dex2jar(d2j) 要稳定可靠得多,尤其是在处理重度混淆过的apk时 2). 用于对apk进行代码修改,扫描目标apk中函数,并在指定函数的开头部分 添加 调用自定义静态函数的代码 3). 省略 jar 转 smali 再回转的步骤,转而使用 objectweb.asm(ow2) 直接对 jar 文件进行 smali 注入 4). dex2jar 的步骤使用的是 google 自家的 enjarify 工具,没使用老掉牙的、对部分混淆apk处理极不准确极不稳定的 dex2jar(d2j) 5). jar2dex 使用的是 android studio 自带的 dx.bat 工具,貌似 dex2jar(d2j) 在做jar回转的时候也是调用dx.jar 6). 手机无需 root 要求: =================================================================== 已安装 jdk1.7 或 jdk1.8 已安装 Android Studio 已安装 pypy3 或者 python,pypy3 下载地址:http://download.csdn.net/detail/jizhitp/9902691 步骤: =================================================================== 1). 使用 jeb、jadx、jd 等静态分析工具分析源apk,确定要注入代码的类及函数 2). 用记事本打开 main.bat,设置正确的路径,以及源apk文件名 3). 在当前文件夹空白处点一下,然后按 shift + 右击,选择 "在此处打开命令行窗口" 4). 编辑静态类源文件:SmaliInjector\android\StaticClass.java,这个类将会被包进目标apk中 5). 编辑注入器源文件:SmaliInjector\pc\Injector.java,这是注入器关键文件,作用是调用ow2.asm来对目标jar中特定类的特定函数进行代码注入 6). 运行 main.bat即可 错误处理: =================================================================== 1). 此工具默认使用pypy来运行py脚本,若想使用 python 请打开 google_enjarify\enjarify.bat 把 pypy 改成 python 2). 安装重包后的apk时,若出现 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES 错误,请将手机中原有的应用卸载后再安装 3). android.jar 最好选用跟目标手机版本一致的,否则重包后的apk几乎100%闪退 4). 若在 jar 转加 dex 的过程中出现 java.lang.OutOfMemoryError: GC overhead limit exceeded 的错误,请调大 main.bat 中的 -Xmx 和 -Xss 其它: =================================================================== 1). pypy3 比 python 快蛮多,推荐使用 2). enjarify 有两种模式: 默认的模式,转换出来的代码阅读性比较好,但比较慢 --fast 快速模式 3). main.bat 内有两个便捷开关,赋值表示启用,参数留空表示开关不启用 Resign_Only 是否只做 重签名 的动作,可用于验证目标 apk 有没有做签名保护 SkipDex2jar 是否跳过 dex 转 jar 的步骤,转换很耗时,第二次调用 main.bat 时可以选择跳过,能省蛮多时间
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

method_chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值