'Java反序列化-基础'

四个部分

一、java SE基础(这部分不写了,自己看菜鸟教程 或者 b站大学视频教程)
推荐:狂神说java
二、java反射
三、序列化与反序列化
四、jdk动态代理
五、类加载部分

二、java反射

java反射流程

1、Class类对象的获取

编写pojo类,定义两个成员变量(字段),构造有参、无参、get set方法、tostring。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package demo20;

public class Person {
private String name;
private int age;

//无参
public Person(){

}

//有参
public Person(String name, int age) {
this.name = name;
this.age = age;
}

//get set方法
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

//tostring
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

调用代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package demo20;

public class classTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Person person = new Person();
Class c1 = person.getClass();
Class<?> aClass = c1.forName("demo20.Person");

//获取相应参数
System.out.println(aClass.getName());
System.out.println(aClass.getSimpleName());
//实例化
System.out.println(aClass.newInstance());


}

}

获取后的结果截图

实例化后,对其参数进行修改

1
2
3
4
Person person2 = (Person)aClass.newInstance();
person2.setAge(20);
person2.setName("testc");
System.out.println(person2);

进一步理解,对比图:

Class类的方法

方法名 说明
forName() 获取Class对象的一个引用
Object-getClass() 获取Class对象的一个引用,返回表示该对象的实际类型的Class引用
getName() 取全限定的类名(包括包名),即类的完整名字。
getSimpleName() 获取类名(不包括包名)
getCanonicalName() 获取全限定的类名(包括包名)
isInterface() 判断Class对象是否是表示一个接口
getInterfaces() 返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSupercalss() 返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance() 返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。 没有无参构造器,就会报错"InstantiationException"实例化异常。

2、Constructor类及其用法(构造器)

Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。

Constructor相关的主要方法如下:

方法名 说明
getConstructor() 返回指定参数类型、具有public访问权限的构造函数对象
getConstructors() 返回所有具有public访问权限的构造函数的Constructor对象数组
getDeclaredConstructor() 返回指定参数类型、所有声明的(包括private)构造函数对象
getDeclaredConstructors() 返回所有声明的(包括private)构造函数对象

调用代码:

1
2
3
4
//获取构造器,通过反射修改参数
Constructor constructor = aClass.getConstructor(String.class,int.class);
Person person3 = (Person)constructor.newInstance("test123",108);
System.out.println(person3);

获取后的结果截图

获取私有构造,pojo类中的有参构造,public改为private,即可变为私有构造。

1
2
3
4
private Person(String name, int age) {
this.name = name;
this.age = age;
}

调用代码:

1
2
3
4
5
//获取私有构造,利用反射修改其参数
Constructor constructor = aClass.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
Person person3 = (Person)constructor.newInstance("test_private",22);
System.out.println(person3);

3、Field类及其用法(类的成员变量)

方法名 说明
getDeclaredField() 获取指定name名称的(包含private修饰的)字段,不包括继承的字段
getDeclaredFields() 获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
getField() 获取指定name名称、具有public修饰的字段,包含继承字段
getFields() 获取修饰符为public的字段,包含继承字段

调用代码:

1
2
3
4
5
6
7
8
9
10
11
12
        //只能调用public字段
// Field age1 = aClass.getField("age");
// System.out.println(age1);
//

Field age2 = aClass.getDeclaredField("age");
System.out.println(age2);

Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}

利用反射修改其参数,代码与构造器处的代码相同。

4、Method类及其用法(方法)

方法名 说明
getDeclaredMethod() 返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
getMethod() 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

pojo类中添加代码

1
2
3
4
5
6
7
8
9
10
public void action(String act){
System.out.println(act);
}

public void draw(int count , String name){
System.out.println("draw "+ name +",count="+count);
}
private void zero0(){
System.out.println("zero0");
}

调用代码:

