java基础
文件类型
.java //文件为可编写的代码文件
.class //文件为.java 文件编译后的字节码文件
.jar //文件为压缩包,压缩了很多.class 字节码文件
.jsp //文件为 java 和 html 混写的文件
编译
可以使用命令
javac test.java
将.java
文件编译为.class
文件运行
java test.class
Java 中的访问修饰符
在Java中,有四种访问修饰符,用于限定类、方法和变量的访问权限。这些访问修饰符决定了其他类是否可以访问被修饰的类、方法或变量。
public
被声明为 public 的类、方法或变量可以被任何其他类访问,无论这些类是否属于同一个包。这是最宽松的访问级别。
protected
被声明为 protected 的类、方法或变量可以被同一包中的其他类访问,以及其他包中的子类访问。它允许子类访问父类的成员,但不允许其他类访问。
default
默认,不使用关键字修饰。如果类、方法或变量没有显式地使用访问修饰符进行声明,那么它们的访问权限为默认级别。默认级别下,类、方法或变量可以被同一包中的其他类访问,但不能被其他包中的类访问。
private
被声明为 private 的类、方法或变量只能被定义它们的类内部访问,其他类无法访问。这是最严格的访问级别。
序列化
接口
被序列化的类在声明的时候需要实现 Serializable
接口,添加 implements Serializable
字段
import java.io.Serializable;
public class Student implements Serializable{
public String name;
public int age;
}
序列化步骤
1.创建FileOutputStream对象,目的时将序列化的内容输出到文件
2.创建ObjectOutputStream对象,作用是创建一个对象输出流,以便将 Java 对象序列化为字节流并写入文件中。
3.使用 writeObject 函数将对象序列化为字节流,并使用第二步创建的ObjectOutputStream对象将该字节流写入文件中。注意writeObject 函数在序列化的过程中针对的时对象本身而非针对类
序列化实现
新建文件创建,类,在类中定义主函数
主函数中创建 Student 类的对象 test1,并为其属性赋值
// Serlect.java
public class Select {
public static void main(String[] args) {
Student test1 = new Student();
test1.name = "ZhangSan";
test1.age = 18;
}
}
在 Select 类中
编写一个序列化的函数,将其命名为 serialize(),并接受参数,参数为一个对象
public static void serialize(Object out) throws IOException {
// 打开文件并执行写入操作
FileOutputStream FileOutputStream = new FileOutputStream("gjl.bin");
// 创建一个对象输出流
ObjectOutputStream ObjectOutputStream = new ObjectOutputStream(FileOutputStream);
// 将名为 test1 对象序列化为字节流,创建的 ObjectOutputStream 对象将该字节流写入文件中
ObjectOutputStream.writeObject(out);
}
反序列化
反序列化步骤
1.创建 FileInputStream 对象,并指定文件路径,文件中读取字节流数据。
2.创建 ObjectInputStream 对象,并将其与上一步创建的FileInputStream对象关联,将读取的字节数据反序列化为 Java 对象。
3.返回或输出 Java 对象
反序列化实现
在 Select 类中编写一个反序列化的函数,将其命名为 unserialize(),并接受文件路径参数
处理完报错异常后的代码如下:
public static Object unserialize(String FilePath) throws IOException, ClassNotFoundException {
// 读取指定文件的字节流数据
FileInputStream FileInputStream = new FileInputStream(FilePath);
// 将字节流数据转换为 java 对象
ObjectInputStream ObjectInputStream = new ObjectInputStream(FileInputStream);
return ObjectInputStream.readObject();
}
反序列化漏洞
原理
-
漏洞原因:
对
readObject()
函数重写,经过链式调用,最终调用到了执行系统命令的函数 -
关键点
服务端重写
readObject()
函数链式调用
重写原因
如传输过程中对序列化后的数据编码、加密,数据传到
readObject()
时需要对接收的数据进行解码、解密等操作,因此会对 readObject 方法进行重写
漏洞模拟
可以在 Student() 类中重写 readObject()
函数模拟命令执行漏洞
// Student.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Student implements Serializable{
public String name;
public int age;
// 当一个对象从字节流中被反序列化时,如果对象具有 readObject(ObjectInputStream in) 函数,则自动执行
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
Runtime.getRuntime().exec("calc");
// 默认的反序列化操作
in.defaultReadObject();
}
}