互联网博客
http://internet.blog.enorth.com.cn >复制 >收藏本站
首页 IT技术(99) 音视频(7) 汽车(6) 搜索引擎(2) 我的日志(34) 歌词(12) James(19) 笑话(10) Blog技术(4) 诗词歌赋(7)

北方博客 > 首页 > 如何有效的保护JAVA程序
Google
3上一篇: 使用混淆器,保护你的java程序 下一篇: 中国十大穷光蛋排行榜! 4
[IT技术]如何有效的保护JAVA程序
Tags: obfuscator java 模糊 jode 混淆

从头到尾保护JAVA

级别: 初级

胡宪利中兴通讯SoftSwitch产品部

2002 年 6 月 30 日

目前关于JAVA程序的加密方式不外乎JAVA模糊处理(Obfuscator)和运用ClassLoader方法进行加密处理这两种方式(其他的方式亦有,但大多是这两种的延伸和变异)。这两种方式不管给JAVA反编译器造成多少困难, 毕竟还是有迹可寻,有机可乘的。本文介绍的方法是对ClassLoader方式加密处理的一种改进,使之达到传统二进制程序代码安全。

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

关于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最好可配置!









关于作者

胡宪利,具有将近五年的GUI和数据库,Web应用等编程经验,使用语言十分广泛,包括为C,C++和Java 等编程语言,开发工具包括VC,CBuilder,JBuilder等, 数据库包括SqlServer,Oracle,开发过进销存,会计等MIS系统。目前在中兴通讯SoftSwitch产品部工作,从事数据库系统的开发,可通过 samenhu@yahoo.com.cn联系。



字体:
引用地址 http://internet.blog.enorth.com.cn/article/22660.shtml


allenzz7 | 2006-02-07 16:26


发表评论
 
昵称
主页
标题
内容
算式的解
看不清校验算式?
 



 


公告

抗震救灾

众志成城

站内搜索

最新日志

linux 中文显示乱码解决办法
国足欢迎你
A780的快捷键图标设置
Jfreechart中画双Y坐标的折线图
在dwr的回调函数中传递多个参数
Lemon Tree
去掉jsessionid
Windows media encoder及其SDK编程说明
保留weblogic中jsp编译后生成的java文件
行为艺术家扮“房奴”抗议房产商(图)
DWR 调用返回值方法
民间节油“秘笈” “节油王”道出节油六高招
开车十五年心得!真真实实的26条教训!
ChannelSocket INFO: connection timeout reached
杭州干部徐新贤死了都要爱!
创业前请回答这些问题
Linux下让编译安装的Apache自动启动
IllegalStateException: getOutputStream() 异常的解决方案
LINUX下配置系统环境变量
Servlet图像处理 X11错误解决方法
解决winxp搜索不到文件中的某个字或词组的问题
夜的第七章
Linux下两种自动启动Tomcat的方法
使用mod_usertrack跟踪用户
apache2.0 prefork和worker模块性能调优

网友评论

Re:Apache-WebLogic plugin插件的安装
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个简单的DWR例子
Re:一个女人的十年 [录音+文字]
Re:一组照片
Re:一组照片
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:apache2.0 prefork和worker模块性能调优
Re:蒋捷《虞美人·少年听雨歌楼上》
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]
Re:一个女人的十年 [录音+文字]

我的链接

妮妮的世界
小宝成长日记
汽车维修、保养专业站

CSS2中文手册
SpringFramework中文论坛
以前的事 现在的事 将来的事
DynamicDrive
CSDN 金·评·没|YanHui's blog
tomcat 5.0 文档
sixsun的blog
王育琨专栏
西安信息资源网
[埃里克森]的Blog



flashplayer.cn

Apache HTTP Server Version 2.2 文档
天津一汽
Tortoise Cvs Homepage
天津博客
Java开源大全
OpenSymphony
Java视线论坛

网友留言

<写留言>
我们的感觉好比一个十年的女人
你的博客做得很棒
认识你好高兴
其实我也是学计算机的
你好,朋友!
你是不是搞程序的?
交朋识有
祝你新年快乐!
经常关注你的blog,希望有机会认识一下!
元旦快乐

站内统计

日志总数:200
今日访问:1289
访问总数:1145155
评论总数:324
留言总数:14

存档

2008年01月(1)
2007年12月(3)
2007年11月(2)
2007年10月(1)
2007年08月(2)
2007年07月(1)
2007年06月(3)
2007年05月(2)
2007年04月(5)
2007年03月(2)
2007年01月(2)
2006年12月(3)
2006年11月(1)
2006年09月(5)
2006年08月(2)
2006年07月(8)
2006年06月(7)
2006年05月(9)
2006年04月(13)
2006年03月(30)
2006年02月(23)
2006年01月(9)
2005年12月(12)
2005年11月(23)
2005年10月(23)
2005年09月(2)

控制台入口

用户名
密 码