Custom class loading encrypts and decrypts classes

In the JVM class loader , we introduce that in addition to the most basic three types of loaders that cooperate with each other to load, if necessary, we add our own defined class loader.


Here we will define a class loader to implement encryption and decryption of classes.

First, create a new User.javafile with the following contents:

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);
    }
}

Select the file, and click in User.javathe menu bar to compile and obtain the file.BuildReCompile 'User.java'User.class
insert image description here

Then encrypt the compiled ones User.class. As for the encryption method, use the simplest way to demonstrate, using the operation in the Java bit operator^ 按位异或 for processing. A number is XORed twice, and the result is still the same number.


Then move directly User.classto the project, then copy it and rename it UserSrc.class, and then use the XOR operation to overwrite the original User.classfile.

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);
    }
}

You can use Sublime to open the encrypted User.classfile for viewing. According to the class file structure and bytecode instructions , you can find that the class file class structure has been destroyed, such as the common magic number 0xcafebabe.
insert image description here


Now you need to implement your own class loader, which is used to decrypt and then load the encrypted class file at runtime. The first thing you must inherit is ClassLoaderthis abstract class.


The method in the ClassLoader class loadClass()is where the logic of the parent delegation model is implemented. Modifying this method without authorization will cause the model to be destroyed and cause problems. Therefore, it is best for us to make small-scale changes within the framework of the parental delegation model without destroying the original stable structure.

Therefore, in order to ensure that the parent delegation model is not destroyed, it is recommended not to rewrite loadClass()the method, but to rewrite findClass()the method, and only findClass()rewrite the loading method of the custom class.

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);
    }
}

Finally, you can delete the file User.javaand UserSrc.classkeep only the encrypted User.classfile, and use a custom class loader to decrypt and load it. The result is as follows:

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);
    }
}

insert image description here

Guess you like

Origin blog.csdn.net/rockvine/article/details/124836389