首页 > 系统相关 >Java Agent到内存马(一)

Java Agent到内存马(一)

时间:2022-10-07 23:55:32浏览次数:72  
标签:javaagent Java Agent attach 内存 java com public 加载

目录

关于Java Agent

介绍

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

使用方式

Java agent的使用方式有两种:

  • 实现premain方法,在JVM启动前加载。

    流程图(待加)

  • 实现agentmain方法,在JVM启动后加载。

    流程图(待加)

premainagentmain函数声明如下,拥有Instrumentation inst参数的方法优先级更高:

也就是jvm会优先加载带Instrumentation的方法,且加载成功则忽略第二种

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) {
    ...
}
  • agentArgs为Java agent参数
  • init是java.lang.instrument.Instrumentation 的实例,可以用来类定义的转换和操作等等。

premain

JVM启动时 会先执行 premain 方法,大部分类加载都会通过该方法,注意:是大部分,不是所有。遗漏的主要是系统类,因为很多系统类先于 agent 执行,而用户类的加载肯定是会被拦截的。也就是说,这个方法是在 main 方法启动前拦截大部分类的加载活动,既然可以拦截类的加载,就可以结合第三方的字节码编译工具,比如ASM,javassist,cglib等等来改写实现类。

  1. 创建一个目标程序并编译成jar

    package com.helloworld.helloworld;
    
    public class HelloworldApplication {
    
        public static void main(String[] args) {
            System.out.println("Target main run!");
        }
    
    }
    

    image-20220919182839380

  2. 创建premain方法的Agent

    import java.lang.instrument.Instrumentation;
    
    public class PreDemo {
        public static void premain(String args, Instrumentation inst) throws Exception{
            System.out.println("Premain agent run!");
        }
    }
    
  3. 因为java默认为main入口,如果直接打包成jar,会缺少main方法报错

    image-20220927111138646

    所以需要在src/main/resources/加一个META-INF/MANIFEST.MF文件,指定入口

    最后一行需要空行,不能省略

    Manifest-Version: 1.0
    Premain-Class: com.n0r4h.javaagent.PremainDemo
    
    
  4. 只添加MANIFEST.MF文件是不行的,同样会报找不到main方法,pom.xml需要添加:

        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.1.0</version>
                    <configuration>
                        <archive>
                            <!--自动添加META-INF/MANIFEST.MF -->
                            <manifest>
                                <addClasspath>true</addClasspath>
                            </manifest>
                            <manifestEntries>
                                <Premain-Class>com.n0r4h.javaagent.PremainDemo</Premain-Class>
                                <Agent-Class>com.n0r4h.javaagent.AgentmainDemo</Agent-Class>
                                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                <Can-Retransform-Classes>true</Can-Retransform-Classes>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    注意此处version是3.1.0,springboot默认是3.8.x版本,不支持标签会报:Element archive is not allowed here

  5. 最后打包成jar, 执行:java -javaagent:javaagent-0.0.1-SNAPSHOT.jar -jar helloworld-0.0.1-SNAPSHOT.jar

    image-20220927141212359

  6. 可以看到在执行第二个jar之前就是执行了 com.n0r4h.javaagent.PremainDemo#premain方法

    此处流程图为:(参考引用文章):

    image-20220927141417851

我们能联想到这种方式有一些局限性,就是需要在启动之前指定-javaagent,在我们实际环境中,基本都是运行状态,所以无法预先加载premain方法。

