长亭百川云 - 文章详情

Java Agent 核心技术JVM之类的热替换原理解读

RASP安全技术

102

2024-07-13

先讲讲怎么用吧

    一上来就说原理还是不怎么合适的,先给大家讲下这个技术怎么用吧。但是这篇文章重点不是讲怎么用,所以我只讲个大概流程。

第一步:写个Agent类,获取Instrumentation对象

`public class MyAgent {`  `private static Instrumentation mInstrumentation;``   `  `public static void agentmain(String agentArgs, Instrumentation inst) {`    `mInstrumentation = inst;`  `}``   `  `// 拿到Instrumentation对象后就可以利用ClassModifierTransformer来进行类的热替换了`  `public static void modifyClass(Class clazz){`    `ClassFileTransformer transformer = new ClassModifierTransformer();`    `mInstrumentation.addTransformer(transformer, true);`    `mInstrumentation.retransformClasses(new Class[]{clazz});`    `mInstrumentation.removeTransformer(transformer);`  `}``}`

第二步:写个ClassFileTransformer,利用ASM/Javassist等工具进行字节码修改

`public class ClassModifierTransformer implements ClassFileTransformer {``   `  `@Override`  `public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {`    `// 在这里利用Javassist等工具修改类的字节码,返回修改后类的字节数组`    `return null;`  `}``}`

    目前已经有很多文章讲具体使用方法了,大家可以搜索下,我这里先介绍两篇:

  • 基于Java Instrument的Agent实现

  • 谈谈Java Intrumentation和相关应用

原理探究

热替换的核心就在于Instrumentation的两个方法:

 `void addTransformer(ClassFileTransformer transformer, boolean canRetransform);` `void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;`

    addTransformer 用来注册类的修改器;retransformClasses 会让类重新加载,从而使得注册的类修改器能够重新修改类的字节码。

下面让我们重点讲讲这两个方法的实现:

1. addTransformer

      可见我们自己的实现的 ClassFileTransformer 被添加到了 TransformerManager中,让我们跟进去看看:

     ClassFileTransformer对象这次被放入了TransformerManager的一个数组中。    OK,注册完毕,很简单对不对?下面我们再来看下稍微复杂点的retransformClasses 吧。

2.retransformClasses

    这个方法的实现最终调用的是个Native方法。很多同学看到Native方法就头疼,不要急,Native方法也是人写的,不过是一段文本而已。我们来看下具体实现吧:

继续跟进 -->

retransformClasses 最后会调用到 jvmtiEnv.cpp中的 RetransformClasses

补充:Klass是一个抽象基类,它定义了一些接口(纯虚函数),由 InstanceKlass 继承并实现,两者结合可以描述一个java类的方法、字段、父类等信息。InstanceKlass 在jvm层面可以描述绝大部分java类。

上面这段代码主要干了两件事:

(1) 根据 java 层的Class对象,找到JVM层的类实例 InstanceKlass,并获取类的字节码,存放在class_definitions数组中。因为可以一次替换多个类,所以这里加了一个循环体,遍历每个要修改的类。

(2) 调用VMThread::execute(&op)

    在获取了类的字节码之后,创建了一个 VM_RedefineClasses 的 vmop,然后通知VMThread进行处理。

    在分析代码之前,先来看下比较重要的 VM_Operation。VM_Operation 是虚拟机级别的操作,这些操作包含了所有JVM的内置操作,例如GC、获取线程栈等等。这个类是所有这些操作的基类,该类定义在hotspot/src/share/vm/runtime/vmOperations.hpp 。

    首先,该类定义了 Mode 和 VMOp_Type 两个枚举。第一个表示该操作的模式,第二个表示该操作的类型。事实上所有的类型都在文件开头的宏定义中写明了,这里我们只关心 RedefineClasses 这个类型。Mode包括这四种:

 再来看下 VM_RedefineClasses

    VM_RedefineClasses是VM_Operation的子类,实现了类转换的所有逻辑。该类定义和实现分别在hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp和hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp。

    对于一个VM_Operation的子类,首先需要关心 evaluation_mode 函数。VM_RedefineClasses 类中找不到该函数,因此它是一个需要在 safepoint 阻塞的操作。

    然后就是核心操作,即 doit_prologue、doit、doit_epilogue。代码比较复杂,本节先介绍doit_prologue的实现。我们先从注释上了解每个步骤做了什么

1.doit_prologue

    在 doit_prologue 阶段,整个操作都是在Java线程中进行的,因此不会阻塞VMThread,也不会被计入safepoint的耗时。注意整个源码中 the_class 表示待替换的类,scratch_class表示新的类。

    该阶段主要做的就是准备需要的字节码,包括解析字节码、类的链接、常量池合并、字节码校验等步骤。需要说明的是,如果业务代码中准备新的字节码时间比较长(前面提到的获取新字节码的回调也是在这里发生),这个阶段时间就会变长,但是不会阻塞JVM的核心线程。

    然后,我们看下这部分是如何实现的

   VMThread::execute(&op) 中会调用到 VM_RedefineClasses::doit_prologue,核心逻辑在 VM_RedefineClasses::load_new_class_versions()

由于代码较长,分为多个部分,第一部分如下

parse_stream() 这里又调用了KlassFactory::check_class_file_load_hook

    看名字就知道是个hook方法,它会调用post_class_file_load_hook。

    利用JvmtiClassFileLoadHookPoster来通知类修改器进行类的修改。进入 poster.post() 里面

消息发给所有的 jvmtienv , 最终的调用如下:

实际的消息处理者:

  eventHandlerClassFileLoadHook在收到消息后,会调用transformClassFile 

,继续跟进--->

    这里会利用JNI调用 java 层InstrumentationImpl的transform,你看,我们又绕到Java层了。

    transform 方法的调用如下:

    看到这儿,大家还记得我们开始的时候,会将我们自定义的ClassFileTransformer对象注册到TransformerManager中吗?这里终于派上用场了,TransformerManager的transform()方法会遍历它的注册数组,调用每个ClassFileTransformer对象的transform()方法,并将我们修改后的类字节码返回,返回后的字节码最终又回到了上面JVM层的transformClassFile()中,并最终交还给给class_file_load_hook 消息的发送方。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2