基于cydia Hook在线热修复补丁方案

原创 2015年11月12日 17:18:14

最近的在线热补丁修复的讨论相当激烈,从Xopsed到Dexposed,再到AndFix,再到QQ空间团队的Class补丁。可谓是各有特色。本文讨论的是基于Cydia Hook实现的在线Class热补丁。相对于Xopsed、Dexposed、AndFix这三种都是替换Java方法,和QQ空间class替换而言,优势明显。前者是替换方法,但是如果所替换的方法中遇到calss中的成员变量,就要通过反射得到,实现补丁方案。这相对于Class补丁来说解决性能问题,但是使用起来比较麻烦。而QQ空间团队使用的是替换整个Class,这就没有成员变量的问题。但是QQ空间的方案却牺牲了性能,主要是通过防止class打上CLASS_ISPREVERIFIED。本文所讨论的方案是基于两者实现的,没有成员变量反射得到麻烦,也没有防止class打上CLASS_ISPREVERIFIED牺牲性能的问题。那这是怎么实现的呢?请看下文:

我采用的是MultiDex方案实现的,这部分和QQ空间类似就是讲补丁dex文件,放在其他dex(包括主dex),这是因为一个ClassLoader可以包含多个dex文件,每个dex文件是一个Element,多个dex文件排列成一个有序的数组dexElements,当找类的时候,会按顺序遍历dex文件,然后从当前遍历的dex文件中找类,如果找类则返回,如果找不到从下一个dex文件继续查找。具体代码:

