Tomcat自定义classLoader加密解密
class很好反编译,所以需要对class文件先进行加密,然后使用自己的classloader进行解密并加载。
【步骤】
大概分两步:
1.对class文件进行加密
2.写解密class文件并加载的classloader
3.将这个classloader加入到tomcat中,也就是使tomcat可以调用到这个classloader
【加密】
1.思路
字节流读取class文件,进行简单的移位
2.实现
做了一个小程序,实现了对某文件夹下所有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
Tomcat自定义classLoader加密解密的更多相关文章
- SpringBoot自定义classloader加密保护class文件
背景 最近针对公司框架进行关键业务代码进行加密处理,防止通过jd-gui等反编译工具能够轻松还原工程代码,相关混淆方案配置使用比较复杂且针对springboot项目问题较多,所以针对class文件加密 ...
- 推荐分享一个牛X的自定义PHP加密解密类
通俗点说,用它来进行加密,同一个字符串,每次进行加密,得出的结果都是不一样的,大大加强了数据安全性.同时还可设定加密后数据的有效期,简直牛掰了 #食用方法 将下面的第二份模块代码保存为 Mcrypt. ...
- Net Core 自定义 Middleware 加密解密
前言:第一次写文章,有问题请轻喷 当前使用 Net Core 版本 2.1.3 我们经常在开发中需要把实体的主键 Id 传输到前端,但是在Get的时候又不想让前端能看到明文,我们通常会加密这些数据,所 ...
- 解决自定义classloader后无法使用maven install
@上篇博客中探讨了web项目利用自定义classloader进行解密,利用的是编译后的文件直接运行程序一切正常 今天博主在探讨加密后进行混淆时,打包程序报程序包org.apache.catalina. ...
- PHP DES加密解密
自定义密码加密解密函数,源自网友,记录保存一下. <?php /** * DES加密解密 */ class Mcrypt{ public function __construct(){} fun ...
- JAVA 利用JNI加密class文件/自定义ClassLoader 类
利用 JNI 对bytecode 加密.不影响java程序员的正常开发.09年的时候写的,现在拿出来晒晒————————————————————————————混淆才是王道,如果混淆再加密就更酷了.. ...
- Laravel之加密解密/日志/异常处理及自定义错误
一.加密解密 1.加密Crypt::encrypt($request->secret) 2.解密try { $decrypted = Crypt::decrypt($encryptedValue ...
- 【转】asp.net(c#)加密解密算法之sha1、md5、des、aes实现源码详解
原文地址:http://docode.top/Article/Detail/10003 目录: 1..Net(C#)平台下Des加密解密源代码 2..Net(C#)平台下Aes加密解密源代码 3..N ...
- 基于正则的INI读写工具类,支持加密解密
看到这个标题,有人会问,现在都用xml做配置文件了,谁还用INI文件啊!下面来简单对比一下xml和ini: 1.XML功能强大表达能力强,同时扩展性好. 2.它的主要优势是异构平台的整合.通讯. 3. ...
随机推荐
- SpringMVC学习(一):搭建SpringMVC-注解-非注解
文章参考:http://www.cnblogs.com/Sinte-Beuve/p/5730553.html 一.环境搭建: 目录结构: 引用的JAR包: 如果是Maven搭建的话pom.xml配置依 ...
- HTML marquee标签
marquee语法 <marquee></marquee> 实例一<marquee>Hello, World</marquee> marquee常 ...
- HTML5即将迎来黄金时代 轻应用再成行业焦点
2015-01-23 11:03:09 来源:快鲤鱼 大众能看到的H5效果拜“微信”所赐,几乎每天都有H5页面的推广以及H5小游戏在微信上传播.其实,H5的大热与百度不无关系,2012年开始, ...
- 【上】安全HTTPS-全面具体解释对称加密,非对称加密,数字签名,数字证书和HTTPS
一,对称加密 所谓对称加密.就是它们在编码时使用的密钥e和解码时一样d(e=d),我们就将其统称为密钥k. 对称加解密的步骤例如以下: 发送端和接收端首先要共享同样的密钥k(即通信前两方都须要知道相应 ...
- 多线程(C++)临界区Critical Sections
一 .Critical Sections(功能与Mutex相同,保证某一时刻只有一个线程能够访问共享资源,但是不是内核对象,所以访问速度比Mutex快,但是没有等待超时的功能,所以有可能导致死锁,使用 ...
- python2&python3的区别
区别1. python3中>>>range<3,6>range<3,6> python2中>>>range<3,6>[3,4,5 ...
- xorm
https://github.com/go-xorm/xorm Simple and Powerful ORM for Go, support mysql,postgres,tidb,sqlite3, ...
- JSOI2004 平衡点 / 吊打XXX
题目描述 有n个重物,每个重物系在一条足够长的绳子上.每条绳子自上而下穿过桌面上的洞,然后系在一起.图中X处就是公共的绳结.假设绳子是完全弹性的(不会造成能量损失),桌子足够高(因而重物不会垂到地上) ...
- Linux常用命令——持续更新(2018-05-09)
此命令默认是在centos环境下执行,除非特殊标明. 1.查看ip: ifconfig 2.创建指定用户并分配到某个组:创建用户user并分配到root组 useradd -g root user 3 ...
- javascript Date对象的介绍及linux时间戳如何在javascript中转化成标准时间格式
1.Date对象介绍 Date对象具有多种构造函数.new Date()new Date(milliseconds)new Date(datestring)new Date(year, month)n ...