-
-
[原创]Java安全小白的入门心得 - java反序列化(一)
-
发表于: 2022-10-20 09:29 1224
-
Java反序列化是java安全的基础,想要学好java反序列化,就不能只看看相关文章,要自己动手实践,看看java反序列化到底是怎么回事。
Java Serialization(序列化):将java对象以一连串的字节保存在磁盘文件中的过程,也可以说是保存java对象状态的过程。序列化可以将数据永久保存在磁盘上(通常保存在文件中)。
下面我们就手敲代码,自己实现一个序列化程序!
注意点:
因此,我们整个实现过程为:创建需要序列化的对象、创建文件对象、创建文件输出流对象、创建对象输出流对象、序列化
运行程序,我们得到1.txt
可以看到,在java_security模块下生成了1.txt文件,里面包含着innerClass对象(即我刚刚序列化的对象)的序列化字节码
这些字节码都是我们人为不可看的,很不利于我们在对于java反序列化或者java安全方面的研究,有什么办法能解决这个问题呢?
我们可以使用SerializationDumper来将序列化字节码转化为方便阅读的形式,下面我们就一起来装一下SerializationDumper叭
进入安装路径,执行build.bat 文件
然后就可以在该目录中使用SerializationDumper.jar了,接下来我们就试试使用SerializationDumper
将SerializationDumper拖入项目
按照上述使用方法 使用 -r 处理raw-data文件
这里就可以很清楚的看到,跟我们之前设定的属性是相符合的。
与序列化的代码片相反,反序列化将文件内的字节流重新反序列化为对象。反序列化流程如上,便不再赘述。
###
经过上面简单的案例,大家应该能了解到序列化与反序列化的大体步骤,接下来就开始了解readObject和writeObject的复写。
进阶的一些小trick
我们看到类实现的Serializable 接口,它是没有任何内容的,相当于一个标识符。
那么我们该怎么复写readObject和writeObject呢。分析源码
首先从writeObject方法进了writeObject0
跟踪语句,我们找到了这样一句,若obj或其子类实现了Serializable,则进入这个判断语句,即进入writeOrdinaryObject方法
这里按按实现了Externalizable
接口或Serializable
接口分别执行writeExternalData
和writeSerialData
方法,我们这里进入writeSerialData
方法。
进入hasWriteObjectMethod方法
康康writeObjectMethod变量
这里可以看到了,如果类中定义了 writeObject 。查找该变量康康。
好!这里可以看到,利用了java反射得到对象的writeObject方法,这里就说判断序列化对象中是否含有writeObject方法
回到writeSerialData
方法
看到这里,我们基本上就可以得出结论了:
要重写readObject和writeObject方法,只需要在需要序列化和反序列化中的类中写相应的方法。简单的说,以readObject方法为例,在ObjectInputStream对象调用readObject时,经过一系列调用,检测你需要序列化对象中是否含有readObject,如果有则通过java反射特性,得到需要序列化对象的readObject方法,否则使用默认的readObject方法!
接下来,我们来改写一下inner类,加入readObject和writeObject方法
运行结果无疑是成功了。
再康康序列化出来的结构
最后多出来一节
这就意味着我们可以在序列化时向字节码中写入一些这个对象的属性以外的东西。这个特性就让Java的开发变得非常灵活。
本文初步介绍了java反序列化步骤,自己动手实实在在的实现了基本的序列化与反序列化。利用SerializationDumper看到了序列化字节码的基本结构。对readObject与writeObject方法进行源码跟进,理解了为什么Serializable是空接口的情况下,我们可以在需要反序列化的类内部直接重写readObject与writeObject方法,实质是ObjectOutputStream对象经过调用链,利用java反射,获取了类中的方法,从而执行在类中的readObject与writeObject方法。
java反序列化的学习还在继续,下一章我将和大家一起学习java中一些简单的反序列化漏洞。
JSON和XML是通用数据交互格式,通常用于不同语言、不同环境下数据的交互,比如前端的JavaScript通过JSON和后端服务通信、微信服务器通过XML和公众号服务器通信。但这两个数据格式都有一个共同的问题:不支持复杂的数据类型。大多数处理方法中,JSON和XML支持的数据类型就是基本数据类型,整型、浮点型、字符串、布尔等,如果开发者希望在传输数据的时候直接传输一个对象,那么就不得不想办法扩展基础的JSON(XML)语法。
JSON和XML是通用数据交互格式,通常用于不同语言、不同环境下数据的交互,比如前端的JavaScript通过JSON和后端服务通信、微信服务器通过XML和公众号服务器通信。但这两个数据格式都有一个共同的问题:不支持复杂的数据类型。大多数处理方法中,JSON和XML支持的数据类型就是基本数据类型,整型、浮点型、字符串、布尔等,如果开发者希望在传输数据的时候直接传输一个对象,那么就不得不想办法扩展基础的JSON(XML)语法。
public
class
main {
private static
class
innerClass implements Serializable {
String name;
String test;
int
years;
public innerClass(){}
public innerClass(String name, String test,
int
years) {
this.name
=
name;
this.test
=
test;
this.years
=
years;
}
@Override
public String toString() {
return
"innerClass{"
+
"name='"
+
name
+
'\''
+
", test='"
+
test
+
'\''
+
", years="
+
years
+
'}'
;
}
}
public static void main(String[] args) throws Exception {
innerClass ic
=
new innerClass();
/
/
创建对象
ic.name
=
"123"
;
ic.test
=
"test"
;
ic.years
=
123546
;
File
f
=
new
File
(
"java_security/1.txt"
);
/
/
模块名
/
文件名
if
(f.exists()) {
System.out.println(
"文件存在"
);
}
else
{
/
/
否则创建新文件
f.createNewFile();
}
try
{
FileOutputStream fos
=
new FileOutputStream(f);
ObjectOutputStream oos
=
new ObjectOutputStream(fos);
oos.writeObject(ic);
/
/
将ic对象序列化写入文件
oos.flush();
oos.close();
fos.close();
}catch (Exception e) {
System.out.println(e);
}
}
}
public
class
main {
private static
class
innerClass implements Serializable {
String name;
String test;
int
years;
public innerClass(){}
public innerClass(String name, String test,
int
years) {
this.name
=
name;
this.test
=
test;
this.years
=
years;
}
@Override
public String toString() {
return
"innerClass{"
+
"name='"
+
name
+
'\''
+
", test='"
+
test
+
'\''
+
", years="
+
years
+
'}'
;
}
}
public static void main(String[] args) throws Exception {
innerClass ic
=
new innerClass();
/
/
创建对象
ic.name
=
"123"
;
ic.test
=
"test"
;
ic.years
=
123546
;
File
f
=
new
File
(
"java_security/1.txt"
);
/
/
模块名
/
文件名
if
(f.exists()) {
System.out.println(
"文件存在"
);
}
else
{
/
/
否则创建新文件
f.createNewFile();
}
try
{
FileOutputStream fos
=
new FileOutputStream(f);
ObjectOutputStream oos
=
new ObjectOutputStream(fos);
oos.writeObject(ic);
/
/
将ic对象序列化写入文件
oos.flush();
oos.close();
fos.close();
}catch (Exception e) {
System.out.println(e);
}
}
}
1
、序列化对象需要实现Serializable接口
2
、序列化需要使用ObjectOutputStream对象创建对象输出流
3
、ObjectOutputStream对象序列化所用方法writeObject()
4
、ObjectOutputStream对象需要文件输出流作为输出目标
5
、FileOutputStream对象需要一个文件对象
1
、序列化对象需要实现Serializable接口
2
、序列化需要使用ObjectOutputStream对象创建对象输出流
3
、ObjectOutputStream对象序列化所用方法writeObject()
4
、ObjectOutputStream对象需要文件输出流作为输出目标
5
、FileOutputStream对象需要一个文件对象
git clone https:
/
/
github.com
/
NickstaDB
/
SerializationDumper.git
git clone https:
/
/
github.com
/
NickstaDB
/
SerializationDumper.git
E:\web
-
Tools\SerializationDumper> build.bat
E:\web
-
Tools\SerializationDumper> build.bat
E:\IntelliJ IDEA
2018.2
.
7
\project\java_security>java
-
jar SerializationDumper.jar
Usage:
SerializationDumper <
hex
-
ascii
-
data>
SerializationDumper
-
f <
file
-
containing
-
hex
-
ascii>
SerializationDumper
-
r <
file
-
containing
-
raw
-
data>
Rebuild a dumped stream:
SerializationDumper
-
b <
input
-
file
> <output
-
file
>
E:\IntelliJ IDEA
2018.2
.
7
\project\java_security>java
-
jar SerializationDumper.jar
Usage:
SerializationDumper <
hex
-
ascii
-
data>
SerializationDumper
-
f <
file
-
containing
-
hex
-
ascii>
SerializationDumper
-
r <
file
-
containing
-
raw
-
data>
Rebuild a dumped stream:
SerializationDumper
-
b <
input
-
file
> <output
-
file
>
E:\IntelliJ IDEA
2018.2
.
7
\project\java_security>java
-
jar SerializationDumper.jar
-
r
1.txt
>
2.txt
STREAM_MAGIC
-
0xac
ed
STREAM_VERSION
-
0x00
05
Contents
TC_OBJECT
-
0x73
TC_CLASSDESC
-
0x72
className
Length
-
15
-
0x00
0f
Value
-
main$innerClass
-
0x6d61696e24696e6e6572436c617373
serialVersionUID
-
0xca
3e
75
e0
69
b7
50
c5
newHandle
0x00
7e
00
00
classDescFlags
-
0x02
-
SC_SERIALIZABLE
fieldCount
-
3
-
0x00
03
Fields
0
:
Int
-
I
-
0x49
fieldName
Length
-
5
-
0x00
05
Value
-
years
-
0x7965617273
1
:
Object
-
L
-
0x4c
fieldName
Length
-
4
-
0x00
04
Value
-
name
-
0x6e616d65
className1
TC_STRING
-
0x74
newHandle
0x00
7e
00
01
Length
-
18
-
0x00
12
Value
-
Ljava
/
lang
/
String;
-
0x4c6a6176612f6c616e672f537472696e673b
2
:
Object
-
L
-
0x4c
fieldName
Length
-
4
-
0x00
04
Value
-
test
-
0x74657374
className1
TC_REFERENCE
-
0x71
Handle
-
8257537
-
0x00
7e
00
01
classAnnotations
TC_ENDBLOCKDATA
-
0x78
superClassDesc
TC_NULL
-
0x70
newHandle
0x00
7e
00
02
classdata
main$innerClass
values
years
(
int
)
123546
-
0x00
01
e2
9a
name
(
object
)
TC_STRING
-
0x74
newHandle
0x00
7e
00
03
Length
-
3
-
0x00
03
Value
-
123
-
0x313233
test
(
object
)
TC_STRING
-
0x74
newHandle
0x00
7e
00
04
Length
-
4
-
0x00
04
Value
-
test
-
0x74657374
E:\IntelliJ IDEA
2018.2
.
7
\project\java_security>java
-
jar SerializationDumper.jar
-
r
1.txt
>
2.txt
STREAM_MAGIC
-
0xac
ed
STREAM_VERSION
-
0x00
05
Contents
TC_OBJECT
-
0x73
TC_CLASSDESC
-
0x72
className
Length
-
15
-
0x00
0f
Value
-
main$innerClass
-
0x6d61696e24696e6e6572436c617373
serialVersionUID
-
0xca
3e
75
e0
69
b7
50
c5
newHandle
0x00
7e
00
00
classDescFlags
-
0x02
-
SC_SERIALIZABLE
fieldCount
-
3
-
0x00
03
Fields
0
:
Int
-
I
-
0x49
fieldName
Length
-
5
-
0x00
05
Value
-
years
-
0x7965617273
1
:
Object
-
L
-
0x4c
fieldName
Length
-
4
-
0x00
04
Value
-
name
-
0x6e616d65
className1
TC_STRING
-
0x74
newHandle
0x00
7e
00
01
Length
-
18
-
0x00
12
Value
-
Ljava
/
lang
/
String;
-
0x4c6a6176612f6c616e672f537472696e673b
2
:
Object
-
L
-
0x4c
fieldName
Length
-
4
-
0x00
04
Value
-
test
-
0x74657374
className1
TC_REFERENCE
-
0x71
Handle
-
8257537
-
0x00
7e
00
01
classAnnotations
TC_ENDBLOCKDATA
-
0x78
superClassDesc
TC_NULL
-
0x70
newHandle
0x00
7e
00
02
classdata
main$innerClass
values
years
(
int
)
123546
-
0x00
01
e2
9a
name
(
object
)
TC_STRING
-
0x74
newHandle
0x00
7e
00
03
Length
-
3
-
0x00
03
Value
-
123
-
0x313233
test
(
object
)
TC_STRING
-
0x74
newHandle
0x00
7e
00
04
Length
-
4
-
0x00
04
Value
-
test
-
0x74657374
try
{
FileInputStream fis
=
new FileInputStream(
"java_security/1.txt"
);
ObjectInputStream ois
=
new ObjectInputStream(fis);
innerClass ic2
=
(innerClass)ois.readObject();
System.out.println(ic2);
ois.close();
fis.close();
}catch(Exception e) {
System.out.println(e);
}
try
{
FileInputStream fis
=
new FileInputStream(
"java_security/1.txt"
);
ObjectInputStream ois
=
new ObjectInputStream(fis);
innerClass ic2
=
(innerClass)ois.readObject();
System.out.println(ic2);
ois.close();
fis.close();
}catch(Exception e) {
System.out.println(e);
}
public final void writeObject(
Object
obj) throws IOException {
if
(enableOverride) {
writeObjectOverride(obj);
return
;
}
try
{
writeObject0(obj, false);
} catch (IOException ex) {
if
(depth
=
=
0
) {
writeFatalException(ex);
}
throw ex;
}
}
public final void writeObject(
Object
obj) throws IOException {
if
(enableOverride) {
writeObjectOverride(obj);
return
;
}
try
{
writeObject0(obj, false);
} catch (IOException ex) {
if
(depth
=
=
0
) {
writeFatalException(ex);
}
throw ex;
}
}
if
(obj instanceof String) {
writeString((String) obj, unshared);
}
else
if
(cl.isArray()) {
writeArray(obj, desc, unshared);
}
else
if
(obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
}
else
if
(obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
}
else
{
if
(extendedDebugInfo) {
throw new NotSerializableException(
cl.getName()
+
"\n"
+
debugInfoStack.toString());
}
else
{
throw new NotSerializableException(cl.getName());
}
}
if
(obj instanceof String) {
writeString((String) obj, unshared);
}
else
if
(cl.isArray()) {
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!