资讯   |   开发   |   选机中心   |   产品大全 | IBM | 惠普 | 联想 | 戴尔 | 苹果 | 神舟
更多: | 华硕 | 明基 | 方正 | 紫光 | TCL | 夏新 | 联宝 | 宏碁 | 七喜 | 长城 | 清华同方 | 海尔 | 三星 | 东芝 | 索尼 | 富士通 | LG | 技术 | ddnoon
当前位置:笔记本 > 编程开发 >
Advertisement
文章正文

如何有效的保护JAVA程序 _编程

类型:转载   责任编辑:asp.net   日期:2007/05/23


热门软件下载:


   
  • 从SP提供给CP的接口看SP的实力  
  • 利用java IDE(Jbuilder ,Eclipse)快速生成代码(1)--JavaBean  
  • 始终会用上的Common BeanUtils  
  • GuestBook Test  
  • PackageTest  
  • 每个初学JAVA者都应该搞懂的问题  
  • JDBC连接数据库格式  
  • New Groovy --- Closure/Block问题  
  • 谈谈J2SE中的序列化(二)  
  • (五)jdbc从入门到精通,特别适合初学者!  
  • 页面导航:

    正文内容:

    第一章 流行的加密方式简介

    关于java程序的加密方式,一直以来都是以java模糊处理(obfuscator)为主。这方面的研究结果也颇多,既有模糊器(如现在大名鼎鼎的jode),也有针对反编译器的"炸弹"(如针对反编译工具mocha的 "炸弹" crema和hosemocha)。模糊器,从其字面上,我们就可以知道它是通过模糊处理java代码,具体的说,就是更换变量名,函数名,甚至类名等方法使其反编译出来的代码变得不可理解。举个例子来说吧。

    先将将下面源代码编译成class文件。

    public class test { int sortway; void sort(vector a) { …… } void setsortway(int way) { …… } void sort(vector a, int way) { …… } }

    后通过jode进行模糊处理后,反编译过来后, 可能变成下列代码。

    public class oooooooo0oo0o { int oooo0ooo0oo0o; void ooo0oooo0oo0o (vector ooooo0oo0oooo) { …… } void oooo00ooooo0o (int oo0ooooo0oo0o) { …… } void ooo0oooo0oo0o (vector ooooo0oo0oooo, int oo0ooooo0oo0o) { oooo00ooooo0o (oo0ooooo0oo0o); ooo0oooo0oo0o (ooooo0oo0oooo); } }

    其实这只是做到了视觉上的处理,其业务逻辑却依然不变,加以耐心,仍是可以攻破的,如果用在用户身份验证等目的上,完全可以找到身份验证算法而加以突破限制。

    而所谓的"炸弹"是针对反编译工具本身的缺陷,这种方法对于特定的反编译工具是非常有效的,然而到目前为止,还没有一个全能型的,对每一种反编译工具皆有效,其局限性是明显的!

    另一种方法是采用classloader加密。java虚拟机通过一个称为classloader的对象装来载类文件的字节码,而classloader是可以由java程序自己来定制的。classloader是如何装载类的呢?classloader根据类名在jar包中找到该类的文件,读取文件,并把它转换成一个class对象。该方法的原理就是,对需加密的类文件我们先行采用一定的方法(可以是pgp, rsa, md5等方法)进行加密处理,我们可以在读取文件之后,进行解密后,再转换成一个class对象。

    关于classloader工作方式的详细介绍就不在此一一述说了,前面已有文章专题讨论了。

    有没有发现,该方法并未解决classloader本身的安全性? 显然,只要反编译了该classloader类,就可以顺藤摸瓜找到其它的类了。可见classloader本身"明码"方式仍然造成一定的不安全性,然而,如果该方法解决了classloader本身的安全性,其不失为一个比较好安全方案。

    第二章 classloader加密方式改进

    java程序是通过java.exe/javaw.exe来启动的,要对classloader进行解密处理,只能从java.exe/javaw.exe身上着手。

    我们先来考察一下jdk的发布路径, 发现jdk的每一个版本都提供了src.jar,用winzip打开看看, 可以看到一个launcher的路径,里面包含的就是java.exe/javaw.exe的程序代码。哈哈, 这下我们可以随心所欲了。:-)打开java.c看看,里面有一段, 如下:

    jstring mainclassname = getmainclassname(env, jarfile); if ((*env)->exceptionoccurred(env)) { (*env)->exceptiondescribe(env); goto leave; } if (mainclassname == null) { fprintf(stderr, "failed to load main-class manifest attribute " "from\n%s\n", jarfile); goto leave; } classname = (char *)(*env)->getstringutfchars(env, mainclassname, 0); if (classname == null) { (*env)->exceptiondescribe(env); goto leave; } mainclass = loadclass(env, classname); (*env)->releasestringutfchars(env, mainclassname, classname); } else { mainclass = loadclass(env, classname); } if (mainclass == null) { (*env)->exceptiondescribe(env); status = 4; goto leave; }

    其中,函数loadclass见下:

    static jclass loadclass(jnienv *env, char *name) { char *buf = memalloc(strlen(name) + 1); char *s = buf, *t = name, c; jclass cls; jlong start, end; if (debug) start = counterget(); do { c = *t++; *s++ = (c == .) ? / : c; } while (c != \0); cls = (*env)->findclass(env, buf); free(buf); if (debug) { end = counterget(); printf("%ld micro seconds to load main class\n", (jint)counter2micros(end-start)); printf("----_java_launcher_debug----\n"); } return cls; }

    分析上面的程序,我们可以看到env中的函数findclass根据类名直接得到mainclass对象的。如果我们要装载已加密过的java程序, 显然直接调用findclass函数是不行的,那么,我们有没有办法自己读取文件,然后将之转换成一个mainclass对象呢?

    我们来看看jnienv里面还有什么?打开jdk路径\include\jni.h, 在里面我们查到下列定义:

    #ifdef __cplusplus typedef jnienv_ jnienv; #else typedef const struct jninativeinterface_ *jnienv; #endif

    而在jninativeinterface_的定义中:

    struct jninativeinterface_ { …… jclass (jnicall *defineclass) (jnienv *env, const char *name, jobject loader, const jbyte *buf, jsize len); …… }

    对了,defineclass就是我们要找的,它可以将一个缓冲区(class字节码)转换成一个类实例!下面就是一个实现如何装载加密class:

    static jclass loadclass(jnienv *env, char *name) { file *in; long length, i; char *cc; int x; char javaloader [maxpathlen], javapath[maxpathlen]; char *buf = memalloc(strlen(name) + 1); char *s = buf, *t = name, c; jclass cls; jlong start, end; if (debug) start = counterget(); do { c = *t++; *s++ = (c == .) ? / : c; } while (c != \0); /*如果装载的类是myloader*/ if(strcmp(buf,"myloader")==0) { if (getapplicationhome(javapath, sizeof(javapath))) { sprintf(javaloader, "%s\\myloader.class", javapath); } if ((in = fopen(javaloader, "rb")) == null) { fprintf(stderr, "cannot open input file.\n"); return (jclass)0x0f; } /*读出加密的class文件*/ fseek(in, 0l, seek_end); length = ftell(in); fseek(in, 0, seek_set); cc = memalloc(length); fread((void*)cc,length,1,in); fclose(in); /*解密算法*/ …… /*将解密后的class字节码转换成class*/ cls = (*env)->defineclass(env, buf, 0, cc, length-1); free(cc); } else cls = (*env)->findclass(env, buf); free(buf); if (debug) { end = counterget(); printf("%ld micro seconds to load main class\n", (jint)counter2micros(end-start)); printf("----_java_launcher_debug----\n"); } return cls; }

    第三章 应用范例

    在实际应用中,建议新的启动程序继续采用java.exe的参数调用格式, 即java [-options] class [args...],这样的话,一方面程序在开发版本(非加密)和发布版本(加密)时的调用方式就保持一致了,便于别人的理解,另一方面启动程序的制作也简单多了,只需改动java.c中的loadclass方法了。

    下面是一般应用的示意图:

    如果调用的方式是这样的:class1调用class2,而由class2调用class3,其中class2有自己定制的classloader(非class3所用的classloader),则这时应该在class2和class3之间加一层interface,由interface调用class3相应的classloader来装载class3, 而interface本身则不能加密。这种形式的典型应用是tomcat上的web应用,tomcat装载servlet类时,是采用自己的classloader来装载的, 如果对servlet加密,tomcat则在装载servlet时不会装载成功,必须采用interface的方式!下面则是其应用示意图:

    第四章 应用范围

    由于解密需要一定的时间,如果不加区分的全部进行加密处理,势必会影响到程序的速度和响应。所以应该在需要加密的地方才加密,比方说,用户密码验证,专利算法,或者是数据库密码等等,这样的才不会导致系统的性能下降。

    要达到以上目的, classloader必须对class加以判断,非加密的class调用jvm系统classloader的loadclass函数, 而对加密的才加以解密处理。建议:classloader最好可配置!

    关于作者



     

     
    热门推荐笔记本: IBM笔记本
    相关文章:
    笔记本相关:
    IT技术文章:
    webmaster:popbb@126.com   最佳浏览:1024X768 MSIE
    ©2007 popbb.net All Rights Reserved