当前位置:   article > 正文

『Java安全』初试JavaAgent实现修改字节码和插桩_agentmain transform 修改字节码

agentmain transform 修改字节码

Java Agent机制

Java Agent提供两个类方法来修改JVM字节码,前者在加载class前修改、后者支持对已加载class的字节码修改并重加载

public static void premain(String agentArgs, Instrumentation inst)
public static void agentmain(String agentArgs, Instrumentation inst)
  • 1
  • 2
  1. premain: 属于Agent模式,启动Java之前添加-javaagent:xxx.jar参数,启动后首先会运行jar包的premain方法
  2. agentmain: 属于attach模式,通过运行过程中的进程id动态修改已加载的字节码

Java Agent程序

MANIFEST.MF

无论是哪种模式,需要将Agent程序打包成jar才能加载,并且拥有/resources/META-INF/MANIFEST.MF,包含以下内容:
(最后一行必须为空)

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

指定Premain-ClassAgent-Class用于确定Agent程序入口,就会调用该类的preman或agentmain方法;
指定Can-Redefine-ClassesCan-Retransform-Classes参数开启修改字节码功能

premain

实现premain方法,并且添加一个自定义transformer,把操作放在自定义的transformer内,该transformer要实现ClassFileTransformer.transform()

主程序运行时:加载每个类前都会进入transform(),可以获取到它的加载器、类名、字节码buffer等

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

public class PremainTest {
    public static void premain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new DefineTransformer(), true);
    }

    static class DefineTransformer implements ClassFileTransformer {
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        	// 输出jvm加载的类的名称
            System.out.println("premain load Class:" + className);
            return classfileBuffer;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述

agentmain

对于agentmain也是一样的流程,但多一步inst.retransformClasses()的操作让JVM重新加载修改过的类的字节码,否则修改不会生效

在transform()内部修改字节码这里使用Javassist作为演示

https://ho1aas.blog.csdn.net/article/details/123205525

......
    public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
        inst.addTransformer(new DefineTransformer(), true);
        Class classes[] = inst.getAllLoadedClasses();
        for (int i = 0; i < classes.length; i++) {
            if (classes[i].getName().equals("TargetClass")) {
                inst.retransformClasses(classes[i]);
                break;
            }
        }
    }
    static class DefineTransformer implements ClassFileTransformer{
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
            if("TargetClass".equals(className)){
                try {
                    ClassPool classPool = ClassPool.getDefault();
                    CtClass clazz = classPool.get(className);
                    CtMethod method = clazz.getDeclaredMethod("testMethod");
                    method.setBody("{return true;}");

                    byte[] bytes = clazz.toBytecode();
                    clazz.detach();
                    return bytes;
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
            return classfileBuffer;
        }
    }
}

  • 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

使用agentmain的Agent jar包写好后,再写一个触发的程序:在jvm搜索待修改类的进程,然后attach进行Agent修改即可

import com.sun.tools.attach.*;
import java.util.List;

public class AgentMainTest {
    public static void main(String[] args) throws Exception{
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        for (VirtualMachineDescriptor desc : list) {
            if(desc.displayName().equals("TargetClass")){
                String pid = desc.id();
                String agentPath = "Agentmain.jar";
                VirtualMachine vm = VirtualMachine.attach(pid);
                vm.loadAgent(agentPath);
                vm.detach();
                break;
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Maven生成Agent jar包

Agent机制只认jar包,还要涉及到MANIFEST编写和打包javassist等依赖,因此使用maven打包就比较方便

使用maven的插件maven-assembly-plugin可以做到自定义jar包名称、自动生成MANIFEST、自动打包依赖

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <!--包名-->
                    <finalName>${project.artifactId}-${project.version}</finalName>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <!--MANIFEST模板,按实际填写-->
                        <manifestEntries>
                            <Premain-Class>PremainTest</Premain-Class>
                            <Agent-Class>PremainTest</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>assembly</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • 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

mvn clean然后mvn install即可

Agent demo示例

代码

目标类

public class Main {
    final static String flag = "0";
    public static void main(String[] args) throws Exception{
        while (true) {
            System.out.println(test(flag));
            Thread.sleep(3000);
        }
    }

    static boolean test(String flag){
        return !flag.equals("0");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

循环输出false

在这里插入图片描述

Agent

import javassist.*;

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

public class PremainTest {
    public static void premain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new JavassistTransformer(), true);
    }

    public static void agentmain(String agentArgs, Instrumentation inst) throws UnmodifiableClassException {
        inst.addTransformer(new JavassistTransformer(), true);
        Class classes[] = inst.getAllLoadedClasses();
        for (int i = 0; i < classes.length; i++) {
            if (classes[i].getName().equals("Main")) {
                inst.retransformClasses(classes[i]);
                break;
            }
        }
    }

    static class JavassistTransformer implements ClassFileTransformer{
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
            if("Main".equals(className)){
                try {
                    ClassPool classPool = ClassPool.getDefault();
                    CtClass clazz = classPool.get(className);

                    CtMethod method = clazz.getDeclaredMethod("test");
					
					// 完全修改test类方法体或者修改返回值
                    
                    // method.insertAfter("return true;");
                    method.setBody("{return flag.equals(\"0\");}");

                    byte[] bytes = clazz.toBytecode();
                    clazz.detach();

                    return bytes;
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
            return classfileBuffer;
        }
    }
}

  • 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

pom.xml下载javassist

<?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>JavassistAgentTest</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>
    </properties>

    <packaging>jar</packaging>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.22.0-GA</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <finalName>${project.artifactId}-${project.version}</finalName>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <manifestEntries>
                            <Premain-Class>PremainTest</Premain-Class>
                            <Agent-Class>PremainTest</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>assembly</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
  • 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

Agent模式运行

IDEA添加vm option -javaagent运行目标类即可

在这里插入图片描述
修改成功

在这里插入图片描述

Attach模式运行

先运行目标类再打开attach模式

在这里插入图片描述
动态修改已加载的字节码成功

在这里插入图片描述

参考

https://ho1aas.blog.csdn.net/article/details/123205525
https://www.cnblogs.com/rickiyang/p/11368932.html
https://javasec.org/javase/JavaAgent/

欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://ho1aas.blog.csdn.net/article/details/126158738
版权声明:本文为原创,转载时须注明出处及本声明

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/450756
推荐阅读
相关标签
  

闽ICP备14008679号