软件保护方案
背景
Java是一种跨平台的解释型语言,由于跨平台的需求,Java的指令集比较简单而通用,较容易得出程序的语义信息。因此java开发的软件在诸如安装到客户端的商业环境下,往往存在商业机密泄露,滥用的风险,对企业生产者造成不可估量的损失。
Java编译器将每一个类编译成一个单独的文件,这也简化了反编译的工作。Java源代码编译中间“字节码”存储于Class文件中。Class文件是一种字节码形式的中间代码,该字节码中仍然保留所有的方法名称、变量名称,并且通过这些名称来访问变量和方法,这些符号往往带有许多语义信息。由于Java程序自身的特点,对于不经过处理的Java程序,反编译的效果非常好。
目前,市场上有许多Java的反编译工具,这些工具的反编译速度和效果都很不错。好的反编译软件,能够反编译出非常接近源代码的程序。通过反编译器,黑客能够对这些程序进行更改,或者复用其中的程序。因此,如何保护Java程序不被反编译,是非常重要的一个问题。此外,Java程序基于JVM这一“中间层”能够在不同平台上运行,真正实现了“一次编译,到处运行”的目的。Java的这一特性对商业软件的版权保护也带来了挑战。
因此java开发的软件在诸如安装到客户端的商业环境下,往往存在商业机密泄露,滥用的风险,对企业生产者造成不可估量的损失。为此,本文就围绕维护生产者合法权益,商业机密的保护,即软件保护方案作出若干讨论。
软件保护主要涉及两大方面
源文件保护
目前针对Java源文件方法主要有本地编译、数字水印、ClassLoader加密,以及代码混淆。
1.本地编译
Java本地编译是指将Java应用程序编译成本地应用程序,如 Windows平台下名为exe的应用程序。
通过java虚拟机将源代码生成Java类文件,再将类文件编译成可执行文件。用该技术生成的本地应用程序是二进制格式的可执行文件,但该方法牺牲了java的跨平台特性,对于桌面应用程序的开发问题不大但是对于Web应用程序的开发,则是一个致命缺陷。
2.数字水印
数字水印技术是将一些标识信息(即数字水印)直接嵌入数字载体(包括多媒体、文档、软件等)当中但不影响原载体的使用价值,也不容易被人的知觉系统觉察或注意到。通过这些隐藏在载体中的信息,可以达到确认内容创建者、购买者、传送隐秘信息或者判断载体是否被篡改等目的。在需要证明程序是否非法使用时,数字水印就变得很重要。使用水印技术并不能阻止类文件被反编译,但是可以在需要确认某些程序是否属于剽窃时提供有效证据。
实施方案:
第一步、创建印章库
添加的数字水印不能破坏原来代码的运行,而注释打包后会清除,因此以注解方式隔行嵌入。由于反编译后,水印可以通过正则,批量删除,因此添加的水印需要具备不可批量替换性,与一般代码注解相仿的仿真性。
我们需要建立印章库,即存储所有生成水印所需的印章来生成水印,并且可以校验反编译后的代码是否包含我们独有的水印来作为代码剽窃的证据。
通过find . -name "*.js" -or -name "*.tpl" -or -name "*.xml" -or -name "*.java" -or -name "*.css" |xargs grep -v " ^$"|wc
-l
统计现有项目roshi-parent(54445行),recruit-parent(109426行)代码知,印章库需要100000印章。
第二步、生成印章
可以通过随机生成长度在10~100的纯字母字符串做为印章。
例如:Afefefefefefe
第三步、添加水印
需要创建添加水印服务,首先从印章库随机获取印章,假如为Afefefefefefe。
根据已经获取到的印章生成印章注解,放在com.roshi.print的包下。
印章代码如图所示:
逐行扫描源代码,在所有public修饰行的前方增加水印@Afefefefefefe,每添加一次水印就根据印章库的印章重新生成一次印章,尽可能让水印不同。
添加完水印的代码如图所示:
3. Classloader加密
Classloader的基本目标是对类的请求提供服务。
当JVM需要使用类时,它根据名称向 Classloader请求这个类,然后 Classloader试图返回一个表示这个类的Class对象。通过覆盖对应于这个过程不同阶段的方法,可以创建定制的 Classloader。在装入原始数据后先进行解密,再转换成 Class对象。由于把原始字节码转换成Class对象的过程完全由系统负责,因此只需先获得原始数据,接着就可以进行包含解密在内的任何转换。这种方案比其他方案更加安全,然而这种加密方法存在一个漏洞,由于 Classloader的类是用Java编写的,如果对 Classloader类进行反编译,提取其中解密算法,就可解密所有被加密的其他类。
实施方案
====》自己重写classloader
public class MyClassLoader extends ClassLoader{
public static void main(String[] args) throws IOException {
String srcPath = args[0];
String destDir = args[1];
String destFileName = srcPath.substring(srcPath.lastIndexOf("\\") + 1);
String destFilePath = destDir + "\\" + destFileName;
FileInputStream fin = new FileInputStream(srcPath);
FileOutputStream fout = new FileOutputStream(destFilePath);
cypher(fin,fout);
fin.close();
fout.close();
}
/**
* <p>Title: cypher</p>
* <p>Description:加密, 将原来的1改为0,0改为1</p>
* @param in
* @param out
* @throws IOException
*/
public static void cypher(InputStream in ,OutputStream out) throws IOException{
int b=0;
while ((b=in.read())!=-1) {
out.write(b ^ 0xff);
}
}
private String classDir;
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir=classDir;
}
/**
* 覆盖ClassLoader的findClass方法
*/
@SuppressWarnings("deprecation")
@Override
protected Class<?> findClass(String name)
throws ClassNotFoundException {
String classFileName = classDir + "\\" + name + ".class";
try {
FileInputStream fin = new FileInputStream(classFileName);
ByteArrayOutputStream baout = new ByteArrayOutputStream();
//解密
cypher(fin,baout);
fin.close();
//转为字节数组
byte[] byteArray = baout.toByteArray();
return defineClass(byteArray, 0, byteArray.length);
} catch (Exception e) {
}
return null;
}
}
运行MyclassLoader的main方法,得到加密后的class,加密后的class再通过以下测试代码进行解密。
@SuppressWarnings("rawtypes")
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class c = new MyClassLoader("myloaderlib").loadClass("ClassLoaderAttach");
Date d = (Date)c.newInstance();
System.out.println(d);
}
====》通过xjar
xjar是什么
xjar的定义