1
2
3
4
5
6
7
8
9
10
11
//获取pojo类中的 action方法
Method action = aClass.getMethod("action", String.class);
System.out.println(action);

//获取public的所有方法,包含继承来自父类的方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}



反射部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
//反射修改参数流程
//获取指定对象
Class<?> aClass = Class.forName("demo20.Person");
//对其进行实例化
Person person2 = (Person)aClass.newInstance();
//获取draw方法对象Method
Method draw = aClass.getMethod("draw", int.class, String.class);
draw.invoke(person2,15,"zer0_climb");
System.out.println("======================");
//操作私有方法
Method zero0 = aClass.getDeclaredMethod("zero0");
zero0.setAccessible(true);
zero0.invoke(person2);

5、反射机制执行的流程

(1)获取类实例forName

跟进代码

先通过反射,获取调用进来的类信息,从而获取当前的 classLoader

1
2
3
4
5
6
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

调用native方法进行获取class信息 “demo20.classTeset”

1
2
@CallerSensitive
public static native Class<?> getCallerClass();

进入到ClassLoader类的getClassLoader方法
判断虚拟机是否请求他

1
2
3
4
if (caller == null) {
return null;
}
return caller.getClassLoader0();

跟进getClassLoader0方法
并没有将实现留给了java,而是交给了jvm去加载。

1
ClassLoader getClassLoader0() { return classLoader; }

查看classLoader类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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;
}
}

判断太底层了,跟不动了。。。

(2)newInstance反射初始化(或者说实例化?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}

// NOTE: the following code may not be strictly correct under
// the current Java memory model.

// Constructor lookup
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}

接下来的话就是一些权限检测的工作(java.security.PrivilegedAction)
出来之后,运行构造函数(Run constructor部分代码)

newInstance() 主要做了三件事:

(1)权限检测,如果不通过直接抛出异常;
(2)查找无参构造器,并将其缓存起来;
(3)调用具体方法的无参构造方法,生成实例并返回;

获取构造器,则要跟进getConstructor0方法

1
2
3
4
5
6
7
8
9
10
11
12
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException
{
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}

先获取所有的constructors, 然后通过进行参数类型比较;

跟进privateGetDeclaredConstructors方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
checkInitted();
Constructor<T>[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
if (res != null) return res;
}
// No cached value available; request value from VM
if (isInterface()) {
@SuppressWarnings("unchecked")
Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
res = temporaryRes;
} else {
res = getDeclaredConstructors0(publicOnly);
}
if (rd != null) {
if (publicOnly) {
rd.publicConstructors = res;
} else {
rd.declaredConstructors = res;
}
}
return res;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
if (useCaches &&
reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
return newReflectionData(reflectionData, classRedefinedCount);
}

private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData,
int classRedefinedCount) {
if (!useCaches) return null;

while (true) {
ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
// try to CAS it...
if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
return rd;
}
// else retry
oldReflectionData = this.reflectionData;
classRedefinedCount = this.classRedefinedCount;
if (oldReflectionData != null &&
(rd = oldReflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
}
}

如上,privateGetDeclaredConstructors(), 获取所有的构造器主要步骤;

先尝试从缓存中获取;
如果缓存没有,则从jvm中重新获取,并存入缓存,缓存使用软引用进行保存,保证内存可用;

ReflectionData 的数据结构如下。

跟进copyConstructor方法:参数类型比较完毕,找到匹配后,通过 ReflectionFactory copy一份constructor返回;

否则抛出 NoSuchMethodException。

(3)获取方法

getDeclaredMethod

1
2
3
4
5
6
7
8
9
10
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}

忽略第一个检查权限,剩下就只有两个动作了。

获取所有方法列表;
根据方法名称和方法列表,选出符合要求的方法;
如果没有找到相应方法,抛出异常,否则返回对应方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
// No cached value available; request value from VM
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
return res;
}

