java-字节码

什么是字节码

其实仅仅指的是Java虚拟机执行使用的一类指令,通常被存储在.class文件中

更广义上来说,所有能恢复成一个类并在jvm虚拟机里加载的字节序列,都在我们的探讨范围内。

利用URLClassLoader加载远程class文件

ClassLoader:

​ 是用来加载字节码文件最基础的方法,是一个加载器,告诉java虚拟机如何加载这个类,java默认的ClassLoader就是根据类名来加载类,,并且类名是类完整路径,如java.lang.Runtime

URLClassLoader

URLClassLoader实际上是我们平时默认使用的AppClassLoader的父类,所以,URLClassLoader=java类加载器的工作流程

正常情况下,Java会根据配置项sun.boot.class.pathjava.class.path中列举到的基础路径(这些路径是经过处理后的java.net.URL类)来寻找.class文件来加载,而这个基础路径有三种情况:

  • URL未以斜杠/结尾,则认为是一个JAR文件,使用JarLoader来寻找类,即为在Jar包中寻找.class文件
  • URL以斜杠/结尾,且协议名是file,则使用FileLoader来寻找类,即为在本地文件系统中寻找.class文件
  • URL以斜杠/结尾,且协议名不是file,则使用最基础的Loader来寻找类。

java的URL支持哪些协议

JAVA默认提供了对file,ftp,gopher,http,https,jar,mailto,netdoc协议的支持

Loader寻找类

loader寻找类,最常见的情况就是http协议。

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

public class classloader {
public static void main(String[] args ) throws Exception
{
URL[] urls={new URL("http://localhost:8000/")};
URLClassLoader loader = URLClassLoader.newInstance(urls);
Class c =loader.loadClass("ex_1");
c.newInstance();

}
}
image-20220727152404028

利用ClassLoader#defineClass直接加载字节码

java加载远程class文件/本地class或jar文件

ClassLoader#loadClass ——> ClassLoader#findClass ——>ClassLoader#defineClass

其中:

  • Loadclass的作用是从已加载的类缓存,父加载器等位置寻找类(双亲委派机制)
  • findClass的作用是根据基础URL指定的方式来加载类的字节码,可能会在本地文件系统、jar包、或远程http服务器上读取字节码,然后交给defineClass
  • defineClass的作用是处理前面传入的字节码,将其处理为真正的java类

defineClass可以从byte[]还原出一个Class对象

使用defineClass直接加载字节码

import java.lang.reflect.Method;
import java.util.Base64;

public class defineclass {
public static void main(String[] args) throws Exception{
Method defineclass=ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineclass.setAccessible(true);

byte[] code=Base64.getDecoder().decode("字节码内容");
Class hello = (Class) defineclass.invoke(ClassLoader.getSystemClassLoader(),"Hello",code,0,code.length);
hello.newInstance();
}

}

使用TemplatesImpl加载字节码

TransletClassLoader

TransletClassLoader来自com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

其中重写了defineClass方法

Class defineClass(final byte[] b) { 
return defineClass(null, b, 0, b.length);
}

在重写的这个方法中,由于其没有显式声明作用域,,所以其作用域为default,所以也就是说这里的defineClass由其父类的protected类型变成了一个default类型的方法,可以被类外部调用。

TransletClassLoader#defineClass()向前追溯调用链为

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()

利用newTransformer()构造poc:

public static void main(String[] args) throws Exception{
byte[] code = Base64.getDecoder().decode("")

TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_bytecodes",new byte[][] {code});
setFieldValue(obj,"_name","HelloTemplatesImpl");
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
obj.newTransformer();
}

其中,setFieldValue方法来设置私有属性,其中_bytecodes、_name和_tfactory._bytecodes是由字节码组成的数组;_name可以是任意字符串,只要不为null即可。

_tfactory需要是一个TransformerFactoryImpl对象,因为TemplatesImpl#defineTransletClasses()方法里有调用到\_tfactory.getExternalExtensionsMap()如果是null会出错

TemplatesImpl中对加载的字节码有一定要求即,这个字节码对应的类必须是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子类

所以,我们需要构造一个特殊的类

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 HelloTemplatesImpl extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
public HelloTemplatesImpl() {
super();
System.out.println("Hello TemplatesImpl");
}
}

它继承了AbstractTranslet类,并在构造函数里插入Hello的输出,将其编译成字节码,即可被TemplatesImpl执行

利用BCEL ClassLoader加载字节码

package com.govuln; import com.sun.org.apache.bcel.internal.classfile.JavaClass; import com.sun.org.apache.bcel.internal.classfile.Utility; import com.sun.org.apache.bcel.internal.Repository; 

public class HelloBCEL {
public static void main(String []args) throws Exception {
JavaClass cls = Repository.lookupClass(evil.Hello.class);
String code = Utility.encode(cls.getBytes(), true);
System.out.println(code);
}
}
Author

vague huang

Posted on

2022-09-20

Updated on

2023-02-14

Licensed under

Comments