从头到尾保护 JAVA

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

0 评论:

胡宪利 (samenhu@yahoo.com.cn)中兴通讯SoftSwitch产品部

2002 年 6 月 30 日

  • 内容

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

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

原文:http://www.ibm.com/developerworks/cn/java/l-protectjava/

相关文章:

如何利用DES加密的算法保护Java源代码

http://security.ctocio.com.cn/tips/42/7728542.shtml

如何有效的保护 JAVA 程序的更多相关文章

  1. 如何保护java程序不被反编译

    Java是一种 跨平台的.解释型语言 Java 源代码编译中间“字节码”存储于class文件中.Class文件是一种字节码形式的中间代码,该字节码中包括了很多源代码的信息,例如变量名.方法名 等.因此 ...

  2. Java程序版权保护解决方案

    通常C.C++等编程语言开发的程序都被编译成目标代码,这些目标代码都是本机器的二进制可执行代码.通常所有的源文件被编译.链接成一个可执行文件.在这些可执行文件中,编译器删除了程序中的变量名称.方法名称 ...

  3. 使用 Acegi 保护 Java 应用程序

    第 1 部分: 架构概览和安全过滤器 Acegi Security System 是一种功能强大并易于使用的替代性方案,使您不必再为 Java 企业应用程序编写大量的安全代码.虽然它专门针对使用 Sp ...

  4. 谈谈java程序代码保护及license设计

    理论上讲,不存在牢不可破的漏洞,只是时间和成本问题.通常我们认为的不可破解,说的是破解需要难以接受的时间和成本.对于java程序来说,class文件很容易被反编译,所以理论上而言,对java程序做li ...

  5. 编写高质量代码:改善Java程序的151个建议(第1章:JAVA开发中通用的方法和准则___建议16~20)

    建议16:易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP,Ruby,Groovy.Javascript等,这些入侵者都有一个共同特征:全是同一类语言-----脚本语言,它 ...

  6. Java程序员岗位

    Java程序员岗位面试题有哪些?   1.面向对象的特征有哪些方面(1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择 ...

  7. zabbix 监控java程序

    http://www.tuicool.com/articles/IRnM7f http://transcoder.baidu.com/from=1012852q/bd_page_type=1/ssid ...

  8. 运用加密技术保护Java源代码/定制ClassLoader

    为什么要加密? 对于传统的C或C++之类的语言来说,要在Web上保护源代码是很容易的,只要不发布它就可以.遗憾的是,Java程序的源代码很容易被别人偷看.只要有一个反编译器,任何人都可以分析别人的代码 ...

  9. (四)java程序基本组成

    一个基本的java程序一般包括几个部分,分别是程序所在的包名.程序中用到的其他包的路径.程序的类.类中的方法.变量和字面量. package demo; import java.util.Date; ...

随机推荐

  1. pyqt5界面与逻辑分离--信号槽的装饰器实现方式

    本文展示了 pyqt5 信号槽的装饰器实现方式(借鉴自 eirc6) 一个简单的例子.实现功能:两个数相加,显示结果.如图 两个文件,第一个是界面文件 ui_calc.py # ui_calc.py ...

  2. 说说C#中IList与List区别

    首先IList 泛型接口是 ICollection 泛型接口的子代,并且是所有泛型列表的基接口. 它仅仅是所有泛型类型的接口,并没有太多方法可以方便实用,如果仅仅是作为集合数据的承载体,确实,ILis ...

  3. 千万不要在JS中使用连等赋值操作

    前言 文章标题这句话原本是在国外某JavaScript规范里看到的,当时并没有引起足够的重视,直到最近一次出现了bug发现JS里的连等赋值操作的特色(坑). 网上搜索一番发现一个非常好的连等赋值的(来 ...

  4. Asp.net MVC在View里动态捆绑压缩引用的js

    前言 Asp.net MVC 4以上版本多了BundleConfig.RegisterBundles方法,可以把要捆绑的脚本或样式进行捆绑压缩,以减少客户端的请求次数从而提高了客户端的访问速度. 问题 ...

  5. OSX 上安装 Scrapy 的那些坑

    Scrapy 这个爬网框架真心不错,但在OSX上安装总是会出现各种的问题,在这里就作一个收集汇总.我的系统环境是 OS X El Capitan (10.11.1) 首先要保证 pip , virtu ...

  6. Fiddler工具的基本功能

    Fiddler是一款用于网页数据分析,抓取的工具,里面集成了对网页强大的功能外,还可以通过设置,使其对手机的数据也可以进行抓取 Fiddler的原理是: 通过在客户端和服务器之间创建一个代理服务器来对 ...

  7. [USACO2005][POJ2228]Naptime(对特殊环状DP的处理)

    题目:http://poj.org/problem?id=2228 题意:将一天分为N小时,每小时都有一个价值w,有一头牛要睡觉,而它的睡觉是连续的,且第一小时不能算价值,即如果你睡了[a,b],则你 ...

  8. 第一章 : javaScript框架分类及主要功能

      从内部架构和理念划分,目前JavaScript框架可以划分为5类. 第一种是以命名空间为导向的类库或框架,如果创建一个数组用new Array(),生成一个对象用new Object(),完全的j ...

  9. CreateCompatibleDC与BitBlt 学习

    CreateCompatibleDC与BitBlt CreateCompatibleDC 创建一个与指定设备一致的内存设备描述表. HDC CreateCompatibleDC(HDC hdc //设 ...

  10. MVC2 Area实现网站多级目录

    Areas是ASP.NET Mvc 2.0版本中引入的众多新特性之一,它可以帮你把一个较大型的Web项目分成若干组成部分,即Area.实现Area的功能可以有两个组织形式: 在1个ASP.NET Mv ...