和获取所有构造器的方法很相似,都是先从缓存中获取方法,如果没有,则从jvm中获取。
不同的是,方法列表需要进行过滤 Reflection.filterMethods;当然后面看来,这个方法我们一般不会派上用场。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes)
{
Method res = null;
String internedName = name.intern();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName() == internedName
&& arrayContentsEq(parameterTypes, m.getParameterTypes())
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType())))
res = m;
}

return (res == null ? res : getReflectionFactory().copyMethod(res));
}

匹配到方法名,然后参数类型匹配,才可以。
匹配到一个方法,并没有退出for循环,而是继续进行匹配。

(4)调用 method.invoke() 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}

invoke时,是通过 MethodAccessor 进行调用的,而 MethodAccessor 是个接口,在第一次时调用 acquireMethodAccessor() 进行新创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it
// if so
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}

return tmp;
}

进行 ma.invoke(obj, args); 调用时,调用 NativeMethodAccessorImpl.invoke();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
public MethodAccessor generateMethod(Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6) {
return (MethodAccessor)this.generate(var1, var2, var3, var4, var5, var6, false, false, (Class)null);
}

public ConstructorAccessor generateConstructor(Class<?> var1, Class<?>[] var2, Class<?>[] var3, int var4) {
return (ConstructorAccessor)this.generate(var1, "<init>", var2, Void.TYPE, var3, var4, true, false, (Class)null);
}

public SerializationConstructorAccessorImpl generateSerializationConstructor(Class<?> var1, Class<?>[] var2, Class<?>[] var3, int var4, Class<?> var5) {
return (SerializationConstructorAccessorImpl)this.generate(var1, "<init>", var2, Void.TYPE, var3, var4, true, true, var5);
}

