0x01 Java Agent
JavaAgent有两种使用方式。
第一种是在 JVM启动的时候加载,通过 javaagent 启动参数,在程序main方法执行之前执行agent 中的premain 方法。
第二种方式是在 JVM启动后进行Attach,通过Attach API进行加载,在agent加载以后执行agentmain 方法。
0x02 javaagent
对于第一种方式,javaagent是java命令的其中一个参数,-javaagent用于指定一个 jar 包,可装载多个agent。
其与java.lang.instrument有关,java.lang.instrument在jre中lib里面的rt.jar包。
对于javaagent的jar包,其需满足:
MANIFEST.MF文件必须指定Premain-Class。
Premain-Class指定的类必须实现一个公共静态premain方法。
https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html
JVM初始化后,执行premain方法,再执行main方法。
该premain方法的定义为:
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
JVM初始化后会先加载定义了Instrumentation的第一种类型方法,加载成功则忽略第二种。
如果第一种类型没定义,则加载第二种方法。
在rt.jar包的java.lang.instrument中,Instrumentation接口定义了若干个方法:
https://www.cnblogs.com/rickiyang/p/11368932.html
public interface Instrumentation {
void addTransformer(ClassFileTransformer var1, boolean var2);
void addTransformer(ClassFileTransformer var1);
boolean removeTransformer(ClassFileTransformer var1);
boolean isRetransformClassesSupported();
void retransformClasses(Class<?>... var1) throws UnmodifiableClassException;
boolean isRedefineClassesSupported();
void redefineClasses(ClassDefinition... var1) throws ClassNotFoundException, UnmodifiableClassException;
boolean isModifiableClass(Class<?> var1);
Class[] getAllLoadedClasses();
Class[] getInitiatedClasses(ClassLoader var1);
long getObjectSize(Object var1);
void appendToBootstrapClassLoaderSearch(JarFile var1);
void appendToSystemClassLoaderSearch(JarFile var1);
boolean isNativeMethodPrefixSupported();
void setNativeMethodPrefix(ClassFileTransformer var1, String var2);
}
其中,常用的方法的作用如下:
- 增加Class文件的转换器,用于改变 Class 二进制流的数据,参数 canRetransform 设置是否允许重新转换。
void addTransformer(ClassFileTransformer var1, boolean var2);
- 增加Class文件的转换器,在类加载之前,重新定义 Class 文件,ClassDefinition 表示对一个类新的定义。
void addTransformer(ClassFileTransformer var1);
- 读取Class文件的转换器,读取重新定义 的Class 文件。
void redefineClasses(ClassDefinition... var1) throws ClassNotFoundException, UnmodifiableClassException;
- 重读取Class文件的转换器,如果在类加载之后,需要使用 retransformClasses 方法重新定义。对于已经加载过的类,可以使用retransformClasses来重新触发这个Transformer的拦截。
void retransformClasses(Class<?>... var1) throws UnmodifiableClassException;
- 删除Class文件的转换器,删除重新定义 的Class 文件。
boolean removeTransformer(ClassFileTransformer var1);
0x03 Before main
1、测试demo
2、测试agent
3、装载agent
可以看到premain方法先main方法执行。
0x04 Replace Return
1、测试demo
通过Javaagent,在main方法加载执行前,将num方法中的返回值修改为2。
2、测试agent
通过premain方法,调用增加Class文件的转换器addTransformer
来改变 Class 二进制流的数据。
接着继承和重写ClassFileTransformer
类中的transform
方法,
在定位修改的目标类和方法后,通过使用javassist来修改目标方法中的代码和返回值。
3、装载agent
最后成功修改目标方法的代码和返回值。