插桩java_字节码插桩技术

时间:2024-10-01 07:29:23

字节码插桩

我们知道JVM是不能直接执行.java 代码,也不能直接执行.class文件,它只能执行.class 文件中存储的指令码。这就是为什么class需要通过classLoader 装载以后才能运行。基于此机制可否在ClassLoader装载之前拦截修改class当中的内容(jvm 指令码)从而让程序中包含我们的埋点逻辑呢?答案是肯定的,但需要用到两个技术 javaagent与javassist 。前者用于拦截ClassLoad装载,后者用于操作修改class文件。

javaagent

javaagent介绍

javaagent 是java1.5之后引入的特性,其主要作用是在class 被加载之前对其拦截,以插入我们的监听字节码

javaagent jar包

javaagent 最后展现形式是一个Jar包,有以下特性:

1.必须 META-INF/中指定Premain-Class 设定启agent启动类。

2.在启类需写明启动方法 public static void main(String arg,)

3.不可直接运行,只能通过 jvm 参数-javaagent: 附着于其它jvm 进程运行。

javaagent使用

1、编写agent方法

public classMyAgent {public static void premain(String args, Instrumentation instrumentation) throwsException {

("Hello javaagent permain:"+args);

}

}

2、添加premain-class参数

org.apache.

maven-jar-plugin

2.2

${}

${}

true

true

true

3、构建打包

4、在任一JAVA应用中 添加jvm 参数并启动 -javaagent: META-INF/

参数说明:

Premain-Class:必填,agent启动

classCan-Redefine-Classes:默认为false ,是否允许重新定义

classCan-Retransform-Classes:默认为false,是否允许重置Class,重置后相当于class 从classLoade中清除,下次有需要的时候会重新装载,也会重新走Transformer 流程。

Boot-Class-Path:agent 所依赖的jar 路径,多个用空格分割

创建一个测试类MyAgentTest并运行查看结果

public classMyAgentTest {public static voidmain(String[] args) {

("main");

}

}//运行结果:main

添加jvm参数

参数内容:-javaagent:/Users/jinyunlong/IdeaProjects/test-agent/target/test-agent-1.=123

再次运行测试类MyAgentTest并查看结果

javassist

javassist介绍

javassist是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成(注:也可以使用ASM实现,但需要会操作字节码指令,学习使用成本高)

javassist使用

使用javassist需要引入javasssist的jar包,添加内容如下:

javassist

3.18.1-GA

.plugins

maven-jar-plugin

2.2

${}

${}

true

true

javassist-3.18.

true

演示插入打印当前时间

创建类MyServer

public classMyServer {publicInteger sayHello(String name,String message){

("hello");return 0;

}

}

myAgent类

创建测试类并调用MyServer中的sayHello方法

演示计算方法调用时间

类MyAgent

public classMyAgent {public static void premain(String args, Instrumentation instrumentation) throwsException {

(newClassFileTransformer() {public byte[] transform(ClassLoader loader, String className, Class>classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throwsIllegalClassFormatException {if(!"com/javaagent/MyServer".equals(className)){return null;

}try{returnbuildMonitorClass();

}catch(Exception e) {

();

}return null;

}

},true);

}private static byte[] buildMonitorClass() throwsException{/*** 1、拷贝一个新的方法

* 2、修改原方法名

* 3、加入监听代码*/ClassPool pool= newClassPool();

();

CtClass ctClass= ("");

CtMethod ctMethod= ("sayHello");

CtMethod copyMethod= (ctMethod,ctClass,newClassMap());

("sayHello$agent");

("{\n" +

" long begin = ();\n" +

" try {\n" +

" return sayHello$agent($1,$2);\n" +

" } finally {\n" +

" (() - begin);}\n" +

" }");

(copyMethod);();

}

}

修改类MyServer

public classMyServer {publicInteger sayHello(String name,String message){

("hello name:"+name+",message:"+message);return 0;

}

}

修改测试类并运行

public classMyAgentTest {public static voidmain(String[] args) {

MyServer myServer= newMyServer();

("paul","1234");

}

}//运行结果:

hello name:paul,message:1234

186537

javassist特殊语法