自定义类加载对类进行加密和解密

JVM类加载器中,我们介绍除了最基础的3种类加载器互相配合进行加载的,我们如果有必要,是加入自己定义的类加载器。


这里我们就来自定义一个类加载器,用于实现对类进行加密和解密处理。

首先新建一个User.java文件,其中内容具体如下:

package com.rockvine.loader;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class User {
    
    
    private String name = "Rocky";
    private int age = 18;

	// 省略Getter、Setter方法
	// ...

    @Override
    public String toString() {
    
    
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

选中User.java文件,在菜单栏的Build中,点击 ReCompile 'User.java',即可编译获取到User.class文件。
在这里插入图片描述

然后对编译生成的User.class进行加密,至于加密方法使用最简单的方式进行演示,利用Java位运算符中的 ^ 按位异或 运算进行处理,一个数异或同一个数两次,结果还是那个数。


然后直接将的User.class移动至项目中,然后将其复制一份重命名为UserSrc.class,再利用异或操作来覆盖原User.class文件。

public class XorEncryptUtil {
    
    

    // 异或操作, 可以进行加密和解密
    private static void xor(InputStream in, OutputStream out) throws Exception {
    
    
        int ch;
        while (-1 != (ch = in.read())) {
    
    
            ch = ch ^ 0xff;
            out.write(ch);
        }
    }

    // 加密方法
    public static void encrypt(File src, File des) throws Exception {
    
    
        InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(des);

        xor(in, out);

        in.close();
        out.close();
    }

    // 解密方法
    public static byte[] decrypt(File src) throws Exception {
    
    
        InputStream in = new FileInputStream(src);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        xor(in, bos);
        byte[] data = bos.toByteArray();

        in.close();
        bos.close();
        return data;
    }
}
public class XorEncryptTest {
    
    
    public static void main(String[] args) throws Exception {
    
    
        String dirPath = System.getProperty("user.dir");
        String basePath = dirPath + "/jvm/src/main/java/com/rockvine/loader";

        File src = new File(basePath + "/UserSrc.class");
        File des = new File(basePath + "/User.class");

        XorEncryptUtil.encrypt(src, des);
    }
}

可以使用Sublime打开加密后的User.class文件进行查看,根据类文件结构及字节码指令介绍的,可以发现class文件类结构已经被破坏了,如常见的魔数0xcafebabe之类的。
在这里插入图片描述


现在就需要实现一个自己的类加载器,用于对加密后的class文件在运行时,先解密再加载,首先必须要继承的就是 ClassLoader 这个抽象类。


ClassLoader类中的loadClass()方法是实现双亲委托模型逻辑的地方,擅自修改这个方法会导致模型被破坏,容易造成问题。因此我们最好是在双亲委托模型框架内进行小范围的改动,不破坏原有的稳定结构。

所以为了保证双亲委派模型不被破坏,建议不要重写loadClass()方法,而是重写findClass()方法,只在findClass()里重写自定义类的加载方法。

public class MyClassLoader extends ClassLoader {
    
    

    private String basePath;
    private final static String FILE_EXT = ".class";

    public void setBasePath(String basePath) {
    
    
        this.basePath = basePath;
    }

    // 解密
    private byte[] loadClassData(String name) {
    
    
        try {
    
    
            String tempName = name.replaceAll("\\.", System.getProperty("file.separator"));
            return XorEncryptUtil.decrypt(new File(basePath + tempName + FILE_EXT));
        } catch (Exception e) {
    
    
            System.out.println("自定义类加载器加载失败,错误原因:" + e.getMessage());
            return null;
        }
    }

    @Override
    protected Class<?> findClass(String name) {
    
    
        byte[] data = this.loadClassData(name);
        return this.defineClass(name, data, 0, data.length);
    }
}

最后可以把User.javaUserSrc.class文件删除,只保留加密后的User.class文件,用自定义的类加载器进行解密加载,结果如下:

public class Client {
    
    
    public static void main(String[] args) throws Exception {
    
    
        String dirPath = System.getProperty("user.dir");
        String basePath = dirPath + "/jvm/src/main/java/";

        MyClassLoader myClassLoader = new MyClassLoader();
        myClassLoader.setBasePath(basePath);

        Class<?> clazz = myClassLoader.findClass("com.rockvine.loader.User");
        System.out.println(clazz.getClassLoader());

        Object o = clazz.newInstance();
        System.out.println(o);
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/rockvine/article/details/124836389
今日推荐