参考链接
fastjson反序列化入门文章
https://tttang.com/archive/1579/
https://xz.aliyun.com/t/12096
ASM动态加载相关,如何查看内存生成的类的源码
https://juejin.cn/post/6974566732224528392#heading-6
https://blog.csdn.net/wjy160925/article/details/85288569
关闭ASM去调试
https://blog.csdn.net/qq_45854465/article/details/120960671
toJSONString()方法的源码分析(较浅)
https://blog.csdn.net/qq_31615049/article/details/85013129
Fastjson流程分析
先写个例子
package fastjson.pojo;
public class User{
private String name;
private int age;
private String hobby;
public User() {
}
public User(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public String getName() {
System.out.println("调用了getName");
return name;
}
public void setName(String name) {
System.out.println("调用了setName");
this.name = name;
}
public int getAge() {
// System.out.println("调用了getAge");
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "user{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
package fastjson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import fastjson.pojo.User;
public class EasyStart extends User {
public static void main(String[] args) {
//序列化对象,会调用getter方法
SerializeConfig.getGlobalInstance().setAsmEnable(false);
User user = new User("Jasper",22,"fuck_some_people");
// String s1 = JSON.toJSONString(user);
String s2 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
System.out.println(s2);
System.out.println("---------------------------------------");
// String s2 = "{\"@type\":\"fastjson.pojo.User\",\"age\":22,\"hobby\":\"fuck_some_people\",\"name\":\"Jasper\"}";
ParserConfig.getGlobalInstance().setAsmEnable(false);
//情况1,parse里面调setter
Object o1 = JSON.parse(s2);
System.out.println(o1);
System.out.println(o1.getClass().getName());
System.out.println("---------------------------------------");
//情况2,parseObject是parse的封装,所以会调setter,另外JSON.toJSON会调getter
Object o2 = JSON.parseObject(s2);
System.out.println(o2);
System.out.println(o2.getClass().getName());
System.out.println("---------------------------------------");
//情况3,parseObject在里边调了setter
Object o3 = JSON.parseObject(s2,Object.class);
System.out.println(o3);
System.out.println(o3.getClass().getName());
System.out.println("---------------------------------------");
}
}
输出结果:
调用了getName
{"@type":"fastjson.pojo.User","age":22,"hobby":"fuck_some_people","name":"Jasper"}
---------------------------------------
调用了setName
user{name='Jasper', age=22, hobby='fuck_some_people'}
fastjson.pojo.User
---------------------------------------
调用了setName
调用了getName
{"name":"Jasper","age":22,"hobby":"fuck_some_people"}
com.alibaba.fastjson.JSONObject
---------------------------------------
调用了setName
user{name='Jasper', age=22, hobby='fuck_some_people'}
fastjson.pojo.User
---------------------------------------
目前来看,序列化会调getter;反序列化会调setter,有的还会调getter。
这里我看大部分文章都没分析为什么会调用,这里我跟了一下源码,下面是分析结果。
序列化分析
get:450, FieldInfo
getPropertyValueDirect:110, FieldSerializer
write:196, JavaBeanSerializer
write:275, JSONSerializer
toJSONString:559, JSON
toJSONString:548, JSON
main:16, EasyStart
get里反射调用了getter
public Object get(Object javaObject) throws IllegalAccessException, InvocationTargetException {
if (method != null) {
Object value = method.
invoke(javaObject, new Object[0]);
return value;
}
return field.get(javaObject);
}
注意:需要用下面的语句把ASM关掉,会进到动态生成的类里,调试不了。
:::info
SerializeConfig.getGlobalInstance().setAsmEnable(false);
:::
反序列化分析
反序列化函数底层大差不差,最多是多了一些判断,这里只以parse()为例:
setValue:96, FieldDeserializer
deserialze:593, JavaBeanDeserializer
deserialze:188, JavaBeanDeserializer
deserialze:184, JavaBeanDeserializer
parseObject:368, DefaultJSONParser
parse:1327, DefaultJSONParser
parse:1293, DefaultJSONParser
parse:137, JSON
parse:128, JSON
main:22, EasyStart
setValue()里有反射调用setter方法:
public void setValue(Object object, Object value) {
if (value == null //
&& fieldInfo.fieldClass.isPrimitive()) {
return;
}
try {
Method method = fieldInfo.method;
if (method != null) {
if (fieldInfo.getOnly) {
if (fieldInfo.fieldClass == AtomicInteger.class) {
AtomicInteger atomic = (AtomicInteger) method.invoke(object);
if (atomic != null) {
atomic.set(((AtomicInteger) value).get());
}
} else if (fieldInfo.fieldClass == AtomicLong.class) {
AtomicLong atomic = (AtomicLong) method.invoke(object);
if (atomic != null) {
atomic.set(((AtomicLong) value).get());
}
} else if (fieldInfo.fieldClass == AtomicBoolean.class) {
AtomicBoolean atomic = (AtomicBoolean) method.invoke(object);
if (atomic != null) {
atomic.set(((AtomicBoolean) value).get());
}
} else if (Map.class.isAssignableFrom(method.getReturnType())) {
Map map = (Map) method.invoke(object);
if (map != null) {
map.putAll((Map) value);
}
} else {
Collection collection = (Collection) method.invoke(object);
if (collection != null) {
collection.addAll((Collection) value);
}
}
} else {
method.invoke(object, value);
}
return;
} else {
final Field field = fieldInfo.field;
if (fieldInfo.getOnly) {
if (fieldInfo.fieldClass == AtomicInteger.class) {
AtomicInteger atomic = (AtomicInteger) field.get(object);
if (atomic != null) {
atomic.set(((AtomicInteger) value).get());
}
} else if (fieldInfo.fieldClass == AtomicLong.class) {
AtomicLong atomic = (AtomicLong) field.get(object);
if (atomic != null) {
atomic.set(((AtomicLong) value).get());
}
} else if (fieldInfo.fieldClass == AtomicBoolean.class) {
AtomicBoolean atomic = (AtomicBoolean) field.get(object);
if (atomic != null) {
atomic.set(((AtomicBoolean) value).get());
}
} else if (Map.class.isAssignableFrom(fieldInfo.fieldClass)) {
Map map = (Map) field.get(object);
if (map != null) {
map.putAll((Map) value);
}
} else {
Collection collection = (Collection) field.get(object);
if (collection != null) {
collection.addAll((Collection) value);
}
}
} else {
if (field != null) {
field.set(object, value);
}
}
}
} catch (Exception e) {
throw new JSONException("set property error, " + fieldInfo.name, e);
}
}
注意:需要用下面的语句把ASM关掉,会进到动态生成的类里,调试不了。
:::info
ParserConfig.getGlobalInstance().setAsmEnable(false);
:::
利用点分析
从输出结果还可以看到,如果序列化的字符串里用@type指定了类名User,那么即使parseObject(s2, Object.class)指定序列化成Object.class,还是会反序列化成User
这意味着,反序列化成什么样的对象,是受传入的序列化字符串控制的,而前面说了Fastjson在反序列化和序列化的时候,都会自动调用getter和setter方法,那么如果我们传入有危险getter/setter方法的类的序列化字符串就能利用了。
1.2.24漏洞调试
还有很多东西没学,暂时卡住了。