agentmain方式

  1. JDK1.6后增加了agentmain方式,测试agentmain前面步骤一致,后面META-INF/MANIFEST.MF中需要添加:

    Manifest-Version: 1.0
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    Premain-Class: com.n0r4h.javaagent.PremainDemo
    Agent-Class: com.n0r4h.javaagent.AgentmainDemo
    
    

    这种方式是在JVM运行之后再加载,所以官方提供了对应的Attach API来实现这个功能。而Attach API中有两个重要的类,在com.sun.tools.attach中,分别是VirtualMachineVirtualMachineDescriptor,我们主要看VirtualMachine

  2. VirtualMachine类

    翻译过来是就是一个java虚拟机,就是程序需要监控的目标虚拟机,主要提供了以下几个方法:

    Attach :从 JVM 上面解除一个代理等方法,可以实现的功能可以说非常之强大 。该类允许我们通过给attach方法传入一个jvm的pid(进程id),远程连接到jvm上

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

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

    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) { ... }
    }
    
  3. 下面是一个获取java程序进程id的方法:

    package com.n0r4h.javaagent;
    
    import com.sun.tools.attach.VirtualMachine;
    import com.sun.tools.attach.VirtualMachineDescriptor;
    
    import java.util.List;
    
    public class test {
        public static void main(String[] args) {
            List<VirtualMachineDescriptor> list = VirtualMachine.list();
            for (VirtualMachineDescriptor virtualMachineDescriptor : list) {
                System.out.println(virtualMachineDescriptor+"\n"+virtualMachineDescriptor.id());
            }
        }
    }
    

    image-20220927150050375

    有了进程id之后就可以使用attach api注入Agent了。

  4. 添加agentmain.java:

    package com.n0r4h.javaagent;
    
    import java.lang.instrument.Instrumentation;
    
    public class AgentmainDemo {
        public static void agentmain(String args, Instrumentation inst) throws Exception{
            System.out.println("hello I`m agentmain agent!!!");
        }
    }
    

    打包成jar文件

    这里打包有一个坑会找不到程序包com.sun.tools.attach,可以直接导入绝对路径

    <dependency>
        <groupId>com.sun</groupId>
        <artifactId>tools</artifactId>
        <version>1.8.0</version>
        <scope>system</scope>
        <systemPath>/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/tools.jar</systemPath>
    </dependency>
    
  5. 测试:

    package com.n0r4h.javaagent;
    
    import com.sun.tools.attach.*;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.List;
    
    public class test {
        public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
    
            System.out.println("main running");
            File directory = new File("");
    
            directory.getAbsolutePath(); //获取绝对路径。
            System.out.println(directory.getAbsolutePath());
    
            List<VirtualMachineDescriptor> list = VirtualMachine.list();
            for (VirtualMachineDescriptor vir : list) {
                System.out.println(vir.displayName());//打印JVM加载类名
                if (vir.displayName().endsWith("com.n0r4h.javaagent.test")) {
                    VirtualMachine attach = VirtualMachine.attach(vir.id());   //attach注入一个jvm id注入进去
                    attach.loadAgent("javaagent-0.0.1-SNAPSHOT.jar");//加载agent
                    attach.detach();
                }
            }
    
        }
    }
    

    image-20220927154345026

    成功在jvm启动后加载Agentmain。

    执行流程引用先知师傅:

    image-20220927154656504

参考

https://xz.aliyun.com/t/9450

https://mp.weixin.qq.com/s/9oiMMPNLeGU9fc19Abij6A

https://www.cnblogs.com/nice0e3/p/14086165.html

标签:javaagent,Java,Agent,attach,内存,java,com,public,加载
From: https://www.cnblogs.com/N0r4h/p/16767537.html

相关文章

  • Java流程控制复习
    Java流程控制复习1.用户交互Scanner通过Scanner类的next()和nextLine()方法获取输入的字符串注意:next():一定要读取到有效字符后才可以结束输入对于输入有效字......
  • 服务器Linux系统(Ubuntu)安装Java
    下载Java  前往Java官网找到对应版本链接并右键复制链接  这里使用的是Ubuntu云服务器于是下载Linuxx64CompressedArchieve,复制该项链接  用管理员账户登......
  • 进程、内存指令
    目录查看进程信息:ps动态显示进程:top终止进程:kill检测磁盘空间:df检测目录所占磁盘空间:du查看进程信息:ps进程是一个具有一定独立功能的程序,它是操作系统动态执行的基本单元......
  • 前端内存泄露浅析
    手上负责的vue项目最近出现一个这样的问题,用户用着用着就出现:”喔唷,崩溃啦!“的提示。做了以下性能优化尝试:主动销毁对象及其子对象主动取消监听listener本地搜索减少组件DOM......
  • java 范型的通配符
    一、需要通配符的原因假设类Manager继承Employee,并且有如下的范型Pair类classPair<T>{privateTfirst;privateTsecond;publicPair(){......
  • java中,如何解决@NotBlank不生效的问题 @NotBlank @NotEmpty不生效,以及对象嵌套问题
    这篇文章主要介绍了如何解决@NotBlank不生效的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教。1.解决@NotBlank不生效最近做一个新......
  • Java19虚拟线程都来了,我正在写的线程代码会被淘汰掉吗?
    Java19中引入了虚拟线程,虽然默认是关闭的,但是可以以Preview模式启用,这绝对是一个重大的更新,今天Java架构杂谈带大家开箱验货,看看这家伙实现了什么了不起的功能。1为什么......
  • Java俱乐部社团面试记录
    Java俱乐部社团面试记录1.申请表自身能拿出手的太少了,能写在申请表上的除了自己的热情,近乎没有,该打打大大小小的比赛了2.个人介绍自己过于紧张,一进门脑子只剩空气了3.......
  • Skywalking光会用可不行,必须的源码分析分析 - Skywalking Agent &插件解析
    3Skywalking源码导入接上文,已经学习了Skywalking的应用,接下来我们将剖析Skywalking源码,深度学习SkywalkingAgent。3.1源码环境搭建当前最新版本是8.3.0,我们首先找到8.3.......
  • Skywalking应用实战 Agent探针、Rocketbot以及告警
    3Skywalking应用相关术语:skywalking-collector:链路数据归集器,数据可以落地ElasticSearch/H2skywalking-ui:web可视化平台,用来展示落地的数据skywalking-agent:探针,用来收......