Premain的简单实现
前言
最近对RASP较感兴趣,和我的开发同学@XXXFREDYANG一起搭搭环境,实现一下小demo
前置知识:
Agent
Agent 在JDK1.5以后,我们可以使用agent技术构建一个独立于应用程序的代理程序(即为Agent),用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能。
Javaagent是java命令的一个参数。参数 javaagent 可以用于指定一个 jar 包,并且对该 java 包有2个要求:
1.这个 jar 包的 MANIFEST.MF 文件必须指定 Premain-Class 项。
2.Premain-Class 指定的那个类必须实现 premain() 方法。
premain 方法,从字面上理解,就是运行在 main 函数之前的的类。当Java 虚拟机启动时,在执行 main 函数之前,JVM 会先运行-javaagent所指定 jar 包内 Premain-Class 这个类的 premain 方法 。
在命令行输入 java可以看到相应的参数,其中有 和 java agent相关的:
Attach机制
jvm提供一种jvm进程间通信的能力,能让一个进程传命令给另外一个进程,并让它执行内部的一些操作,比如说我们为了让另外一个jvm进程把线程dump出来,那么我们跑了一个jstack的进程,然后传了个pid的参数,告诉它要哪个进程进行线程dump,既然是两个进程,那肯定涉及到进程间通信,以及传输协议的定义,比如要执行什么操作,传了什么参数等。
这里用到的机制就是attach机制,它可以将JVM A连接至JVM B,并发送指令给JVM B执行,JDK自带常用工具如jstack,jps等就是使用该机制来实现的。这里我们先用tomcat启动一个程序用作主程序B,再来写A程序代码
MANIFEST.MF文件
MANIFEST.MF文件用于描述Jar包的信息,例如指定入口函数等。我们需要在该文件中加入如下配置,指定我们编写的含有premain方法类的全路径,然后将agent类打成Jar包。
1
| /Users/nuc/IdeaProjects/javaAgent/resources/META-INF/MANIFEST.MF
|
当然这里我们用不到,因为我的jar包是直接用maven打包的
实现步骤
1.写agent文件
PremainAgent.java
1 2 3 4 5 6 7 8 9
| package com.l1m3.premainagent.demo; import java.lang.instrument.Instrumentation;
public class PremainAgent { public static void premain(String arg, Instrumentation instrumentation) { System.err.println("agent startup , args is " + arg); } }
|
pom.xml
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.5.RELEASE</version> <relativePath/> </parent> <groupId>com.l1m3.premainAgent</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>premainAgent</name> <description>premainAgent</description>
<properties> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.5.0</version> <scope>test</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency>
</dependencies>
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> <manifestEntries> <Premain-Class>com.l1m3.premainagent.demo.PremainAgent</Premain-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build> </project>
|
重点在于:
这里是你实现premain函数的类
使用maven打包
路径为
/Users/nuc/IdeaProjects/premainAgent/target/demo-0.0.1-SNAPSHOT.jar
2.写main函数测试
MainFuncTestApplication.java
1 2 3 4 5 6 7
| package com.l1m3.mainfunctest.main;
public class MainFuncTestApplication{ public static void main(String[] args){ System.out.println("this is main function"); } }
|
3.测试
这里直接在IDEA里面使用vm option来设置agent,当然命令行参数也可以
-javaagent:/Users/nuc/IdeaProjects/premainAgent/target/demo-0.0.1-SNAPSHOT.jar
输出结果
到此为止就实现了premain函数。