第二章 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;
}