SandBox中的JavaAgent技术

2024-03-03 11:52
文章标签 java 技术 agent sandbox

本文主要是介绍SandBox中的JavaAgent技术,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

8.1 JavaAgent

Java Agent 是一种强大的技术,在运行时动态修改已加载类的字节码,为应用程序注入额外的功能和行为。

JDK 1.5 支持静态 Instrumentation,基本的思路是在 JVM 启动的时候添加一个代理(javaagent),每个代理是一个 jar 包,其 MANIFEST.MF 文件里指定了代理类,这个代理类包含一个 premain 方法。JVM 在类加载时候会先执行代理类的 premain 方法,再执行 Java 程序本身的 main 方法,这就是 premain 名字的来源。在 premain 方法中可以对加载前的 class 文件进行修改。

8.1.1 应用场景:

代码注入:通过 Java Agent 技术,我们可以在运行时将自定义的字节码注入到已加载的类中。这样可以实现 AOP 编程思想,将通用的逻辑代码动态地注入到目标类中,简化代码的编写和维护。

性能监控:Java Agent 可以用于实时监控应用程序的性能指标,如方法的执行时间、方法的调用次数等。通过在方法的前后插入字节码,我们可以收集这些信息,并进行实时监控、统计和分析,帮助优化和调优应用程序的性能。

调试工具:Java Agent 可以用作调试工具的基础。我们可以通过动态修改字节码,将额外的调试信息插入到目标类中,例如打印方法的参数、返回值等。这样,在调试过程中可以更加方便地获取和分析这些信息,帮助快速定位问题。

安全检查:Java Agent 还可以用于实现安全检查机制。通过对正在加载的类进行拦截和检查,我们可以在运行时进行代码审计、防止恶意代码注入等,提高应用程序的安全性。

8.1.2 启动方式

启动挂载(Agent):

所有的类在加载时都会执行transform,我们在此方法中通过 Instrumentation 增强目标类。

随着JVM启动一起启动(java -javaagent:(jar包名) (方法类名))

premain(): 实现premain方法,并且添加一个自定义transformer,把操作放在自定义的transformer内,该transformer要实现ClassFileTransformer.transform() 主程序运行时:加载每个类前都会进入transform(),可以获取到它的加载器、类名、字节码buffer等

动态挂载(Attach):

动态挂载之后,类加载时都会执行transform,我们在此方法中通过 Instrumentation 增强目标类。

动态挂载前 已经加载的类,都未经历增强的处理。 可通过调用retransformClasses方法,让已加载的类重新加载, 重新加载时也会执行transform,我们在此方法中增强目标类。

在已经运行的JVM进程中,动态的插入。通过attach API调用,在sandbox中执行./sandbox -p pid(目标 jvm 进程 id)是使用的 attach api 方式进行 agent 的挂载

agentmain():对于agentmain也是一样的流程,但多一步inst.retransformClasses()的操作让JVM重新加载修改过的类的字节码,否则修改不会生效 使用com.sun.tools.attach.VirtualMachine进行动态挂载Agent:

 
private void attachAgentToTargetJVM() throws Exception {List<VirtualMachineDescriptor> virtualMachineDescriptors = VirtualMachine.list();VirtualMachineDescriptor targetVM = null;for (VirtualMachineDescriptor descriptor : virtualMachineDescriptors) {if (descriptor.id().equals(configure.getPid())) {targetVM = descriptor;break;}}if (targetVM == null) {throw new IllegalArgumentException("could not find the target jvm by process id:" + configure.getPid());}VirtualMachine virtualMachine = null;try {virtualMachine = VirtualMachine.attach(targetVM);virtualMachine.loadAgent("{agent}", "{params}");} catch (Exception e) {if (virtualMachine != null) {virtualMachine.detach();}}}

它的 addTransformer 给 Instrumentation 注册一个 transformer,transformer 是 ClassFileTransformer 接口的实例,这个接口就只有一个 transform 方法,调用 addTransformer 设置 transformer 以后,后续JVM 加载所有类之前都会被这个 transform 方法拦截,这个方法接收原类文件的字节数组,返回转换过的字节数组,在这个方法中可以做任意的类文件改写。

8.1.3 JavaAgent使用demo

使用premain的方式进行javaAgent的启动(不详细展示对java类的增强demo)

首先在一个maven项目中创建我们的核心demo-AgentDemo

 
package com.xhz.demo;import java.lang.instrument.Instrumentation;/*** @version V1.0* @Author : xiehuizhi* @create 2023/12/20 14:33*/public class AgentDemo {public static void premain(String agentArgs, Instrumentation instrumentation){System.out.println(agentArgs+"============尊贵的xxxx===============");}public static void premain(String agentArgs){System.out.println(agentArgs+"============尊贵的xxxx================");}}

