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.java
file 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.java
the menu bar to compile and obtain the file.Build
ReCompile 'User.java'
User.class
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.class
to the project, then copy it and rename it UserSrc.class
, and then use the XOR operation to overwrite the original User.class
file.
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.class
file 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.
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 ClassLoader
this 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.java
and UserSrc.class
keep only the encrypted User.class
file, 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);
}
}