对class文件加密,然后自定义classloader,tomcat启动时调用该classloader加载加密的class文件,最好有详细代码
大概分两步:
1.对class文件进行加密
2.写解密class文件并加载的classloader
3.将这个classloader加入到tomcat中,也就是使tomcat可以调用到这个classloader
【加密】
1.思路
字节流读取class文件,进行简单的移位
2.实现
做了一个小程序,实现了对某文件夹下所有class文件字节流读取,并+2位的加密方式
3.说明
swing是使用myeclipse的插件做的,可能比较乱
4.代码&下载
源代码和程序打包成jar文件上传到了这里,双击可以使用。
【classloader】
[java] view plaincopy在CODE上查看代码片派生到我的代码片
package com.uikoo9;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.catalina.loader.WebappClassLoader;
/**
@author uikoo9
*/
public class MyClassLoader extends WebappClassLoader{
/**
/**
/* (non-Javadoc)
@see org.apache.catalina.loader.WebappClassLoader#findClass(java.lang.String)
*/
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = null;
try {
classBytes = loadClassBytes(name);
} catch (Exception e) {
throw new ClassNotFoundException(name);
}
Class<?> cl= defineClass(name, classBytes, 0, classBytes.length);
if(cl == null) throw new ClassNotFoundException(name);
return cl;
}
/**
@throws IOException
*/
private byte[] loadClassBytes(String name) throws IOException{
String cname = name.replace('.', '/') + ".class";
FileInputStream in = new FileInputStream(cname);
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int ch;
while((ch = in.read()) != -1){
if(cname.contains("uikoo9")){// 如果包含uikoo9说明是自己写的class,进行解密
System.out.println("++");
buffer.write((byte)(ch-2));
}else{
buffer.write((byte)ch);
}
}
in.close();
return buffer.toByteArray();
}finally{
in.close();
}
}
}
【加入到tomcat中】
1.网上
网上很多文章都问到tomcat怎么使用自己的classloader,但是说明白的几乎没有,
最后自己读了tomcat官网的文档,找到了答案,
地址:http://tomcat.apache.org/tomcat-6.0-doc/config/loader.html
2.方法
说简单点,就是在tomcat\conf\context.xml中添加以下这段代码:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
3.classloader
但是注意,这里的com.uikoo9.MyClassLoader并不是项目中的,
而是需要放到tomcat\lib下。
【新的问题】
1.这个自己写的classloader确实起作用的,但是问题也随之而来,
原来tomcat在调用classloader之前会调用一个自己的classparser类来对class文件进行解析
2.classparser
位于org\apache\tomcat\util\bcel\classfile下的ClassParser.java,
源代码:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
/*
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.tomcat.util.bcel.Constants;
/**
@author M. Dahm
*/
public final class ClassParser {
private DataInputStream file;
private boolean fileOwned;
private String file_name;
private String zip_file;
private int class_name_index, superclass_name_index;
private int major, minor; // Compiler version
private int access_flags; // Access rights of parsed class
private int[] interfaces; // Names of implemented interfaces
private ConstantPool constant_pool; // collection of constants
private Field[] fields; // class fields, i.e., its variables
private Method[] methods; // methods defined in the class
private Attribute[] attributes; // attributes defined in the class
private boolean is_zip; // Loaded from zip file
private static final int BUFSIZE = 8192;
/**
/**
@throws ClassFormatException
*/
public JavaClass parse() throws IOException, ClassFormatException {
ZipFile zip = null;
try {
if (fileOwned) {
if (is_zip) {
zip = new ZipFile(zip_file);
ZipEntry entry = zip.getEntry(file_name);
if (entry == null) {
throw new IOException("File " + file_name + " not found");
}
file = new DataInputStream(new BufferedInputStream(zip.getInputStream(entry),
BUFSIZE));
} else {
file = new DataInputStream(new BufferedInputStream(new FileInputStream(
file_name), BUFSIZE));
}
}
/****************** Read headers ********************************/
// Check magic tag of class file
readID();
// Get compiler version
readVersion();
/****************** Read constant pool and related **************/
// Read constant pool entries
readConstantPool();
// Get class information
readClassInfo();
// Get interface information, i.e., implemented interfaces
readInterfaces();
/****************** Read class fields and methods ***************/
// Read class fields, i.e., the variables of the class
readFields();
// Read class methods, i.e., the functions in the class
readMethods();
// Read class attributes
readAttributes();
// Check for unknown variables
//Unknown[] u = Unknown.getUnknownAttributes();
//for(int i=0; i < u.length; i++)
// System.err.println("WARNING: " + u[i]);
// Everything should have been read now
// if(file.available() > 0) {
// int bytes = file.available();
// byte[] buf = new byte[bytes];
// file.read(buf);
// if(!(is_zip && (buf.length == 1))) {
// System.err.println("WARNING: Trailing garbage at end of " + file_name);
// System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf));
// }
// }
} finally {
// Read everything of interest, so close the file
if (fileOwned) {
try {
if (file != null) {
file.close();
}
if (zip != null) {
zip.close();
}
} catch (IOException ioe) {
//ignore close exceptions
}
}
}
// Return the information we have gathered in a new object
return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor,
access_flags, constant_pool, interfaces, fields, methods, attributes);
}
/**
/**
/**
/**
/******************** Private utility methods **********************/
/**
/**
/**
/**
3.问题
发现这个解析类的文件会先去判断class的头信息来确定是不是class文件,
但是由于我们对class进行了加密,所以头信息变了,所以这个解析class文件的类会报错,
也就不会调用到classloader了。
【继续】
文章有点长,不知道有人有耐心看完不。
1.上面的问题折腾了一天,才发现是自己解密的部分有问题,
2.不过也是有收获的,发现自定写的loader只能加载非class的文件,而不能加载class
3.意思就是说,你需要将原来的class文件加密并改变文件后缀,然后配合自己的loader使用
4.加密和解密两个程序:加密,解密
【delegate】
由于自己英语水平有限,所以之前的tomcat文章一知半解,
通过今天的研究发现context.xml中的delegate属性的用法。
1.loader的代码:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
package com.uikoo9.loader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.catalina.loader.WebappClassLoader;
/**
@author uikoo9
*/
public class UClassLoader extends WebappClassLoader{
/**
/**
/* (non-Javadoc)
@see org.apache.catalina.loader.WebappClassLoader#findClass(java.lang.String)
*/
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = null;
try {
if(name.contains("uikoo9")){
System.out.println("++++++" + name);
classBytes = loadClassBytesEncrypt(name);
}else{
System.out.println("-------" + name);
classBytes = loadClassBytesDefault(name);
}
} catch (Exception e) {
e.printStackTrace();
}
Class<?> cl = defineClass(name, classBytes, 0, classBytes.length);
if (cl == null)
throw new ClassNotFoundException(name);
return cl;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(name.contains("uikoo9")){
return findClass(name);
}else{
return super.loadClass(name);
}
}
/**
/**
2.delegate="false"时,启动tomcat:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
3.delegate="true"时,启动tomcat:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
4.总结
delegate为true的时候自定义的loader只用来加载自己的代码