1. 程式人生 > 其它 >Java中動態載入位元組碼的那些方法

Java中動態載入位元組碼的那些方法

持續補充 ...

URLCLassLoader

ClassLoader#loader(URL[])遠端載入class

URLCLassLoader實際上是我們平時預設使用的AppClassLoader的父類,所以,我們解釋URLClassLoader的工作過程實際上就是在解釋預設的Java類載入器的工作流程。

java 對路徑的處理:

  • URL未以斜槓 / 結尾,則認為是一個JAR檔案,使用 JarLoader 來尋找類,即為在Jar包中尋 找.class檔案
  • URL以斜槓 / 結尾,且協議名是 file ,則使用 FileLoader 來尋找類,即為在本地檔案系統中尋 找.class檔案
  • URL以斜槓 / 結尾,且協議名不是 file ,則使用最基礎的 Loader 來尋找類

也就是協議不是file且以 / 結尾,會使用Loader尋找類,最常見的是http協議。可以通過這種方法直接載入遠端的class檔案,所以如果我們控制了目標Java ClassLoader的基礎路徑為一個http伺服器,即可RCE

CLassLoader#definClass 直接載入位元組碼

無論載入遠端class還是本地class或者jar檔案,呼叫過程都是下面三個方法:

  • loadClass 的作用是從已載入的類快取、父載入器等位置尋找類(這裡實際上是雙親委派機 制),在前面沒有找到的情況下,執行 findClass
  • findClass 的作用是根據基礎URL指定的方式來載入類的位元組碼,就像上一節中說到的,可能會在 本地檔案系統、jar包或遠端http伺服器上讀取位元組碼,然後交給defineClass
  • defineClass 的作用是處理前面傳入的位元組碼,將其處理成真正的Java類
    核心在defineClass,決定了如何將一段位元組流轉變成一個Java類,Java預設的ClassLoader#defineClass是一個native方法,邏輯在JVM的C語言程式碼中。
    我們無法直接外部呼叫這些方法來載入位元組碼,但是有一些庫中包含了部分程式碼完成了這個過程,我們可以利用這些庫達到目的。

載入方式一: TemplatesImpl

非常常用的Java反序列化利用鏈組成部分,poc:

public static void main(String[] args) throws Exception {
    BASE64Decoder base64Decoder = new BASE64Decoder();
    byte[] code = base64Decoder.decodeBuffer("");
    TemplatesImpl obj = new TemplatesImpl();
    Field f1 = obj.getClass().getDeclaredField("__bytecodes");
    f1.setAccessible(true);
    f1.set(obj, new byte[][]{code});

    Field f2 = obj.getClass().getDeclaredField("__name");
    f2.setAccessible(true);
    f2.set(obj, "HelloTemplatesImpl");

    Field f3 = obj.getClass().getDeclaredField("__tfactory");
    f3.setAccessible(true);
    f3.set(obj, new TransformerFactoryImpl());

    obj.newTransformer();

}

注意:
設定了三個屬性: _bytecodes 、 _name 和 _tfactory 。

  • _bytecodes 是由位元組碼組成的陣列;
  • _name 可以是任意字串,只要不為null即可;
  • _tfactory 需要是一個 TransformerFactoryImpl 物件,因為emplatesImpl#defineTransletClasses()方法裡有呼叫到tfactory.getExternalExtensionsMap(),如果是null會出錯。

另外,TemplatesImpl 中對載入的位元組碼是有一定要求的:這個位元組碼對應的類必須是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子類。因此需要構造一個特殊的類。

載入方式二: BCELClassLoader

BCEL Classloader去哪了
在java 8u251前可用。
使用的是com.sun.org.apache.bcel.internal.util.ClassLoader;

// 原生位元組碼轉BCEL
JavaClass cls = Repository.lookupClass(exp.class);
String code = Utility.encode(cls.getBytes(), true);
System.out.println(code);
// 載入
new ClassLoader().loadClass("exp").newInstance();

待補充...