public Class findClass(String name, List<Throwable>suppressed){
    for (Elementelement : dexElements) {  //每个Element就是一个dex文件
        DexFile dex = element.dexFile;
        if (dex != null) {
            Class clazz=dex.loadClassBinaryName(name, definingContext, suppressed);
            if (clazz!= null) {
                return clazz;
            }
        }
    }
    if (dexElementsSuppressedExceptions!= null){
       suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

 

我的方案中采用的是MultiDex,对其进行一部分改造,具体代码:

1、添加dex文件,并执行install

/**
* 添加apk包外的dex文件
* 自动执行install
* @param dexFile
*/
public static void addDexFileAutoInstall(Context context, List<File> dexFile,File optimizedDirectory) {
    if (dexFile != null && !dexFile.isEmpty() &&!dexFiles.contains(dexFile)) {
         dexFiles.addAll(dexFile);
         LogUtil.d(TAG, "add other dexfile");
         installDexFile(context,optimizedDirectory);
     }
}

2、installDexFile直接调用MultiDex 的installSecondaryDexes方法。

/**
 * 添加apk包外的dex文件,
 * @param context
 */
publicstatic void installDexFile(Context context, File optimizedDirectory){
    if (checkValidZipFiles(dexFiles)) {
        try {
            installSecondaryDexes(context.getClassLoader(), optimizedDirectory, dexFiles);
        } catch (IllegalAccessExceptione){
           e.printStackTrace();
        } catch (NoSuchFieldExceptione) {
           e.printStackTrace();
        } catch (InvocationTargetExceptione){
           e.printStackTrace();
        } catch (NoSuchMethodExceptione) {
           e.printStackTrace();
        } catch (IOExceptione) {
           e.printStackTrace();
        }
    }
}

3、将patch.dex放在所有dex最前面。

private static voidexpandFieldArray(Object instance, String fieldName, Object[]extraElements) throws NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
                    Field jlrField = findField(instance, fieldName);
                    Object[]original = (Object[]) jlrField.get(instance);
                    Object[]combined = (Object[]) Array.newInstance(original.getClass().getComponentType(),original.length + extraElements.length);
                    // 将后来的dex放在前面,主dex放在最后。
                  System.arraycopy(extraElements, 0, combined, 0, extraElements.length);
                  System.arraycopy(original, 0, combined, extraElements.length,original.length);
                   // 原始的dex合并,是将主dex放在前面,其他的dex依次放在后面。
                   //System.arraycopy(original, 0, combined, 0, original.length);
                   //System.arraycopy(extraElements, 0, combined, original.length,extraElements.length);
                  jlrField.set(instance, combined);
}
到此将patch.dex放进了Element,接下来的问题就是加载Class,当加载patch.dex中类的时候,会遇到一个问题,这个问题就是QQ空间团队遇到,Classd的CLASS_ISPREVERIFIED。具体原因是dvmResolveClass这个方法对Class进行了校验。判断这个要Resolve的class是否和其引用来自一个dex。如果不是,就会遇到问题。

 

 

当引用这和被引用者不在同一个dex中就会抛出异常,导致Resolve失败。QQ空间团队的方案是阻止所有的Class类打上CLASS_ISPREVERIFIED来逃过校验,这种方式其实是影响性能。

我们的方案是和QQ团队的类似,但是和QQ空间不同的是,我们将fromUnverifiedConstant设置为true,来逃过校验,达到补丁的路径。具体怎么实现呢?

要引用Cydia Hook技术来hook Native dalvik中dvmResolveClass这个方法。有关Cydia Hook技术请参考:

官网地址:http://www.cydiasubstrate.com/

官方教程:http://www.cydiasubstrate.com/id/38be592b-bda7-4dd2-b049-cec44ef7a73b

SDK下载地址:http://asdk.cydiasubstrate.com/zips/cydia_substrate-r2.zip

具体代码如下。

//指明要hook的lib :
MSConfig(MSFilterLibrary,"/system/lib/libdvm.so")

 

// 在初始化的时候进行hook

MSInitialize {
    LOGD("Cydia Init");
    MSImageRef image;
    //载入lib
    image = MSGetImageByName("/system/lib/libdvm.so");
    if (image != NULL) {
        LOGD("image is not null");
        void *dexload=MSFindSymbol(image,"dvmResolveClass");
        if(dexload != NULL) {
            LOGD("dexloadis not null");
            MSHookFunction(dexload, (void*)proxyDvmResolveClass, (void**)&dvmResolveClass_Proxy);
        } else{
            LOGD("errorfind dvmResolveClass");
        }
    }
}

// 在初始化的时候进行hook//保留原来的地址
ClassObject* (*dvmResolveClass_Proxy)(ClassObject* referrer, u4 classIdx, boolfromUnverifiedConstant);
// 新方法地址
static ClassObject* proxyDvmResolveClass(ClassObject* referrer, u4 classIdx,bool fromUnverifiedConstant) {
        return dvmResolveClass_Proxy(referrer, classIdx,true);
}

有人可能会担心cydia Hook性能,稳定性问题,但是据我所知,目前有些公司已经用它来实现apk加壳和脱壳防止反编译技术方案。具体可以参考http://www.gitzx.com/android-cydiasubstrate/

到这里为止在线热补丁修补方案就完了,由于本人技术原因,可能有些东西没有讲清楚,或者有什么纰漏,请告知。最后,这个方案我近期会整理一下开源出来。

github代码:https://github.com/Jarlene/ClassPatch.git

版权声明:本文为博主原创文章,未经博主允许不得转载。

某梆企业版加固脱壳及抽代码还原方法

某梆加固企业版还是会调用系统的dvmDexFileOpenPartial 接口,因此可以这里添加hook                          51df6008-52cd50__unpa...
  • justFWD
  • justFWD
  • 2016年04月15日 19:59
  • 10974

加载类需指定ClassLoader(默认当前ClassLoader加载)ClassObject* dvmResolveClass(ClassObject* referrer, u4 classIdx)

  • tuhuolong
  • tuhuolong
  • 2015年04月03日 16:03
  • 1084

Dalvik虚拟机为新创建对象分配内存的过程分析

在前面一文中,我们分析了Dalvik虚拟机创建Java堆的过程。有了Java堆之后,Dalvik虚拟机就可以在上面为对象分配内存了。在Java堆为对象分配内存需要解决内存碎片和内存不足两个问题。要解决...
  • Luoshengyang
  • Luoshengyang
  • 2014年12月08日 01:00
  • 19353

Dalvik 分析 - Class加载篇

Java 源代码经过编译后会生成后缀为class的文件,也即字节码文件。然后在Android中使用dx工具将其转换为后缀为jar 的dex类型文件。Dalvik 虚拟机负责解释并执行编译后的字节码。在...
  • VirtualPower
  • VirtualPower
  • 2010年07月06日 10:17
  • 12725

深入理解Dalvik虚拟机- 解释器的运行机制

Dalvik的指令执行是解释器+JIT的方式,解释器就是虚拟机来对Javac编译出来的字节码,做译码、执行,而不是转化成CPU的指令集,由CPU来做译码,执行。可想而知,解释器的效率是相对较低的,所以...
  • threepigs
  • threepigs
  • 2016年04月06日 00:14
  • 3761

Android 利用MultiDex方案实现热补丁修复

在线热补丁修复的讨论,从Xopsed到Dexposed,再到AndFix,再到QQ空间团队的Class补丁。可谓是各有特色。本文讨论的是基于Cydia Hook实现的在线Class热补丁。相对于Xop...
  • u011467537
  • u011467537
  • 2017年02月17日 14:00
  • 461

基于cydia Hook在线热修复补丁方案

背景:发布的app遇到重大bug怎么办,重新发布肯定只能浪费人力财力,那么有什么可以向windows下实现补丁的方式来动态更新类呢?答案是肯定的,尽管android系统中没有提供原生的补丁的技术,但是...
  • xwl198937
  • xwl198937
  • 2015年11月12日 17:18
  • 6125

APK加固之类抽取分析与修复

测试环境与工具 手机系统: 华为U9508 android 4.2.2 IDA Pro 6.8 AndroidKiller 1.2 高手不要见笑,仅供小菜玩乐,有不对或不足的地方还请多多指教,不胜...
  • justFWD
  • justFWD
  • 2015年11月27日 18:01
  • 3107

Android 热补丁技术的探索与简单实战----Qzone方案

Android app客户端与Web app相比的有一个劣势在于web app有更新不需要重新安装程序,而Android app如果有更新则需要重新下载最新版本安装完成更新,这个缺点无疑会给用户带来不...
  • u012760183
  • u012760183
  • 2016年07月28日 13:36
  • 1433

Cydia Substrate之hook native代码

由于许多公司对APP的安全性越来越重视,因此很多公司的核心业务处理模块一般会采用NDK开发,通过jni机制调用C代码来实现模块功能。这种用C/C++开发出来的代码反编译分析的难度远远大于java开发,...
  • qq_18870023
  • qq_18870023
  • 2016年09月27日 15:23
  • 3844
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基于cydia Hook在线热修复补丁方案
举报原因:
原因补充:

(最多只允许输入30个字)