由于Java字节码的抽象级别较高,因此它们较容易被反编译。下面介绍了四种APP防反编译的常用(yòng)方法,用(yòng)于保护Java字节码不被反编译。通常,这些方法不能(néng)够绝对防止程序被反编译,而是加大反编译的难度而已,因為(wèi)这些方法都有(yǒu)自己的使用(yòng)环境和弱点。
1.隔离Java程序
最简单的方法就是让用(yòng)户不能(néng)够访问到Java Class程序,这种方法是最根本的方法,具(jù)體(tǐ)实现有(yǒu)多(duō)种方式。例如,开发人员可(kě)以将关键的Java Class放在服務(wù)器端,客户端通过访问服務(wù)器的相关接口来获得服務(wù),而不是直接访问Class文(wén)件。这样黑客就没有(yǒu)办(bàn)法反编译Class文(wén)件。目前,通过接口提供服務(wù)的标准和协议也越来越多(duō),例如 HTTP、Web Service、RPC等。但是有(yǒu)很(hěn)多(duō)应用(yòng)都不适合这种保护方式,例如对于单机运行的程序就无法隔离Java程序。
2.对Class文(wén)件进行加密
為(wèi)了防止Class文(wén)件被直接反编译,许多(duō)开发人员将一些关键的Class文(wén)件进行加密,例如对注册码、序列号管理(lǐ)相关的类等。在使用(yòng)这些被加密的类之前,程序首先需要对这些类进行解密,而后再将这些类装(zhuāng)载到JVM当中(zhōng)。这些类的解密可(kě)以由硬件完成,也可(kě)以使用(yòng)软件完成。
在实现时,开发人员往往通过自定义ClassLoader类来完成加密类的装(zhuāng)载(注意由于安(ān)全性的原因,Applet不能(néng)够支持自定义的 ClassLoader)。自定义的ClassLoader首先找到加密的类,而后进行解密,最后将解密后的类装(zhuāng)载到JVM当中(zhōng)。在这种保护方式中(zhōng),自定义的ClassLoader是非常关键的类。由于它本身不是被加密的,因此它可(kě)能(néng)成為(wèi)黑客最先攻击的目标。如果相关的解密密钥和算法被攻克,那么被加密的类也很(hěn)容易被解密。
3.转换成本地代码
将程序转换成本地代码也是一种防止反编译的有(yǒu)效方法。因為(wèi)本地代码往往难以被反编译。开发人员可(kě)以选择将整个应用(yòng)程序转换成本地代码,也可(kě)以选择关键模块转换。如果仅仅转换关键部分(fēn)模块,Java程序在使用(yòng)这些模块时,需要使用(yòng)JNI技(jì )术进行调用(yòng)。当然,在使用(yòng)这种技(jì )术保护Java程序的同时,也牺牲了 Java的跨平台特性。对于不同的平台,我们需要维护不同版本的本地代码,这将加重软件支持和维护的工(gōng)作(zuò)。不过对于一些关键的模块,有(yǒu)时这种方案往往是必要的。為(wèi)了保证这些本地代码不被修改和替代,通常需要对这些代码进行数字签名(míng)。在使用(yòng)这些本地代码之前,往往需要对这些本地代码进行认证,确保这些代码没有(yǒu)被黑客更改。如果签名(míng)检查通过,则调用(yòng)相关JNI方法。
4.代码混淆
代码混淆是对Class文(wén)件进行重新(xīn)组织和处理(lǐ),使得处理(lǐ)后的代码与处理(lǐ)前代码完成相同的功能(néng)(语义)。但是混淆后的代码很(hěn)难被反编译,即反编译后得出的代码是非常难懂、晦涩的,因此反编译人员很(hěn)难得出程序的真正语义。从理(lǐ)论上来说,黑客如果有(yǒu)足够的时间,被混淆的代码仍然可(kě)能(néng)被破解,甚至目前有(yǒu)些人正在研制反混淆的工(gōng)具(jù)。但是从实际情况来看,由于混淆技(jì )术的多(duō)元化发展,混淆理(lǐ)论的成熟,经过混淆的Java代码还是能(néng)够很(hěn)好地防止反编译。下面我们会详细介绍混淆技(jì )术,因為(wèi)混淆是一种保护Java程序的重要技(jì )术。
几种技(jì )术的总结:
以上几种技(jì )术都有(yǒu)不同的应用(yòng)环境,各自都有(yǒu)自己的弱点,表1是相关特点的比较。
到目前為(wèi)止,对于Java程序的保护,混淆技(jì )术还是最基本的保护方法。Java混淆工(gōng)具(jù)也非常多(duō),包括商(shāng)业的、免费的、开放源代码的。Sun公(gōng)司也提供了自己的混淆工(gōng)具(jù)。它们大多(duō)都是对Class文(wén)件进行混淆处理(lǐ),也有(yǒu)少量工(gōng)具(jù)首先对源代码进行处理(lǐ),然后再对Class进行处理(lǐ),这样加大了混淆处理(lǐ)的力度。目前,商(shāng)业上比较成功的混淆工(gōng)具(jù)包括JProof公(gōng)司的1stBarrier系列、Eastridge公(gōng)司的JShrink和4thpass.com 的SourceGuard等。主要的混淆技(jì )术按照混淆目标可(kě)以进行如下分(fēn)类,它们分(fēn)别為(wèi)符号混淆(Lexical Obfuscation)、数据混淆(Data Obfuscation)、控制混淆(Control Obfuscation)、预防性混淆(Prevent Transformation)。
符号混淆
在Class中(zhōng)存在许多(duō)与程序执行本身无关的信息,例如方法名(míng)称、变量名(míng)称,这些符号的名(míng)称往往带有(yǒu)一定的含义。例如某个方法名(míng)為(wèi) getKeyLength(),那么这个方法很(hěn)可(kě)能(néng)就是用(yòng)来返回Key的長(cháng)度。符号混淆就是将这些信息打乱,把这些信息变成无任何意义的表示,例如将所有(yǒu)的变量从vairant_001开始编号;对于所有(yǒu)的方法从method_001开始编号。这将对反编译带来一定的困难。对于私有(yǒu)函数、局部变量,通常可(kě)以改变它们的符号,而不影响程序的运行。但是对于一些接口名(míng)称、公(gōng)有(yǒu)函数、成员变量,如果有(yǒu)其它外部模块需要引用(yòng)这些符号,我们往往需要保留这些名(míng)称,否则外部模块找不到这些名(míng)称的方法和变量。因此,多(duō)数的混淆工(gōng)具(jù)对于符号混淆,都提供了丰富的选项,让用(yòng)户选择是否、如何进行符号混淆。
数据混淆
数据混淆是对程序使用(yòng)的数据进行混淆。混淆的方法也有(yǒu)多(duō)种,主要可(kě)以分(fēn)為(wèi)改变数据存储及编码(Store and Encode Transform)、改变数据访问(Access Transform)。
改变数据存储和编码可(kě)以打乱程序使用(yòng)的数据存储方式。例如将一个有(yǒu)10个成员的数组,拆开為(wèi)10个变量,并且打乱这些变量的名(míng)字;将一个两维数组转化為(wèi)一个一维数组等。对于一些复杂的数据结构,我们将打乱它的数据结构,例如用(yòng)多(duō)个类代替一个复杂的类等。
另外一种方式是改变数据访问。例如访问数组的下标时,我们可(kě)以进行一定的计算,图5就是一个例子。 在实践混淆处理(lǐ)中(zhōng),这两种方法通常是综合使用(yòng)的,在打乱数据存储的同时,也打乱数据访问的方式。经过对数据混淆,程序的语义变得复杂了,这样增大了反编译的难度。
控制混淆
控制混淆就是对程序的控制流进行混淆,使得程序的控制流更加难以反编译,通常控制流的改变需要增加一些额外的计算和控制流,因此在性能(néng)上会给程序带来一定的负面影响。有(yǒu)时,需要在程序的性能(néng)和混淆程度之间进行权衡。控制混淆的技(jì )术最為(wèi)复杂,技(jì )巧也最多(duō)。这些技(jì )术可(kě)以分(fēn)為(wèi)如下几类:
增加混淆控制通过增加额外的、复杂的控制流,可(kě)以将程序原来的语义隐藏起来。例如,对于按次序执行的两个语句A、B,我们可(kě)以增加一个控制条件,以决定B的执行。通过这种方式加大反汇编的难度。但是所有(yǒu)的干扰控制都不应该影响B的执行。图6就给出三种方式,為(wèi)这个例子增加混淆控制。
控制流重组
重组控制流也是重要的混淆方法。例如,程序调用(yòng)一个方法,在混淆后,可(kě)以将该方法代码嵌入到调用(yòng)程序当中(zhōng)。反过来,程序中(zhōng)的一段代码也可(kě)以转变為(wèi)一个函数调用(yòng)。另外,对于一个循环的控制流,為(wèi)可(kě)以拆分(fēn)多(duō)个循环的控制流,或者将循环转化成一个递归过程。这种方法最為(wèi)复杂,研究的人员也非常多(duō)。 预防性混淆
这种混淆通常是针对一些专用(yòng)的反编译器而设计的,一般来说,这些技(jì )术利用(yòng)反编译器的弱点或者Bug来设计混淆方案。例如,有(yǒu)些反编译器对于Return后面的指令不进行反编译,而有(yǒu)些混淆方案恰恰将代码放在Return语句后面。这种混淆的有(yǒu)效性对于不同反编译器的作(zuò)用(yòng)也不太相同的。一个好的混淆工(gōng)具(jù),通常会综合使用(yòng)这些混淆技(jì )术。
案例分(fēn)析
在实践当中(zhōng),保护一个大型Java程序经常需要综合使用(yòng)这些方法,而不是单一使用(yòng)某一种方法。这是因為(wèi)每种方法都有(yǒu)其弱点和应用(yòng)环境。综合使用(yòng)这些方法使得Java程序的保护更加有(yǒu)效。另外,我们经常还需要使用(yòng)其它的相关安(ān)全技(jì )术,例如安(ān)全认证、数字签名(míng)、PKI等。
本文(wén)给出的例子是一个Java应用(yòng)程序,它是一个SCJP(Sun Certificate Java Programmer)的模拟考试软件。该应用(yòng)程序带有(yǒu)大量的模拟题目,所有(yǒu)的题目都被加密后存储在文(wén)件中(zhōng)。由于它所带的题库是该软件的核心部分(fēn),所以关于题库的存取和访问就成為(wèi)非常核心的类。一旦这些相关的类被反编译,则所有(yǒu)的题库将被破解。现在,我们来考虑如何保护这些题库及相关的类。
在这个例子中(zhōng),我们考虑使用(yòng)综合保护技(jì )术,其中(zhōng)包括本地代码和混淆技(jì )术。因為(wèi)该软件主要发布在Windows上,因此转换成本地代码后,仅仅需要维护一个版本的本地代码。另外,混淆对Java程序也是非常有(yǒu)效的,适用(yòng)于这种独立发布的应用(yòng)系统。
在具(jù)體(tǐ)的方案中(zhōng),我们将程序分(fēn)為(wèi)两个部分(fēn),一个是由本地代码编写的题库访问的模块,另外一个是由Java开发的其它模块。这样可(kě)以更高程度地保护题目管理(lǐ)模块不被反编译。对于Java开发的模块,我们仍然要使用(yòng)混淆技(jì )术。
对于题目管理(lǐ)模块,由于程序主要在Windows下使用(yòng),所以使用(yòng)C++开发题库访问模块,并且提供了一定的访问接口。為(wèi)了保护题库访问的接口,我们还增加了一个初始化接口,用(yòng)于每次使用(yòng)题库访问接口之前的初始化工(gōng)作(zuò)。它的接口主要分(fēn)為(wèi)两类:
1. 初始化接口
在使用(yòng)题库模块之前,我们必须先调用(yòng)初始化接口。在调用(yòng)该接口时,客户端需要提供一个随机数作(zuò)為(wèi)参数。题库管理(lǐ)模块和客户端通过这个随机数,按一定的算法同时生成相同的SessionKey,用(yòng)于加密以后输入和输出的所有(yǒu)数据。通过这种方式,只有(yǒu)授权(有(yǒu)效)的客户端才能(néng)够连接正确的连接,生成正确的 SessionKey,用(yòng)于访问题库信息。非法的客户很(hěn)难生成正确的SessionKey,因此无法获得题库的信息。如果需要建立更高的保密级别,也可(kě)以采用(yòng)双向认证技(jì )术。
2. 数据访问接口
认证完成之后,客户端就可(kě)以正常的访问题库数据。但是,输入和输出的数据都是由SessionKey所加密的数据。因此,只有(yǒu)正确的题库管理(lǐ)模块才能(néng)够使用(yòng)题库管理(lǐ)模块
爱加密(www.ijiami.cn)是全球专业的移动信息安(ān)全综合服務(wù)提供商(shāng),专注于移动应用(yòng)安(ān)全、安(ān)全大数据及物(wù)联网安(ān)全,坚持以用(yòng)户需求為(wèi)导向、持续不断的创新(xīn),致力于為(wèi)客户提供全方位、一站式的移动安(ān)全全生命周期解决方案。爱加密的愿景是通过革新(xīn)性安(ān)全方案和7X24小(xiǎo)时全天候的专业服務(wù)保护更加智能(néng)世界的安(ān)全,打造和谐、强大、高度安(ān)全的万物(wù)互联生态环境。
爱加密获得了多(duō)项专业资质(zhì)和荣誉,主导并参与多(duō)项國(guó)家标准和行业规范的制定,可(kě)提供满足合规要求的、基于企业移动信息安(ān)全的一體(tǐ)化综合解决方案。爱加密拥有(yǒu)移动安(ān)全咨询、移动安(ān)全培训、移动安(ān)全检测、移动安(ān)全加固、移动安(ān)全感知、移动安(ān)全管理(lǐ)等产(chǎn)品體(tǐ)系,可(kě)為(wèi)用(yòng)户提供基于企业移动信息安(ān)全的一體(tǐ)化综合解决方案。