javaee加密,tomcat使用自己的classloader解密
来源:互联网 发布:sql注入布尔型攻击 编辑:程序博客网 时间:2024/05/19 03:43
【起因】
公司需要对一个web项目进行加密之后出售,
大家都知道的,class很好反编译,
所以需要对class文件先进行加密,
然后使用自己的classloader进行解密并加载。
【步骤】
大概分两步:
1.对class文件进行加密
2.写解密class文件并加载的classloader
3.将这个classloader加入到tomcat中,也就是使tomcat可以调用到这个classloader
【加密】
1.思路
字节流读取class文件,进行简单的移位
2.实现
做了一个小程序,实现了对某文件夹下所有class文件字节流读取,并+2位的加密方式
3.说明
swing是使用myeclipse的插件做的,可能比较乱
4.代码&下载
源代码和程序打包成jar文件上传到了这里,双击可以使用。
【classloader】
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中添加以下这段代码:
<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使用
4.加密和解密两个程序:加密,解密
【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;}@Overridepublic 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】
经过中午的挣扎,这个问题终于解决了,
详情:http://blog.csdn.net/uikoo9/article/details/17281403
- javaee加密,tomcat使用自己的classloader解密
- javaee加密部署,tomcat使用自己的classloader解密【正解】
- javaee加密部署,tomcat使用自己的classloader解密
- Tomcat自定义classLoader加密解密
- javaee加密部署项目通过tomcat使用自定义的classload解密
- Java加密部署,使用自定义的classloader解密的方法实例
- 编写自己的classloader加载加密过的class
- RSA加密解密的使用!
- 使用自己的ClassLoader实现热替换
- 使用自己的ClassLoader实现热替换
- 使用自己的ClassLoader实现热替换
- java源程序加密解决方案(基于Classloader解密)
- 自定义ClassLoader对Class加密并解密
- ClassLoader与Tomcat的ClassLoader
- 使用AES+自己算法设计加密解密算法
- 自己加密并解密数据
- Tomcat的classloader
- tomcat的classloader机制
- struts2标签库无法运行
- “Borland license information was found,but it is not valid for delphi.”
- STL源码剖析之map set multimap multiset【2013.12.10】
- ArcGIS WebAPI接入google瓦片服务
- How to Compile Linux Kernel from Source to Build Custom Kernel
- javaee加密,tomcat使用自己的classloader解密
- 同样的sql传入的条件不同执行效率相差百倍的原因
- [IE兼容性]
- hibernate使用hql 查询
- Java 解析 XML 文件之 DOM 解析
- Robotium_易测云生成的自动化测试脚本(5)
- Mongodb architecture
- Hql语句注意事项总结 批量删除 批量查询
- objective-C中的"非正式协议"和“正式协议”