0%

javaagent

前言

这篇文章主要是在学习java内存马的过程中,理解agent类型内存马时出现了一些问题,于是就来学了一下agent的一些api。主要想搞清楚retransformClasses和redefineClasses之间有什么区别。

Instrumentation::retransformClasses

根据函数名,可以大概知道这个函数的作用:重新转换类。接着我们看官方说明

This function facilitates the instrumentation of already loaded classes.

When classes are initially loaded or when they are redefined, the initial class file bytes can be transformed with the ClassFileTransformer. This function reruns the transformation process (whether or not a transformation has previously occurred).

从官方说明可以得知,当一个类最开始被加载或者是重定义的时候,类的字节码会被ClassFileTransformer进行转换。所谓转换,就是修改类的字节码。而retransformClasses函数的作用,就是重新执行一遍转换的过程。

重新转换的过程如下

  1. 从初始的字节码开始,遍历每个transformer
  2. 如果transformer是RetransfomableTransformer,则执行它的transform方法

Instrumentation::redefineClasses

根据函数名,可以大概知道这个函数的作用:重新定义类。与retransformClasses的区别在于,retransformClasses是拿到原来的字节码,然后可以对其进行修改,而redefineClasses是直接给出自定义的字节码。用一个不太恰当的比喻,retransformClasses可以看作是动态代理实现类增强,而redefineClasses则是重新写一个类。

相同点是,无论使用哪个函数,都不能对类的字段、方法签名做修改,更不能添加或删除字段或方法。且无论是redefineClasses还是retransformClasses都不会触发类的初始化操作

ClassFileTransformer

前面在讲Instrumentation::retransformClasses方法的时候,就提到了ClassFileTransformer,即class文件转换器。retransformClasses方法功能的实现也是依赖于这个class文件转换器。下面就来看看官方对这个接口的说明

这个接口就一个方法transform

The implementation of this method may transform the supplied class file and return a new replacement class file.

官方说了,实现这个方法可以转换类文件,返回一个新的类文件。

继续看一下具体细节,我们可以通过Instrumentation::addTransformer方法来添加我们自定义的class文件转换器。这个方法的第二个参数用来指明这个转换器是否是Retransformation capable的。

the transformer will be called for every new class definition and every class redefinition. Retransformation capable transformers will also be called on every class retransformation.

转换器在每个新类定义或者是重定义时都会被调用。如果转换器被设置成Retransformation capable,那么在重转换,也就是Instrumentation::retransformClasses方法调用时,转换器也会被调用。

当我们多次调用Instrumentation::addTransformer,添加多个ClassFileTransformer时,就构成了一条转换器链。

对于转换器链上第一个转换器的transform方法的传入参数classfileBuffer,官方做出了解释

  • 当新类定义时,classfileBuffer就等价于传递给defineClass的字节数组
  • 当类重定义时,classfileBuffer就是definitions.getDefinitionClassFile()的返回值
  • 当类重转换时,如果这个类被重定义过,那么classfileBuffer就是最后一次重定义的结果;如果没有重定义过,那么classfileBuffer就是第一次加载时传递给defineClass的字节数组。