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

待补充...

posted @ 2021-08-11 16:19  ChanGeZ  阅读(828)  评论(0编辑  收藏  举报