理论教育 实现Android类动态加密优化技术

实现Android类动态加密优化技术

时间:2023-06-19 理论教育 版权反馈
【摘要】:在加载APK文件和DEX文件时,使用了类动态加载技术。在Android应用开发过程中,通常常规的开发方式和代码架构就能满足我们的普通需求。如何让Android应用执行服务器上的不可预知的代码。如何对Android应用加密,而只在执行时自解密,从而防止被破解。例如,在使用标准Java虚拟机时,经常自定义继承自ClassLoader的类加载器。DexClassLoader和PathClassLoader其实都是通过类DexFile实现类加载功能的。不过坚信的是,Dalvik或Android开源项目都正在向能够支持raw数据定义类的方向努力。

实现Android类动态加密优化技术

在加载APK文件和DEX文件时,使用了类动态加载技术。在Android应用开发过程中,通常常规的开发方式和代码架构就能满足我们的普通需求。但是有些特殊问题,常常引发我们进一步的沉思。考虑下面的问题:

如何开发一个可以自定义控件的Android应用?就像Eclipse一样,可以动态加载插件。

如何让Android应用执行服务器上的不可预知的代码。

如何对Android应用加密,而只在执行时自解密,从而防止被破解。

熟悉Java技术的读者会想到,我们需要使用类加载器灵活的加载执行的类。这在Java中已经算是一项比较成熟的技术了,但是在Android应用中,我们都还比较陌生。

(1)类加载机制

Dalvik虚拟机如同其他Java虚拟机一样,在运行程序时首先需要将对应的类加载到内存中。而在Java标准的虚拟机中,类加载可以从CLASS文件中读取,也可以是其他形式的二进制流。因此,我们常常利用这一点,在程序运行时手动加载Class,从而达到代码动态加载执行的目的。

但是Dalvik虚拟机毕竟不算是标准的Java虚拟机,因此在类加载机制方面它们有相同的地方,也有不同之处。我们必须区别对待。例如,在使用标准Java虚拟机时,经常自定义继承自ClassLoader的类加载器。然后通过defineClass()方法来从一个二进制流中加载Class,但是这在Android里是行不通的,这一点可以从Android源码知道。Android中ClassLoader的defineClass()方法,具体是调用VMClassLoader的defineClass()本地静态方法。而这个本地方法除了抛出一个“UnsupportedOperationException”异常之外,什么都没做,甚至连返回值都为空。下面是演示代码:

(2)Dalvik虚拟机类的加载机制

如果在Dalvik虚拟机里,ClassLoader不能用,我们该如何实现动态加载类呢?Android为我们从ClassLoader派生出了两个类:DexClassLoader和PathClassLoader。其中需要特别说明的是,PathClassLoader中如下被注释掉的代码:

这可以从另一方面证明了defineClass()函数在Dalvik虚拟机中被限制了。而在这两个继承自ClassLoader的类加载器,本质上是重载了ClassLoader的findClass方法。在执行loadClass时可以参照ClassLoader的部分源码:(www.daowen.com)

由此可见,DexClassLoader和PathClassLoader都属于符合双亲委派模型的类加载器(因为它们没有重载loadClass方法)。也就是说,它们在加载一个类之前,会检查自己以及自己以上的类加载器是否已经加载了这个类。如果已经加载过了,则会直接将之返回,而不会重复加载。

DexClassLoader和PathClassLoader其实都是通过类DexFile实现类加载功能的。这里需要提及的是,Dalvik虚拟机识别的是DEX文件,而不是CLASS文件。因此,我们供类加载的文件也只能是DEX文件,或者包含有DEX文件的.apk或.jar文件。

PathClassLoader是通过构造函数DexFile(path)来生成生DexFile对象的;而DexClassLoader则是通过其静态方法loadDex()得到DexFile对象的。这两者的区别在于DexClassLoader需要提供一个可写的outpath路径,用来释放.apk包或者.jar包中的DEX文件。也就是说,PathClassLoader不能主动从zip包中释放出DEX,因此只支持直接操作DEX格式文件,或者已经安装的APK(因为已经安装的APK在cache中存在缓存的DEX文件)。而DexClassLoader可以支持.apk、.jar和.dex文件,并且会在指定的outpath路径释放出DEX文件。

当PathClassLoader在加载类时,调用的是DexFile的loadClassBinaryName,而DexClassLoader调用的是loadClass。所以在使用PathClassLoader时,类的全名需要用“/”替换“.”。

(3)具体操作

在具体操作时,可能需要使用到的工具有:javac、dx、eclipse等。其中在使用dx工具时,最好指明:--no-strict,因为CLASS文件的路径可能不匹配。当加载好类后,通常可以通过Java反射机制来使用这个类。但是这样做的效率相对不高,而且老用反射代码也比较复杂。更好的做法是定义一个interface,并将这个interface写进容器端。待加载的类,继承自这个interface,并且有一个参数为空的构造函数,以使我们能够通过Class的newInstance方法产生对象。然后将对象强制转换为interface对象,于是就可以直接调用成员方法了。

(4)代码加密

在加密代码时,最初设想将DEX文件加密,然后通过JNI将解密代码写在Native层。解密之后直接传入二进制流,再通过defineClass将类加载到内存中。但是由于不能直接使用defineClass,而必须传文件路径给Dalvik虚拟机内核,因此解密后的文件需要写到磁盘上,增加了被破解的风险。

Dalvik虚拟机内核仅支持从DEX文件加载类的方式是不灵活的,由于没有非常深入的研究内核,我不能确定是Dalvik虚拟机本身不支持还是Android在移植时将其限制了。不过坚信的是,Dalvik或Android开源项目都正在向能够支持raw数据定义类的方向努力。

在RawDexFile出来之前,我们只能使用这种存在一定风险的加密方式。我们需要注意释放的DEX文件路径及权限管理。另外在类加载完毕之后,除非出于其他目的,否则应该马上删除临时的解密文件。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