private MagicAccessorImpl generate(final Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6, boolean var7, boolean var8, Class<?> var9) {
ByteVector var10 = ByteVectorFactory.create();
this.asm = new ClassFileAssembler(var10);
this.declaringClass = var1;
this.parameterTypes = var3;
this.returnType = var4;
this.modifiers = var6;
this.isConstructor = var7;
this.forSerialization = var8;
this.asm.emitMagicAndVersion();
short var11 = 42;
boolean var12 = this.usesPrimitiveTypes();
if (var12) {
var11 = (short)(var11 + 72);
}

if (var8) {
var11 = (short)(var11 + 2);
}

var11 += (short)(2 * this.numNonPrimitiveParameterTypes());
this.asm.emitShort(add(var11, (short)1));
final String var13 = generateName(var7, var8);
this.asm.emitConstantPoolUTF8(var13);
this.asm.emitConstantPoolClass(this.asm.cpi());
this.thisClass = this.asm.cpi();
if (var7) {
if (var8) {
this.asm.emitConstantPoolUTF8("sun/reflect/SerializationConstructorAccessorImpl");
} else {
this.asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");
}
} else {
this.asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");
}

this.asm.emitConstantPoolClass(this.asm.cpi());
this.superClass = this.asm.cpi();
this.asm.emitConstantPoolUTF8(getClassName(var1, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
this.targetClass = this.asm.cpi();
short var14 = 0;
if (var8) {
this.asm.emitConstantPoolUTF8(getClassName(var9, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
var14 = this.asm.cpi();
}

this.asm.emitConstantPoolUTF8(var2);
this.asm.emitConstantPoolUTF8(this.buildInternalSignature());
this.asm.emitConstantPoolNameAndType(sub(this.asm.cpi(), (short)1), this.asm.cpi());
if (this.isInterface()) {
this.asm.emitConstantPoolInterfaceMethodref(this.targetClass, this.asm.cpi());
} else if (var8) {
this.asm.emitConstantPoolMethodref(var14, this.asm.cpi());
} else {
this.asm.emitConstantPoolMethodref(this.targetClass, this.asm.cpi());
}

this.targetMethodRef = this.asm.cpi();
if (var7) {
this.asm.emitConstantPoolUTF8("newInstance");
} else {
this.asm.emitConstantPoolUTF8("invoke");
}

this.invokeIdx = this.asm.cpi();
if (var7) {
this.asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");
} else {
this.asm.emitConstantPoolUTF8("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
}

this.invokeDescriptorIdx = this.asm.cpi();
this.nonPrimitiveParametersBaseIdx = add(this.asm.cpi(), (short)2);

for(int var15 = 0; var15 < var3.length; ++var15) {
Class var16 = var3[var15];
if (!isPrimitive(var16)) {
this.asm.emitConstantPoolUTF8(getClassName(var16, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
}
}

this.emitCommonConstantPoolEntries();
if (var12) {
this.emitBoxingContantPoolEntries();
}

if (this.asm.cpi() != var11) {
throw new InternalError("Adjust this code (cpi = " + this.asm.cpi() + ", numCPEntries = " + var11 + ")");
} else {
this.asm.emitShort((short)1);
this.asm.emitShort(this.thisClass);
this.asm.emitShort(this.superClass);
this.asm.emitShort((short)0);
this.asm.emitShort((short)0);
this.asm.emitShort((short)2);
this.emitConstructor();
this.emitInvoke();
this.asm.emitShort((short)0);
var10.trim();
final byte[] var17 = var10.getData();
return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() {
public MagicAccessorImpl run() {
try {
return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();
} catch (IllegalAccessException | InstantiationException var2) {
throw new InternalError(var2);
}
}
});

这里主要看ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();

在ClassDefiner.defineClass方法实现中,每被调用一次都会生成一个DelegatingClassLoader类加载器对象 ,这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载。

而反射生成的类,有时候可能用了就可以卸载了,所以使用其独立的类加载器,从而使得更容易控制反射类的生命周期。

三、序列化与反序列化

序列化是为了将对象进行长期存储而诞生的方法,那么反序列化就是将存储的序列化内容恢复成对象

Java的序列化和反序列化主要是 writeObject和readObject函数,Java允许开发者对readObject进行功能的补充,所以在反序列化过程中如果开发者重写了readObject方法那么Java会优先使用这个重写的方法,所以如果开发者书写不当的话就会导致命令执行

在大部分语言中都有序列化和反序列化的功能,例如PHP的serialize和unserialize,Java中的writeObject和ReadObject

反序列化漏洞在不同语言中影响也不同,我们所知的Java就存在很多反序列化漏洞,PHP则相对较少,这主要是取决于语言机制的关系

Java中的反序列化readObject 支持 Override,如果开发者重写了这个readObject方法,Java在反序列化过程中会优先调用开发者重写的这个readObject方法,通常在利用中我们需要找一个落脚点也就是gadget,利用这个落脚点来执行我们的恶意操作

PHP中的反序列化通常是利用魔术方法触发反序列化从而通过控制对象的属性,某些参数从而执行恶意命令。由于PHP在序列化和反序列化过程中是内部的操作,所以开发者是无法参与的,同样的PHP的反序列化中也需要寻找落脚点来执行我们的恶意操作

序列化代码如下;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//这部分为pojo类,没啥东西,其实就 记住要implements Serializable就行
package demo20;

import java.io.Serializable;

public class Student implements Serializable{
public String name;
public String address;
public int number;
}

//序列化部分
package demo20;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializeDemo {
//序列化,将obj对象 写入到ser.bin中
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out_obj1 = new ObjectOutputStream(new FileOutputStream("ser.bin"));
out_obj1.writeObject(obj);
System.out.println(obj);
}

public static void main(String[] args) throws IOException {
//给pojo类实例化,并将其中的字段赋值
Student student = new Student();
student.name = "ser_test1";
student.address = "addressssssss";
student.number = 123123;

//调用序列化
serialize(student);

}
}

反序列化部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package demo20;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnSerializeDemo {
public static Object unserialization(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream obj2 = new ObjectInputStream(new FileInputStream(Filename));
Object ois = obj2.readObject();
return ois;
}

public static void main(String[] args) throws IOException, ClassNotFoundException {
Student student = (Student)unserialization("ser.bin");
System.out.println(student.name);
System.out.println(student.address);
System.out.println(student.number);
}
}

声明为static和transient类型的数据不能被序列化, 反序列化需要一个无参构造函数

实例:
将pojo类中的number字段改为static类型,序列化,再反序列化调用。
结果中number字段会变为0。

利用BCompare读取ser.bin信息
这个文件里存放的就是反序列化过后的pojo类对象

AC ED:STREAM_MAGIC,声明使用了序列化协议,从这里可以判断保存的内容是否为序列化数据。 (这是在黑盒挖掘反序列化漏洞很重要的一个点)

00 05:STREAM_VERSION,序列化协议版本。

四、jdk动态代理

1、什么是jdk代理

JDK动态代理是有JDK提供的工具类Proxy实现的,动态代理类是在运行时生成指定接口的代理类,每个代理实例(实现需要代理的接口)都有一个关联的调用处理程序对象,此对象实现了InvocationHandler,最终的业务逻辑是在InvocationHandler实现类的invoke方法上。

2、静态代理

这种代理方式需要代理对象和目标对象实现一样的接口。

优点:
可以在不修改目标对象的前提下扩展目标对象的功能。
缺点:
冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。

示例:

  • 接口类:IUser
1
2
3
4
5
6
7
package demo21;

public interface IUser {
void show();

void update();
}
  • 目标对象:UserImpl,一般命名为UserDao。(我这里就不再更改了)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package demo21;

public class UserImpl implements IUser{

@Override
public void show(){
System.out.println("展示");
}

@Override
public void update(){
System.out.println("更新了!!");
}
}

  • 静态代理对象:UserProxy 一般命名为UserDapProxy (IUserDao的具体实现)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package demo21;

public class UserProxy implements IUser{
IUser user;
public UserProxy(){

}

public UserProxy(IUser user) {
this.user = user;
}

@Override
public void show(){
System.out.println("调用了show方法");
}

@Override
public void update(){
System.out.println("更新了!!");
}
}


  • 测试类:ProxyTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ProxyTest {
public static void main(String[] args) {
UserImpl user = new UserImpl();
user.show();
user.update();

System.out.println("===============");
//静态代理
UserPoxry userPoxry = new UserPoxry(user);
userPoxry.show();
userPoxry.update();

}
}

3、动态代理

动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。

  • 接口类:IUser 继续使用静态代理的即可
  • 目标对象:UserImpl 继续使用静态代理的即可
  • 动态代理对象:UserinvocationHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package demo21;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserInvocationHandler implements InvocationHandler {
//维护一个目标对象
IUser iuser;
//无参
public UserInvocationHandler(){

}
//有参
public UserInvocationHandler(IUser iUser) {
this.iuser = iUser;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用了"+method);
// 执行目标对象方法
method.invoke(iuser,args);
return null;
}
}

重写invoke快捷键
alt+ins,使用override。

  • 测试类:ProxyTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package demo21;

import java.lang.reflect.Proxy;

public class ProxyTest {
public static void main(String[] args) {
UserImpl user = new UserImpl();
// user.show();
// user.update();
//
// System.out.println("===============");
// UserProxy userProxy = new UserProxy(user);
// userProxy.show();
// userProxy.update();

//动态代理
UserInvocationHandler userInvocationHandler = new UserInvocationHandler(user);
IUser userproxy = (IUser)Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(), userInvocationHandler);

userproxy.show();
userproxy.update();

}
}

Proxy类的newProxyInstance方法,使用参数解释如下:

user.getClass().getClassLoader() //指定当前目标对象使用类加载器
user.getClass().getInterfaces() //目标对象实现的接口类型
userinvocationHandler //事件处理器

运行截图:

五、类加载部分

一个class文件想要执行,需要加载到jvm中才行。流程如下:
java代码 => 编译成class => 读取(class loader) => JVM环境 => 执行

类加载的生命周期

1、类加载的时机

在六种情况下,类必须进行初始化(自然,在初始化之前,加载、验证、准备阶段也都会执行):

  • 遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类型没有进行过初始 化,则需要先触发其初始化阶段
  • 使用java.lang.reflect包的方法对类型进行反射调用的时候(forName),如果类型没有进行过初始化,则需 要先触发其初始化。
  • 当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
  • 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先 初始化这个主类。
  • 当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解 析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句 柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。
  • 当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有 这个接口的实现类发生了初始化,那该接口要在其之前被初始化

2、加载class文件的方式

  • 从本地系统中直接加载
  • 通过网络下载.class文件
  • 从zip,jar等归档文件中加载.class文件
  • 从专有数据库中提取.class文件
  • 将Java源文件动态编译为.class文件

3、类加载器的层次

(这部分主要是了解下 类加载机制的双亲委派)
注意: 这里父类加载器并不是通过继承关系来实现的,而是采用组合实现的。

一切的Java类都必须经过JVM加载后才能运行,加载时会在他们三个之间进行查找。
当我们不指定类加载器的情况下,默认会使用AppClassLoader加载类。

JVM类加载机制 之 双亲委派
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。

4、类的加载

类加载有三种方式:

  1. 命令行启动应用时候由JVM初始化加载
  2. 通过Class.forName()方法动态加载
  3. 通过ClassLoader.loadClass()方法动态加载

Class.forName加载pojo类 示例:

1
2
3
4
//使用Class.forName()来加载类,并指定ClassLoader,初始化时不执行静态块 
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> c12 = Class.forName("demo18.Person", false, systemClassLoader);
c12.newInstance();

ClassLoader.loadClass加载pojo类 示例:

1
2
3
4
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass = systemClassLoader.loadClass("demo18.Person");
System.out.println(aClass);
System.out.println(aClass.getName());

类加载流程(调试)

下个断点

进入到loadClass方法

1
2
3
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}

if (this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
this.resolveClass(var5);
}

return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}

进到super.loadClass(var1, var2);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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;
}
}

进入到findLoadedClass方法

1
2
3
4
5
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}

findLoadedClass方法检查TestHelloWorld类是否已经初始化,如果JVM已初始化过该类则直接返回类对象。

接着,追findLoadedClass0方法。
又回到loadClass方法。如果当前loadclass时传入父类加载器,就使用父类加载器加载。(这里已经使用父类加载器加载了,我们原本在app,已经到ext了)
否则使用JVM的Bootstrap ClassLoader加载。

如果上面的判断都不能加载Person类,那么调用自身的findClass方法尝试加载。

然后追踪findClass方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}

如果当前类重写了findClass方法并通过传入的demo18.Person类名找到了对应的类字节码,那么应该调用defineClass方法去JVM中注册该类。

追踪defineClass方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private Class<?> defineClass(String name, Resource res) throws IOException {
long t0 = System.nanoTime();
int i = name.lastIndexOf('.');
URL url = res.getCodeSourceURL();
if (i != -1) {
String pkgname = name.substring(0, i);
// Check if package already loaded.
Manifest man = res.getManifest();
definePackageInternal(pkgname, man, url);
}
// Now read the class bytes and define the class
java.nio.ByteBuffer bb = res.getByteBuffer();
if (bb != null) {
// Use (direct) ByteBuffer:
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, bb, cs);
} else {
byte[] b = res.getBytes();
// must read certificates AFTER reading bytes.
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, b, 0, b.length, cs);
}
}

如果调用loadClass的时候传入的resolve参数为true,那么还需要调用resolveClass方法链接类,默认为false。

最后返回一个被JVM加载后的java.lang.Class类对象。