Tomcat自定義classLoader加密解密

class很好反編譯,因此須要對class文件先進行加密,而後使用本身的classloader進行解密並加載。html

【步驟】java

大概分兩步:spring

1.對class文件進行加密express

2.寫解密class文件並加載的classloaderapache

3.將這個classloader加入到tomcat中,也就是使tomcat能夠調用到這個classloader小程序

 

【加密】tomcat

1.思路app

字節流讀取class文件,進行簡單的移位less

2.實現eclipse

作了一個小程序,實現了對某文件夾下全部class文件字節流讀取,並+2位的加密方式

3.說明

swing是使用myeclipse的插件作的,可能比較亂

 

【classloader】

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中添加如下這段代碼:

<Loader loaderClass="com.uikoo9.MyClassLoader"></Loader >  

3.classloader

可是注意,這裏的com.uikoo9.MyClassLoader並非項目中的,

而是須要放到tomcat\lib下。

 

【新的問題】

1.這個本身寫的classloader確實起做用的,可是問題也隨之而來,

原來tomcat在調用classloader以前會調用一個本身的classparser類來對class文件進行解析

2.classparser

位於org\apache\tomcat\util\bcel\classfile下的ClassParser.java,

源代碼:

/* 
 *  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</A> returns a <A href ="JavaClass.html"> 
 * JavaClass</A> 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 <A href="ftp://java.sun.com/docs/specs/vmspec.ps"> 
 * JVM specification 1.0</a>. 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 <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>  
 */  
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 <em>ClassFormatException</em> 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使用

【delegate】

因爲本身英語水平有限,因此以前的tomcat文章只知其一;不知其二,

經過今天的研究發現context.xml中的delegate屬性的用法。

1.loader的代碼:

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:

<Loader loaderClass="com.uikoo9.loader.UClassLoader" delegate="false"></Loader >  


3.delegate="true"時,啓動tomcat:

<Loader loaderClass="com.uikoo9.loader.UClassLoader" delegate="true"></Loader >  

4.總結

delegate爲true的時候自定義的loader只用來加載本身的代碼

 

【新問題】

以上的代碼整理一下,啓動tomcat,沒有報錯,

可是當點擊頁面的時候,也就是向後臺請求的時候依然報錯,

 

【end】

通過中午的掙扎,這個問題終於解決了,

注意,這個只適合沒有spring的,由於spring有本身的classloader。

【classloader】

 1.代碼: 

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 {  
        if(name.contains("uikoo9")){  
            return findClassEncrypt(name);  
        }else{  
            return super.findClass(name);  
        }  
    }  
      
    /** 
     * 查找class 
     * @param name 
     * @return 
     * @throws ClassNotFoundException 
     */  
    private Class<?> findClassEncrypt(String name) throws ClassNotFoundException{  
        byte[] classBytes = null;  
        try {  
            System.out.println("++++++" + name);  
            classBytes = loadClassBytesEncrypt(name);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
  
        Class<?> cl = defineClass(name, classBytes, 0, classBytes.length);  
        if (cl == null)  
            throw new ClassNotFoundException(name);  
        return cl;  
    }  
      
    /** 
     * 加載加密後的class字節流 
     * @param name 
     * @return 
     * @throws IOException 
     */  
    private byte[] loadClassBytesEncrypt(String name) throws IOException {  
        String basepath = "Z:/program/workspaces/_work_03_bzb/WebRoot/WEB-INF/classes/";// 項目物理地址  
        String cname = basepath + name.replace('.', '/') + ".uikoo9";  
        System.out.println(cname);  
        FileInputStream 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();  
        }  
    }  
}  

【加入到tomcat】

1.context.xml

找到tomcat下contex.xml,在context之間加入如下代碼:

<Loader loaderClass="com.uikoo9.loader.UClassLoader" delegate="true"></Loader>  

其中loaderClass就是本身寫loader,delegate=「true」的意思是隻解密非系統的class和jar

 

2.添加loader 

將本身寫的loader的class文件放到tomcat\lib下

  

【開始】

 1.使用加密程序對classes下全部文件加密,加密以後全部的class文件後綴變爲uikoo9,能夠本身修改源代碼

 2.將原classes文件夾刪除,將加密後的classes文件夾複製進去

 3.修改context.xml

 4.tomcat\lib下添加loader.class

 5.啓動tomcat


原文:http://blog.csdn.net/uikoo9/article/details/17281403

相關文章
相關標籤/搜索