day7—动态加载字节码

ClassLoader 是什么呢?它就是一个“加载器”,告诉Java虚拟机如何加载这个类。Java默认的 ClassLoader 就是根据类名来加载类,这个类名是类完整路径,如 java.lang.Runtime 。

URLClassLoader加载远程class文件

image-20211121195009835

URLClassLoader继承于SecureClassLoader,支持从jar文件和文件夹中获取class,继承于ClassLoader,加载时首先去ClassLoader里判断是否由启动类加载器加载过。

java会根据配置项sun.boot.class.path java.class.path 中列举到的基础路径(我们设置的ClassPath和从其他途径导入的)

image-20211121211227250

  • URL未以斜杠 / 结尾,则认为是一个JAR文件,使用 JarLoader 来寻找类,即为在Jar包中寻 找.class文件

  • URL以斜杠 / 结尾,且协议名是 file ,则使用 FileLoader 来寻找类,即为在本地文件系统中寻 找.class文件

  • URL以斜杠 / 结尾,且协议名不是 file ,则使用最基础的 Loader 来寻找类

我们写一个简单Demo来尝试一下使用URLClassLoader

1
2
3
4
5
6
7
8
9
10
11
12
13
package Main;

import java.net.URL;
import java.net.URLClassLoader;

public class UCloader {
public static void main(String[] args) throws Exception {
URL[] urls = {new URL("http://127.0.0.1:8000/")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class test = urlClassLoader.loadClass("Hello");
test.newInstance();
}
}

我们再生成一个Hello.class,并启动一个http服务

1
2
3
4
5
6
7
8
public class Hello {
public Hello(){
System.out.println("hello");
}
public static void main(String[] args) {
System.out.println("main");
}
}

image-20211122140338022

ClassLoader#defineClass加载字节码

根据官方文档可知,defineClass可以直接从byte中还原出一个Class对象,这种方法可以利用在我们构造payload中

根据P牛的解释,在家在class文件或者jar文件中都经历三个步骤

image-20211122141727035

  • loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机 制),在前面没有找到的情况下,执行 findClass

  • findClass 的作用是根据基础URL指定的方式来加载类的字节码,就像上一节中说到的,可能会在 本地文件系统、jar包或远程http服务器上读取字节码,然后交给 defineClass

  • defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java类

我们来尝试将刚刚的Hello.class采用defineClass加载

将刚刚生成的Hello.class转换成base64

image-20211124145510856

我们再使用反射的方式去加载

1
2
3
4
5
6
7
8
/**
* defineClass
*/
Method method = ClassLoader.class.getDeclaredMethod("defineClass",String.class,byte[].class,int.class,int.class);
method.setAccessible(true);
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAHgoABwAQCQARABIIABMKABQAFQgADAcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEAClNvdXJjZUZpbGUBAApIZWxsby5qYXZhDAAIAAkHABgMABkAGgEABWhlbGxvBwAbDAAcAB0BAAVIZWxsbwEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYAIQAGAAcAAAAAAAIAAQAIAAkAAQAKAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACwAAAA4AAwAAAAMABAAEAAwABQAJAAwADQABAAoAAAAlAAIAAQAAAAmyAAISBbYABLEAAAABAAsAAAAKAAIAAAAHAAgACAABAA4AAAACAA8=");
Class Hello = (Class)method.invoke(ClassLoader.getSystemClassLoader(), "Hello", bytes,0, bytes.length);
Hello.newInstance();

TemplatesImpl加载字节码

TemplatesImpl中有一个内部类TransletClassLoader继承了ClassLoader

我们首先寻找一下利用链,根据idea的find usages我们可以查看到哪里被调用,我们由内向外进行推导

1
2
TransletClassLoader#defineClass <- TemplatesImpl#defineTransletClasses <-   TemplatesImpl#getTransletInstance <- TemplatesImpl#newTransformer <-
TemplatesImpl#getOutputProperties

newTransformer,getOutputProperties均为public方法,可以被外部进行调用,尝试加载字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package Main;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.util.Base64;
import java.net.URL;
import java.net.URLClassLoader;

public class UCloader {
public static void main(String[] args) throws Exception {
byte[]code=Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE=");
TemplatesImpl obj=new TemplatesImpl();
setFieldValue(obj,"_bytecodes",new byte[][]{code});
setFieldValue(obj,"_name","HelloTemplatesImpl");
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
obj.newTransformer();
}

public static void setFieldValue(Object obj,String fieldName,Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}

我们一共设置了三个参数_bytecodes,_name,_tfactory我们配合代码一个一个看

  • _bytecodes:我们在调用的时候会在defineTransletClasse中传给defineClass,所以此参数应该传入我们的字节码
    image-20211124164415424

  • _name:主要是过判断,在getTransletInstance中会判断该参数是否为null

    image-20211124164547524

  • _tfactory: 在defineTransletClasse中,会调用_tfactory.getExternalExtensionsMap(),所以_tfactory必须有getExternalExtensionsMap()方法,所以我们让_tfactoryTransformerFactoryImpl的对象,这样就可以调用到这个方法,从而不报错

    image-20211124165257599

其次,我们生成的字节码的类还需要满足一定的条件,我们所使用的类必须是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类
所以我们需要构造一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class TempExp extends AbstractTranslet {

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}

public TempExp(){
System.out.println("this is a exp");
}
}

image-20211124170234970

BCEL ClassLoader加载字节码

fastjson不出网的情况下可以用该方法进行字节码的加载