java

位置:IT落伍者 >> java >> 浏览文章

如何保护Java程序


发布日期:2024年02月04日
 
如何保护Java程序

Java是一种跨平台的解释型语言Java 源代码编译中间字节码存储于class文件中Class文件是一种字节码形式的中间代码该字节码中包括了很多源代码的信息例如变量名方法名等因此Java中间代码的反编译就变得非常容易目前市场上有许多免费的商用的反编译软件都能够生成高质量的反编译后的源代码所以对开发人员来说如何保护Java程序就变成了一个非常重要的挑战本文首先讨论了保护Java程序的基本方法然后对代码混淆问题进行深入研究最后结合一个实际的应用程序分析如何在实践中保护Java程序

反编译成为保护Java程序的最大挑战

通常CC++等编程语言开发的程序都被编译成目标代码这些目标代码都是本机器的二进制可执行代码通常所有的源文件被编译链接成一个可执行文件在这些可执行文件中编译器删除了程序中的变量名称方法名称等信息这些信息往往是由内存地址表示例如如果需要使用一个变量往往是通过这个变量的地址来访问的因此反编译这些本地的目标代码就是非常困难的

Java语言的出现使得反编译变得非常容易而有效原因如下由于跨平台的需求Java的指令集比较简单而通用较容易得出程序的语义信息Java编译器将每一个类编译成一个单独的文件这也简化了反编译的工作Java 的Class文件中仍然保留所有的方法名称变量名称并且通过这些名称来访问变量和方法这些符号往往带有许多语义信息由于Java程序自身的特点对于不经过处理的Java程序反编译的效果非常好

目前市场上有许多Java的反编译工具有免费的也有商业使用的还有的是开放源代码的这些工具的反编译速度和效果都非常不错好的反编译软件能够反编译出非常接近源代码的程序因此通过反编译器黑客能够对这些程序进行更改或者复用其中的程序因此如何保护Java程序不被反编译是非常重要的一个问题

常用的保护技术

由于Java字节码的抽象级别较高因此它们较容易被反编译本节介绍了几种常用的方法用于保护Java字节码不被反编译通常这些方法不能够绝对防止程序被反编译而是加大反编译的难度而已因为这些方法都有自己的使用环境和弱点

隔离Java程序

最简单的方法就是让用户不能够访问到Java Class程序这种方法是最根本的方法具体实现有多种方式例如开发人员可以将关键的Java Class放在服务器端客户端通过访问服务器的相关接口来获得服务而不是直接访问Class文件这样黑客就没有办法反编译Class文件目前通过接口提供服务的标准和协议也越来越多例如 HTTPWeb ServiceRPC等但是有很多应用都不适合这种保护方式例如对于单机运行的程序就无法隔离Java程序这种保护方式见图所示

隔离Java程序示意图

对Class文件进行加密

为了防止Class文件被直接反编译许多开发人员将一些关键的Class文件进行加密例如对注册码序列号管理相关的类等在使用这些被加密的类之前程序首先需要对这些类进行解密而后再将这些类装载到JVM当中这些类的解密可以由硬件完成也可以使用软件完成

在实现时开发人员往往通过自定义ClassLoader类来完成加密类的装载(注意由于安全性的原因Applet不能够支持自定义的ClassLoader)自定义的ClassLoader首先找到加密的类而后进行解密最后将解密后的类装载到JVM当中在这种保护方式中自定义的ClassLoader是非常关键的类由于它本身不是被加密的因此它可能成为黑客最先攻击的目标如果相关的解密密钥和算法被攻克那么被加密的类也很容易被解密这种保护方式示意图见图

对Class文件进行加密示意图

转换成本地代码

将程序转换成本地代码也是一种防止反编译的有效方法因为本地代码往往难以被反编译开发人员可以选择将整个应用程序转换成本地代码也可以选择关键模块转换如果仅仅转换关键部分模块Java程序在使用这些模块时需要使用JNI技术进行调用

当然在使用这种技术保护Java程序的同时也牺牲了Java的跨平台特性对于不同的平台我们需要维护不同版本的本地代码这将加重软件支持和维护的工作不过对于一些关键的模块有时这种方案往往是必要的

