千家信息网

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

发表于:2025-01-23 作者:千家信息网编辑
千家信息网最后更新 2025年01月23日,这篇文章主要介绍了Java中Agent如何动态修改字节码,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1、什么是Java AgentJ
千家信息网最后更新 2025年01月23日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安全错误 数据库的锁怎样保障安全 成都市公安局大运会网络安全 提示网络安全客户端 html5本地数据库使用 Gp数据库怎么数求月底 最流行的软件开发是什么 服务器被当肉鸡 吉林省网络安全审查委员会 提交数据库技术职业岗位的 做到高级软件开发后能做什么 上网服务器是什么意思 根服务器管理权 信息网络安全英语作文 优客乐乐网络技术 数据库文件不是bak如何还原 昌平区有口碑的软件开发不二之选 中国移动通信集团软件开发待遇 数据库和系统如何连接起来的 法规库包含哪些子数据库 数据库中数据20的数据类型是 浙江企业软件开发咨询热线 服务器 域管理 北京仙浴湾互联网科技有限公司 广东软件开发者网站 2003终端服务器组件 数据库bak文件还原 吴江区数据网络技术收费 通力电梯软件开发怎么样 小学网络安全宣传活动总结 在数据库表中班级怎么表示 redis 数据库延迟任务
0