创建完成之后可以手动在resources中创建MANIFEST.MF文件(也可以在pom中配置)

 

Manifest-Version: 1.0

Premain-Class: com.xhz.demo.AgentDemo

Can-Redefine-Classes: true

Can-Retransform-Classes: true

 

pom配置:

 
<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.1.2</version><configuration><archive><manifestEntries><Premain-Class>club.throwable.permain.PermainAgent</Premain-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration></plugin></plugins>

然后要实现agent的功能,需要将你的项目打成jar包

打包命令:mvn clean package

然后新建一个可以启动的Main类

 
package Utils;/*** @version V1.0* @Author : xiehuizhi* @create 2023/12/20 14:29*/public class Main {public static void main(String[] args) {System.out.println("流量回放");}}

 

在springboot启动项增加启动参数

8.2 Sandbox启动与Agent挂载的流程

sandbox-spy:这个包里的代码是一系列的模板代码,后期Sandbox会通过ASM框架将这些代码转化成字节码增加到业务代码的字节码当中,实现对业务代码的增强功能

但spy类为什么要被BootstrapClassLoader加载呢?我们不仅要增强业务代码,也要增强JDK里面的代码,将spy添加到BootstrapClassLoader的类路径,只要不破坏双亲委托机制,都能够被spy类增强,这是一种比较保险的做法。

8.3 JVM-SandBox实现字节码增强

一般的Spring AOP:实现业务切面,针对于JAVA后端应用也需要AOP

JVM-Sandbox的设计目的是实现一种在不重启、不侵入目标JVM应用情况下的AOP解决方案。

Jvm-SandBox字节码增强流程梳理:

8.3.1 增强过程

8.3.2 实现字节码增强逻辑

通过AsmMethods接口获取Sandbox自定义Spy类完成字节码增强

spy增强的静态方法

JavaAgent的本质就是字节码增强,这章主要介绍了Sandbox字节码增强功能的核心逻辑,包括增强逻辑、SandboxClassFileTransformer类形变器、Spy增强的静态方法。

这篇关于SandBox中的JavaAgent技术的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/769532

相关文章

Java中Map的五种遍历方式实现与对比

《Java中Map的五种遍历方式实现与对比》其实Map遍历藏着多种玩法,有的优雅简洁,有的性能拉满,今天咱们盘一盘这些进阶偏基础的遍历方式,告别重复又臃肿的代码,感兴趣的小伙伴可以了解下... 目录一、先搞懂:Map遍历的核心目标二、几种遍历方式的对比1. 传统EntrySet遍历(最通用)2. Lambd

Spring Boot 中 RestTemplate 的核心用法指南

《SpringBoot中RestTemplate的核心用法指南》本文详细介绍了RestTemplate的使用,包括基础用法、进阶配置技巧、实战案例以及最佳实践建议,通过一个腾讯地图路线规划的案... 目录一、环境准备二、基础用法全解析1. GET 请求的三种姿势2. POST 请求深度实践三、进阶配置技巧1

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

Spring Boot 处理带文件表单的方式汇总

《SpringBoot处理带文件表单的方式汇总》本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttr... 目录方式 1:@RequestParam接收文件后端代码前端代码特点方式 2:@RequestPart接

SpringBoot整合Zuul全过程

《SpringBoot整合Zuul全过程》Zuul网关是微服务架构中的重要组件,具备统一入口、鉴权校验、动态路由等功能,它通过配置文件进行灵活的路由和过滤器设置,支持Hystrix进行容错处理,还提供... 目录Zuul网关的作用Zuul网关的应用1、网关访问方式2、网关依赖注入3、网关启动器4、网关全局变

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoot的全局异常拦截实践过程

《SpringBoot的全局异常拦截实践过程》SpringBoot中使用@ControllerAdvice和@ExceptionHandler实现全局异常拦截,@RestControllerAdvic... 目录@RestControllerAdvice@ResponseStatus(...)@Except

Springboot配置文件相关语法及读取方式详解

《Springboot配置文件相关语法及读取方式详解》本文主要介绍了SpringBoot中的两种配置文件形式,即.properties文件和.yml/.yaml文件,详细讲解了这两种文件的语法和读取方... 目录配置文件的形式语法1、key-value形式2、数组形式读取方式1、通过@value注解2、通过

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例