JAVA反序列化

序列化与反序列化

什么是序列化与反序列化

Java序列化是指把Java对象转换为字节序列的过程便于保存在内存、文件、数据库中,而Java反序列化则就是把字节序列恢复为Java 对象的过程

序列化与反序列化的用处

Java反序列化与反序列化可以使Java对象脱离运行环境,有效的实现多平台之间的通信、对象持久化存储。

RMI(远程调用Remote Method Invocation)就要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样

怎样实现序列化和反序列化

实现序列化需要被序列化的类实现SerializableExternalizable接口,一般是实现Serializable接口

序列化需要用到ObjectOutputStream类的writeObject方法

反序列化需要用到ObjectInputStream类的readObject方法

示例代码如下

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
import java.io.*;

public class Person implements Serializable { //被序列化的类
public final String name;
public final String age;
public Person(String name, String age) {
this.name = name;
this.age = age;
}
}


class WriteObject {
public static void main(String[] args) {
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Person.ser")); //存储路径和文件名
Person p = new Person("baby", "12");
oos.writeObject(p); //序列化对象
oos.close();
} catch (IOException i) {
i.printStackTrace();
}
}
}

class ReadObject{
public static void main(String[] args){
Person e;
try
{
FileInputStream fileIn = new FileInputStream("Person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Person) in.readObject(); //反序列化
in.close();
fileIn.close();
}catch(IOException i)
{
i.printStackTrace();
return;
}catch(ClassNotFoundException c)
{
System.out.println("Person class not found");
c.printStackTrace();
return;
}
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.age);
}
}

运行代码,就会在当前目录生成一个Person.ser的文件,存储的就是Person类实例化后对象的序列化信息

自定义反序列化行为

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
import java.io.*;
import java.lang.reflect.Method;

public class test{
public static void main(String args[]) throws Exception{
//定义myObj对象
MyObject myObj = new MyObject();
myObj.name = "hi";
//创建一个包含对象进行反序列化信息的”object”数据文件
FileOutputStream fos = new FileOutputStream("object");
ObjectOutputStream os = new ObjectOutputStream(fos);
//writeObject()方法将myObj对象写入object文件
os.writeObject(myObj);
os.close();
//从文件中反序列化obj对象
FileInputStream fis = new FileInputStream("object");
ObjectInputStream ois = new ObjectInputStream(fis);
//恢复对象
MyObject objectFromDisk = (MyObject)ois.readObject();
System.out.println(objectFromDisk.name);
ois.close();
}
}

class MyObject implements Serializable{
public String name;
//重写readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
//执行默认的readObject()方法
in.defaultReadObject();
//执行打开计算器程序命令
Runtime.getRuntime().exec("calc");
}
}

因为class MyObject实现了Serializable接口并重写了readObject方法,故前面的代码执行到反序列化时就会触发自定义的代码,弹出计算器

JAVA反射

参考:Java反序列化漏洞学习实践二:Java的反射机制

Java反射是指Java程序在运行过程中可以动态加载一个只有名称的类,且对于任意一个已加载的类都能知道这个类的所有方法和属性,并且对于任意一个对象都能调用这个已加载类的任意方法和属性

class对象的获取

  • 实例对象的getClass方法
  • 类的.class属性
  • Class.forName(String class_name)动态加载类

TIPS:使用.forName()来加载类会自动初始化该类的对象,而使用.class来加载则不会自动初始化

获取方法类

例子如下

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
package test;
import java.lang.reflect.Method;

public class test{
public static void main(String[] args) throws ClassNotFoundException {
String path = "test.User";
Class class_1 = Class.forName(path);
Method[] methods = class_1.getMethods(); //获取类方法的集合
for(Method method : methods){
if(method.getName().equals("getName")) {
System.out.println("method = " + method.getName());

Class[] parameterTypes = method.getParameterTypes();//获取方法的参数
Class returnType = method.getReturnType();//获取方法的返回类型
try {
User user = (User)class_1.newInstance();
Object x = method.invoke(user);//user.getName();
//Object x = method.invoke(new test(1), 666);
//new关键字能调用任何构造方法,newInstance()只能调用无参构造方法。但反射的场景中是不应该有机会使用new关键词的。
System.out.println(x);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

class User{
private Integer age;
private String name;

public User() {}
public User(String name,Integer age){ //构造函数,初始化时执行
this.age = age;
this.name = name;
}

public Integer getAge() {
return age;
}

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

public String getName() {
return name;
}

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

访问类私有属性/方法

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
package Step2;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/*
* 测试setAccessible方法,可以通过将它设置为true--setAccessible(true) 来访问private属性和函数。
* 而且可以提高程序的执行效率,因为减少了安全检查。
*/
public class reflectionTest3 {

public static void main(String[] args){
try {
String path = "Step2.User3";
Class clazz = Class.forName(path);

//Method method = clazz.getMethod("setName",String.class);
//getMethod只能获取public的方法,private的方法需要使用getDeclaredMethod来获取,并且设置setAccessible(true)才可以调用访问。
//参数属性也是一样。
Method method = clazz.getDeclaredMethod("setName", String.class);
method.setAccessible(true);

//Constructor strut = clazz.getConstructor(String.class,Integer.class);
//getConstructor只能获取public的构造方法
Constructor strut = clazz.getDeclaredConstructor(String.class,Integer.class);
strut.setAccessible(true);
User3 user = (User3)strut.newInstance("bit4",19);
//调用自定义构造器的方法
Object x = method.invoke(user,"比特");//第一个参数是类的对象。第二参数是函数的参数
System.out.println(user.getName());


} catch (Exception e1) {
e1.printStackTrace();
}
}
}

class User3{

private Integer age;
private String name;

private User3() {}

private User3(String name,Integer age){ //构造函数,初始化时执行
this.age = age;
this.name = name;
}


private Integer getAge() {
return age;
}

private void setAge(Integer age) {
this.age = age;
}

public String getName() {
return name;
}

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

通过反射方法来命令执行

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
package test;

import java.io.*;
import java.lang.reflect.Method;

public class test{
public static void main(String args[]) throws Exception{
//定义myObj对象
MyObject myObj = new MyObject();
myObj.name = "hi";
//创建一个包含对象进行反序列化信息的”object”数据文件
FileOutputStream fos = new FileOutputStream("object");
ObjectOutputStream os = new ObjectOutputStream(fos);
//writeObject()方法将myObj对象写入object文件
os.writeObject(myObj);
os.close();
//从文件中反序列化obj对象
FileInputStream fis = new FileInputStream("object");
ObjectInputStream ois = new ObjectInputStream(fis);
//恢复对象
MyObject objectFromDisk = (MyObject)ois.readObject();
System.out.println(objectFromDisk.name);
ois.close();
}
}

class MyObject implements Serializable{
public String name;
//重写readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
//执行默认的readObject()方法
in.defaultReadObject();
//反射调用计算器
try{
Method method = java.lang.Runtime.class.getMethod("exec", String.class);
Object result = method.invoke(Runtime.getRuntime(),"calc");
}catch (Exception e){
e.printStackTrace();
}
}
}

评论