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加密解密的更多相关文章

  1. SpringBoot自定义classloader加密保护class文件

    背景 最近针对公司框架进行关键业务代码进行加密处理,防止通过jd-gui等反编译工具能够轻松还原工程代码,相关混淆方案配置使用比较复杂且针对springboot项目问题较多,所以针对class文件加密 ...

  2. 推荐分享一个牛X的自定义PHP加密解密类

    通俗点说,用它来进行加密,同一个字符串,每次进行加密,得出的结果都是不一样的,大大加强了数据安全性.同时还可设定加密后数据的有效期,简直牛掰了 #食用方法 将下面的第二份模块代码保存为 Mcrypt. ...

  3. Net Core 自定义 Middleware 加密解密

    前言:第一次写文章,有问题请轻喷 当前使用 Net Core 版本 2.1.3 我们经常在开发中需要把实体的主键 Id 传输到前端,但是在Get的时候又不想让前端能看到明文,我们通常会加密这些数据,所 ...

  4. 解决自定义classloader后无法使用maven install

    @上篇博客中探讨了web项目利用自定义classloader进行解密,利用的是编译后的文件直接运行程序一切正常 今天博主在探讨加密后进行混淆时,打包程序报程序包org.apache.catalina. ...

  5. PHP DES加密解密

    自定义密码加密解密函数,源自网友,记录保存一下. <?php /** * DES加密解密 */ class Mcrypt{ public function __construct(){} fun ...

  6. JAVA 利用JNI加密class文件/自定义ClassLoader 类

    利用 JNI 对bytecode 加密.不影响java程序员的正常开发.09年的时候写的,现在拿出来晒晒————————————————————————————混淆才是王道,如果混淆再加密就更酷了.. ...

  7. Laravel之加密解密/日志/异常处理及自定义错误

    一.加密解密 1.加密Crypt::encrypt($request->secret) 2.解密try { $decrypted = Crypt::decrypt($encryptedValue ...

  8. 【转】asp.net(c#)加密解密算法之sha1、md5、des、aes实现源码详解

    原文地址:http://docode.top/Article/Detail/10003 目录: 1..Net(C#)平台下Des加密解密源代码 2..Net(C#)平台下Aes加密解密源代码 3..N ...

  9. 基于正则的INI读写工具类,支持加密解密

    看到这个标题,有人会问,现在都用xml做配置文件了,谁还用INI文件啊!下面来简单对比一下xml和ini: 1.XML功能强大表达能力强,同时扩展性好. 2.它的主要优势是异构平台的整合.通讯. 3. ...

随机推荐

  1. 服务管理-tomcat

    Tomcat是Apache软件基金会的Jakarta项目中的一个核心项目,由Apache,Sun和其他一些公司及个人共同开发而成.由于有了Sun的参与和支持,最新的Servlet和JSP规范总是能在T ...

  2. 虚拟网卡TUN/TAP 驱动程序设计原理

    昨天韦哥写了<Linux下Tun/Tap设备通信原理>一文,只提到了两个使用Tun的用户进程之间的通信路径,并没有说明Tun虚拟网卡驱动是如何实现的,而正好看到了这里的一篇讲解这方面的文章 ...

  3. .NET MVC 4 实现用户注册功能

    初学MVC,踩了不少坑,所以通过实现一个用户注册功能把近段时间学习到的知识梳理一遍,方便以后改进和查阅. 问题清单: l 为什么EF自动生成的表名后自动添加了s? l 如何为数据库初始化一些数据? l ...

  4. c# CacheManager 缓存管理

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. extjs中新建窗体时,给窗体添加背景图片不显示问题之一

    1.在extjs中新建窗体时,给窗体添加背景图片不显示,例如下面的代码. 不显示的原因:因为设置了  layout: 'fit', Ext.create('Ext.Window', { title: ...

  6. windows下使用ofstream默认输出内存数据到文件中时,会自动将0A换成0A0D

    0A即\n,而0D是\r,windows下换行是\n\r,因此会自动转换. 但是,这样会带来很大的问题,导致由内存写入文件中的数据和内存中不一样,还不知道是什么原因造成的. 特别是将从网络接收来的pn ...

  7. (t,p,o) t:p>=o there cannot be more consumer instances in a consumer group than partitions

    https://kafka.apache.org/intro.html Kafka as a Messaging System How does Kafka's notion of streams c ...

  8. ubuntu 16.04安装Jenkins

    快速安装: sudo wget -q -O - http://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add - sudo ...

  9. hihocoder #1094 : Lost in the City微软苏州校招笔试 12月27日 (建图不大【暴力枚举】 子图的4种形态 1Y )

    #1094 : Lost in the City 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Little Hi gets lost in the city. He ...

  10. Codeforces Round #363 (Div. 2) C. Vacations —— DP

    题目链接:http://codeforces.com/contest/699/problem/C 题解: 1.可知每天有三个状态:1.contest ,2.gym,3.rest. 2.所以设dp[i] ...