为了保证这些本地代码不被修改和替代通常需要对这些代码进行数字签名在使用这些本地代码之前往往需要对这些本地代码进行认证确保这些代码没有被黑客更改如果签名检查通过则调用相关JNI方法这种保护方式示意图见图

代码混淆

转换成本地代码示意图

代码混淆是对Class文件进行重新组织和处理使得处理后的代码与处理前代码完成相同的功能(语义)但是混淆后的代码很难被反编译即反编译后得出的代码是非常难懂晦涩的因此反编译人员很难得出程序的真正语义从理论上来说黑客如果有足够的时间被混淆的代码仍然可能被破解甚至目前有些人正在研制反混淆的工具但是从实际情况来看由于混淆技术的多元化发展混淆理论的成熟经过混淆的Java代码还是能够很好地防止反编译下面我们会详细介绍混淆技术因为混淆是一种保护Java程序的重要技术是代码混淆的示意图

代码混淆示意图

几种技术的总结

以上几种技术都有不同的应用环境各自都有自己的弱点是相关特点的比较

混淆技术介绍

不同保护技术比较表

到目前为止对于Java程序的保护混淆技术还是最基本的保护方法Java混淆工具也非常多包括商业的免费的开放源代码的Sun公司也提供了自己的混淆工具它们大多都是对Class文件进行混淆处理也有少量工具首先对源代码进行处理然后再对Class进行处理这样加大了混淆处理的力度目前商业上比较成功的混淆工具包括JProof公司的stBarrier系列Eastridge公司的JShrink和的SourceGuard等主要的混淆技术按照混淆目标可以进行如下分类它们分别为符号混淆(Lexical Obfuscation)数据混淆(Data Obfuscation)控制混淆(Control Obfuscation)预防性混淆(Prevent Transformation)

符号混淆

在Class中存在许多与程序执行本身无关的信息例如方法名称变量名称这些符号的名称往往带有一定的含义例如某个方法名为getKeyLength()那么这个方法很可能就是用来返回Key的长度符号混淆就是将这些信息打乱把这些信息变成无任何意义的表示例如将所有的变量从vairant_开始编号对于所有的方法从method_开始编号这将对反编译带来一定的困难对于私有函数局部变量通常可以改变它们的符号而不影响程序的运行但是对于一些接口名称公有函数成员变量如果有其它外部模块需要引用这些符号我们往往需要保留这些名称否则外部模块找不到这些名称的方法和变量因此多数的混淆工具对于符号混淆都提供了丰富的选项让用户选择是否如何进行符号混淆

数据混淆

改变数据访问

数据混淆是对程序使用的数据进行混淆混淆的方法也有多种主要可以分为改变数据存储及编码(Store and Encode Transform)改变数据访问(Access Transform)

改变数据存储和编码可以打乱程序使用的数据存储方式例如将一个有个成员的数组拆开为个变量并且打乱这些变量的名字将一个两维数组转化为一个一维数组等对于一些复杂的数据结构我们将打乱它的数据结构例如用多个类代替一个复杂的类等

另外一种方式是改变数据访问例如访问数组的下标时我们可以进行一定的计算就是一个例子

在实践混淆处理中这两种方法通常是综合使用的在打乱数据存储的同时也打乱数据访问的方式经过对数据混淆程序的语义变得复杂了这样增大了反编译的难度

控制混淆

控制混淆就是对程序的控制流进行混淆使得程序的控制流更加难以反编译通常控制流的改变需要增加一些额外的计算和控制流因此在性能上会给程序带来一定的负面影响有时需要在程序的性能和混淆程度之间进行权衡控制混淆的技术最为复杂技巧也最多这些技术可以分为如下几类

增加混淆控制 通过增加额外的复杂的控制流可以将程序原来的语义隐藏起来例如对于按次序执行的两个语句AB我们可以增加一个控制条件以决定B的执行通过这种方式加大反汇编的难度但是所有的干扰控制都不应该影响B的执行就给出三种方式为这个例子增加混淆控制

增加混淆控制的三种方式

控制流重组 重组控制流也是重要的混淆方法例如程序调用一个方法在混淆后可以将该方法代码嵌入到调用程序当中反过来

上一篇:Java的逻辑运算符

下一篇:Java通用集合库