千家信息网

Java中Agent如何动态修改字节码

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要介绍了Java中Agent如何动态修改字节码,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1、什么是Java AgentJ
千家信息网最后更新 2026年11月01日Java中Agent如何动态修改字节码

这篇文章主要介绍了Java中Agent如何动态修改字节码,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

1、什么是Java Agent

Java Agent是一种特殊类型的类,通过使用Java Instrumentation API,它可以拦截JVM上运行的应用程序,修改它们的字节码。Java Agent非常强大,也非常危险。

在开始之前,我将解释Java Agent如何使用简单的HelloWorld示例拦截类。

public class Hello {    public static void main(String[] args){        System.out.println("hello world");    }}

如下图所示:

类加载器负责将类从二进制加载到内存中。运行编译后的HelloWorld应用程序(HelloWorld.class)时,可以将Agent视为在运行时拦截类加载器行为的一种方式。您可能会想,java字节代码是如何被重新构造的,以便Agent可以在正确的位置添加相关代码的。有趣的是,对于Java程序来说,字节码的结构非常接近原始Java程序源代码。因此,虽然我们不为Java程序本身添加工具,但我们使用了它的一个非常接近的表示形式。需要注意的是,有一些非Java语言可以编译成Java字节码(如Scala、Clojure和Kotlin),这意味着程序字节码的结构和形状可能会非常不同。

2、实现Java Agent

JavaAgent基于来自Java平台的facility,它的入口点是Java.lang instrument包,它提供了允许Agent为JVM上运行的程序提供工具的服务。该包非常简单且自包含,因为它包含两个异常类、一个数据类、类定义和两个接口。在这两种方法中,如果我们想编写Java Agent,我们只需要实现classFileTransformer接口。

定义代理有两种方法。

第一个是静态代理,这意味着我们构建Agent代理,将其打包为jar文件,当我们启动Java应用程序时,我们传入一个名为java agent的特殊JVM参数。然后我们给它代理jar在磁盘上的位置,然后JVM发挥它的魔力。

$ java -javaagent: -jar 

我们需要添加一个特殊的清单条目,称为pre-main类,当然,这是一个完全限定的名称类定义。

Premain-Class : org.example.JavaAgent

这个类看起来像这样

public class JavaAgent {    /**     * As soon as the JVM initializes, This  method will be called.     *     * @param agentArgs       The list of agent arguments     * @param instrumentation The instrumentation object     * @throws InstantiationException     */    public static void premain(String agentArgs, Instrumentation instrumentation) throws InstantiationException {        InterceptingClassTransformer interceptingClassTransformer = new InterceptingClassTransformer();        interceptingClassTransformer.init();        instrumentation.addTransformer(interceptingClassTransformer);    }}

premain方法接受两个参数:

  • agentArgs-字符串参数,用户选择作为参数传递给Java Agent调用的任何参数。

  • instrumentation来自java.lang instrument包,我们可以添加一个新的ClassFileTransformer对象,它包含Agent的实际逻辑。

第二个选项称为动态代理。

我们可以做的不是检测启动应用程序的方式,而是编写一小段代码,接收并连接到现有JVM,并告诉它加载某个代理。

VirtualMachine vm = VirtualMachine.attach(vmPid);vm.load(agentFilePath);vm.detach();

此参数agentFilePath与静态代理方法中的参数完全相同。它必须是agent jar的文件名,因此没有输入流也没有字节。这种方法有两个警告。第一个是,这是生活在com.sun空间下的私有API,它通常适用于热点实现。第二个问题是,使用Java9进行排序时,您不能再使用此代码连接到它正在运行的JVM。

2.1 类转换

这是我们需要为agent实现的接口,以便转换类。

public interface ClassFileTransformer {    byte[] transform(ClassLoader loader,                      String className,                      Class classBeingRedefined,                     ProtectionDomain protectionDomain,                      byte[] classfileBuffer)             throws IllegalClassFormatException;}

这有点多,但我将解释方法签名中的必要参数。第一个重要的是类名。此参数的主要目的是帮助查找并区分要拦截的正确类和其他类。显然,您可能不想截取应用程序中的每个类,最简单的方法是使用条件语句进行检查。

然后是类加载器,它主要用于基本应用程序没有平坦类空间的环境中,您可能不需要查看它,但一旦遇到更复杂或模块化的平台,您就需要查看类加载器。classfileBuffer是检测前类的当前定义。要截取它,您需要使用库读取这个字节数组并截取代码,然后必须再次转换回字节码才能返回。

