willygreen2 2015-07-07 06:33 采纳率: 0%
浏览 2183

tomcat自定义classloader

对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();
          }

      /**

      • 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 保存代码闪退