カスタム クラスの読み込みによりクラスが暗号化および復号化されます。

JVM クラスローダーでは、相互に連携してロードする最も基本的な 3 種類のローダーに加えて、必要に応じて独自に定義したクラスローダーを追加することを紹介します。


ここでは、クラスの暗号化と復号化を実装するクラスローダーを定義します。

User.javaまず、次の内容の新しいファイルを作成します。

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

ファイルを選択し、User.javaメニュー バーBuildをクリックしてReCompile 'User.java'コンパイルしてUser.classファイルを取得します。
ここに画像の説明を挿入

次に、コンパイルしたものを暗号化しますUser.class。暗号化方法については、Java ビット演算子の演算を使用し^ 按位异或て、同じ数値を 2 回 XOR して処理しても、結果は同じ数値になることを示す最も簡単な方法を使用します。


User.class次に、プロジェクトに直接移動し、コピーして名前を変更しUserSrc.class、XOR 演算を使用して元のUser.classファイルを上書きします。

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

Sublime を使用して、暗号化されたファイルを開いて表示できます。クラス ファイルの構造とバイトコードの指示User.classによると、共通のマジック ナンバー 0xcafebabe など、クラス ファイルのクラス構造が破壊されていることがわかります。
ここに画像の説明を挿入


次に、実行時に暗号化されたクラス ファイルを復号してロードするために使用される独自のクラス ローダーを実装する必要があります。最初に継承する必要があるのは、ClassLoaderこの抽象クラスです。


ClassLoader クラスのメソッドloadClass()には、親委任モデルのロジックが実装されており、このメソッドを許可なく変更すると、モデルが破壊され、問題が発生します。したがって、元の安定した構造を破壊することなく、親委任モデルの枠組み内で小規模な変更を加えることが最善です。

したがって、親委任モデルが破壊されないようにするには、loadClass()メソッドを書き換えるのではなく、findClass()メソッドを書き換えて、findClass()カスタム クラスの読み込みメソッドのみを書き換えることをお勧めします。

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

最後に、ファイルを削除しUser.javaUserSrc.class暗号化されたUser.classファイルのみを保持し、カスタム クラス ローダーを使用してそれを復号化してロードすると、結果は次のようになります。

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

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/rockvine/article/details/124836389