Java 字節碼易被反編譯(JD-GUI、Fernflower 等工具),源代碼幾乎裸奔。傳統混淆(ProGuard 等)僅增加逆向難度,無法根治。加密 + 自定義 ClassLoader 實現「運行時解密、內存中執行、磁盤無明文」,大幅提高逆向成本。
JVM 載入類時委託 ClassLoader 尋找字節碼、轉為 Class 對象。繼承 ClassLoader 並重寫 loadClass(),即可在定義類前插入解密邏輯。解密後的字節碼僅存在於 JVM 內存,不落盤。
| 方法 | 作用 |
|---|---|
| findLoadedClass | 檢查類是否已載入,避免重複載入 |
| defineClass | 將字節碼數組轉為 Class 對象(核心) |
| findSystemClass | 委託系統類加載器載入(如 JDK 核心類) |
| resolveClass | 解析類的符號引用(當 resolve=true 時調用) |
SecureRandom sr = new SecureRandom();
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(sr);
SecretKey key = kg.generateKey();
byte[] rawKeyData = key.getEncoded(); // 保存供後續使用
DESKeySpec dks = new DESKeySpec(rawKeyData);
SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
SecretKey key = kf.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key, sr);
byte[] encrypted = cipher.doFinal(classBytes); // 寫入 .class 文件
cipher.init(Cipher.DECRYPT_MODE, key, sr);
byte[] decrypted = cipher.doFinal(encryptedClassBytes);
Class clazz = defineClass(name, decrypted, 0, decrypted.length);
GenerateKey:生成密鑰文件 key.dataEncryptClasses:加密 App.class、Foo.class、Bar.classDecryptStart:自定義 ClassLoader + 主程序入口,讀取密鑰、載入並運行加密應用運行方式:java DecryptStart key.data App arg0 arg1
DecryptStart 落盤解密後字節碼 → 對啟動類做深度混淆或編譯為本地機器碼「無完全安全的系統」,但加密 ClassLoader 方案在不修改業務代碼前提下,將逆向門檻從「反編譯工具一鍵搞定」提升至「需分析 JVM 內存/調試 ClassLoader」,足以保護大多數商業邏輯與知識產權。