有几个字节码生成库。您需要进行研究,并根据它是高级API还是低级API、社区大小和许可证自行决定。我放在下面的演示是Javassist,因为我认为它在高级和低级API之间有一个很好的平衡,并且是一个三重许可证,所以几乎任何人都可以使用它。这

就是ClassFileTransformer实现的主体。

@Overridepublic byte[] transform(ClassLoader loader, ..)        throws .. {    byte[] byteCode = classfileBuffer;    // If you wanted to intercept all the classs then you can remove this conditional check.    if (className.equals("Example")) {        try {            ClassPool classPool = scopedClassPoolFactory.create(loader, rootPool,                    ScopedClassPoolRepositoryImpl.getInstance());            CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));            CtMethod[] methods = ctClass.getDeclaredMethods();            for (CtMethod method : methods) {                if (method.equals("main")) {                    method.insertAfter("System.out.println(\"Logging using Agent\");");                }            }byteCode = ctClass.toBytecode();            ctClass.detach();        } catch (Throwable ex) {            log.log(Level.SEVERE, "Error in transforming the class: " + className, ex);        }    }    return byteCode;}ctClass.detach();        } catch (Throwable ex) {            log.log(Level.SEVERE, "Error in transforming the class: " + className, ex);        }    }    return byteCode;}

在这里,从类池中,我们可以直接绕过classfileBuffer获取类,因为我想使用main方法。我们循环遍历类定义中的所有方法,得到我们想要的类。我们根本不需要使用字节码。我们可以简单地向它传递一些合法的Java代码,然后Javassist将编译它,生成新的字节码,并给出定义。

有三种方法可以向方法中插入一些Java代码。insertAfter(..)在正文末尾插入字节码。它在正文的末尾插入字节码。insertAt(..)在正文的指定行插入字节码,insertBefore(..)在正文的开头插入字节码。

2.2 使用Java代理进行实际操作

从指定的链接下载示例应用程序和Java Agent。

使用进入路径并执行命令mvn clean install来构建这两个repo

现在,您将获得目标中的jar文件。复制示例应用程序中.jar文件的路径,并复制Java Agent中-dependencies.jar文件的路径。

首先,使用命令$java-jar仅与示例应用程序一起运行应用程序,并观察输出。Hi I am main。将在控制台中打印。

然后,使用命令$ java -javaagent: -jar file you want to intercept>并观察输出。将在控制台中另外打印使用代理的日志记录。这确保java agent被拦截并添加到main方法的主体中。

总之,如果要实现Java Agent,请执行以下操作:

1. 您需要创建两个Java类。一个是premain方法(JavaAgent),另一个是扩展ClassFileTransformer的类(CustomTransformer)

2. 在premain方法的主体内,需要添加扩展ClassFileTransformer的类的对象

3. 然后,您需要在CustomTransformer中的重写方法transform中添加逻辑。

4. 在转换方法内转换字节码时,可能需要根据用途使用字节码生成库。

5. 您需要在清单中指定premain类并构建jar。

6. 使用javaagent标记将代理加载到要拦截的应用程序中。

感谢你能够认真阅读完这篇文章,希望小编分享的"Java中Agent如何动态修改字节码"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

字节 方法 程序 应用程序 应用 代理 参数 代码 两个 运行 文件 正文 示例 篇文章 动态 特殊 主体 命令 接口 路径 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 天津市摩尔互联网科技 互联网科技有限公司图片 互联网公司 科技公司 互联网科技金融公司 互联网科技创新企业排名 中国最先进互联网科技公司 须眉科技是互联网公司吗 创业天下互联网科技有限公司 乌苏新华互联网科技做什么的 科技类公司包括互联网吗 如何创新互联网科技 工业互联网等科技实现智能化赋能 无锡工业互联网科技有限公司 广州千川互联网科技有限公司 广州互联网高科技取餐柜 神州科技互联网有限公司 互联网科技平台产品运营 西安左小互联网科技有限公司 互联网科技创新创业项目 每日互联网科技新闻 互联网科技行业发展 互联网巨头反垄断金融科技 狮子座互联网科技 互联网科技创新企业排名 互联网科技公司招聘 中国互联网有什么科技含量 江阴互联网智能科技产品进口 烟台灵鸽互联网科技有限公司 工业互联网山东科技大学 深圳互联网科技的发展
0