对class文件加密,然后自定义classloader,tomcat启动时调用该classloader加载加密的class文件,最好有详细代码
1条回答
- u010258525 2015-07-07 08:15关注
大概分两步:
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;
/**
- 自己的ClassLoader
- 用于解密加密过的class文件并加载
-
@author uikoo9
*/
public class MyClassLoader extends WebappClassLoader{/**
- 默认构造器
*/
public MyClassLoader() {
super();
}
/**
- 默认构造器
- @param parent
*/
public MyClassLoader(ClassLoader parent) {
super(parent);
}
/* (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;
}
/**
- 简单的解密
- @param name
- @return
-
@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上查看代码片派生到我的代码片
/*- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
*/
package org.apache.tomcat.util.bcel.classfile;
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;
/**
- Wrapper class that parses a given Java .class file. The method <A
- href ="#parse">parse returns a
- JavaClass object on success. When an I/O error or an
- inconsistency occurs an appropiate exception is propagated back to
- the caller.
- The structure and the names comply, except for a few conveniences,
- exactly with the
- JVM specification 1.0. See this paper for
- further details about the structure of a bytecode file.
- @version $Id: ClassParser.java 992409 2010-09-03 18:35:59Z markt $
-
@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;/**
- Parse class from the given stream.
- @param file Input stream
- @param file_name File name
*/
public ClassParser(InputStream file, String file_name) {
this.file_name = file_name;
fileOwned = false;
String clazz = file.getClass().getName(); // Not a very clean solution ...
is_zip = clazz.startsWith("java.util.zip.") || clazz.startsWith("java.util.jar.");
if (file instanceof DataInputStream) {
this.file = (DataInputStream) file;
} else {
this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE));
}
}
/**
- Parse the given Java class file and return an object that represents
- the contained data, i.e., constants, methods, fields and commands.
- A ClassFormatException is raised, if the file is not a valid
- .class file. (This does not include verification of the byte code as it
- is performed by the java interpreter).
- @return Class object representing the parsed class file
- @throws IOException
-
@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);
}
/**
- Read information about the attributes of the class.
- @throws IOException
- @throws ClassFormatException
*/
private final void readAttributes() throws IOException, ClassFormatException {
int attributes_count;
attributes_count = file.readUnsignedShort();
attributes = new Attribute[attributes_count];
for (int i = 0; i < attributes_count; i++) {
attributes[i] = Attribute.readAttribute(file, constant_pool);
}
}
/**
- Read information about the class and its super class.
- @throws IOException
- @throws ClassFormatException
/
private final void readClassInfo() throws IOException, ClassFormatException {
access_flags = file.readUnsignedShort();
/ Interfaces are implicitely abstract, the flag should be set- according to the JVM specification.
*/
if ((access_flags & Constants.ACC_INTERFACE) != 0) {
access_flags |= Constants.ACC_ABSTRACT;
}
if (((access_flags & Constants.ACC_ABSTRACT) != 0)
&& ((access_flags & Constants.ACC_FINAL) != 0)) {
throw new ClassFormatException("Class " + file_name + " can't be both final and abstract");
}
class_name_index = file.readUnsignedShort();
superclass_name_index = file.readUnsignedShort();
}
- according to the JVM specification.
*/
/**
- Read constant pool entries.
- @throws IOException
- @throws ClassFormatException
*/
private final void readConstantPool() throws IOException, ClassFormatException {
constant_pool = new ConstantPool(file);
}
/**
- Read information about the fields of the class, i.e., its variables.
- @throws IOException
- @throws ClassFormatException
*/
private final void readFields() throws IOException, ClassFormatException {
int fields_count;
fields_count = file.readUnsignedShort();
fields = new Field[fields_count];
for (int i = 0; i < fields_count; i++) {
fields[i] = new Field(file, constant_pool);
}
}
/******************** Private utility methods **********************/
/**- Check whether the header of the file is ok.
- Of course, this has to be the first action on successive file reads.
- @throws IOException
- @throws ClassFormatException
*/
private final void readID() throws IOException, ClassFormatException {
int magic = 0xCAFEBABE;
if (file.readInt() != magic) {
throw new ClassFormatException(file_name + " is not a Java .class file");
}
}
/**
- Read information about the interfaces implemented by this class.
- @throws IOException
- @throws ClassFormatException
*/
private final void readInterfaces() throws IOException, ClassFormatException {
int interfaces_count;
interfaces_count = file.readUnsignedShort();
interfaces = new int[interfaces_count];
for (int i = 0; i < interfaces_count; i++) {
interfaces[i] = file.readUnsignedShort();
}
}
/**
- Read information about the methods of the class.
- @throws IOException
- @throws ClassFormatException
*/
private final void readMethods() throws IOException, ClassFormatException {
int methods_count;
methods_count = file.readUnsignedShort();
methods = new Method[methods_count];
for (int i = 0; i < methods_count; i++) {
methods[i] = new Method(file, constant_pool);
}
}
/**
- Read major and minor version of compiler which created the file.
- @throws IOException
- @throws ClassFormatException
*/
private final void readVersion() throws IOException, ClassFormatException {
minor = file.readUnsignedShort();
major = file.readUnsignedShort();
}
}
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;
/**
- 自定义的classloader
- 可以解密文件并加载
-
@author uikoo9
*/
public class UClassLoader extends WebappClassLoader{/**
- 默认构造器
*/
public UClassLoader() {
super();
}
/**
- 默认构造器
- @param parent
*/
public UClassLoader(ClassLoader parent) {
super(parent);
}
/* (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);
}
}/**
- 加载加密后的class字节流
- @param name
- @return
- @throws IOException
*/
private byte[] loadClassBytesEncrypt(String name) throws IOException {
String cname = name.replace('.', '/') + ".uikoo9";
FileInputStream in = null;
in = new FileInputStream(cname);
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int ch;
while ((ch = in.read()) != -1) {
buffer.write((byte)(ch - 2));
}
in.close();
return buffer.toByteArray();
} finally {
in.close();
}
}
/**
- 加载普通的class字节流
- @param name
- @return
- @throws IOException
*/
private byte[] loadClassBytesDefault(String name) throws IOException {
String cname = name.replace('.', '/') + ".class";
FileInputStream in = null;
in = new FileInputStream(cname);
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int ch;
while ((ch = in.read()) != -1) {
buffer.write((byte)ch);
}
in.close();
return buffer.toByteArray();
} finally {
in.close();
}
}
}
- 默认构造器
*/
2.delegate="false"时,启动tomcat:
[html] view plaincopy在CODE上查看代码片派生到我的代码片3.delegate="true"时,启动tomcat:
[html] view plaincopy在CODE上查看代码片派生到我的代码片4.总结
delegate为true的时候自定义的loader只用来加载自己的代码
解决 无用评论 打赏 举报
悬赏问题
- ¥15 fluent的在模拟压强时使用希望得到一些建议
- ¥15 STM32驱动继电器
- ¥15 Windows server update services
- ¥15 关于#c语言#的问题:我现在在做一个墨水屏设计,2.9英寸的小屏怎么换4.2英寸大屏
- ¥15 模糊pid与pid仿真结果几乎一样
- ¥15 java的GUI的运用
- ¥15 Web.config连不上数据库
- ¥15 我想付费需要AKM公司DSP开发资料及相关开发。
- ¥15 怎么配置广告联盟瀑布流
- ¥15 Rstudio 保存代码闪退