前言(可忽略):近来因为公司要对我所负责的系统进行安全性的检测,项目需要引入VulHunter这个工具,引入的方式正是在Web容器上添加javaagent作为JVM参数,如:-javaagent:/home/haye/vulhunter.jar。这种引入对我的项目代码本身可以说是零侵入,但却实实在在的扫描到了项目存在的漏洞,对此甚为好奇,遂决定研究下javaagent的用法。
javaagent,既Java探针可以将其理解为一种类加载前的拦截器,其具有以下几点特性:
-
agent的代码与你的main方法在同一个JVM中运行;
-
被同一个system classloader装载;
-
被同一的安全策略 (security policy) 和上下文 (context) 所管理。
*以上特性搜集于网络,先放在这里,如果后面有所涉及再进行探讨。
一、下面我将从最简单的示例逐步讲解(学习)
最简单的示例不会使用任何的IDE及构建工具,仅使用原始的javac、java、jar命令实现一个基本的Demo,这样更有助于对其原理的理解
- 创建一个普通的带有Main方法的Java文件:
public class Hello{ public static void main(String[] args){ System.out.println("Main Method:Hello Agent"); } }
- 创建一个带有permain方法的java文件:
import java.lang.instrument.Instrumentation; public class MyAgent{ public static void premain(String agentOps,Instrumentation inst){ System.out.println("MyAgent:Hello Main Method"); } }
这里所说的带有permain方法不是单纯的方法名一样,参数列表也需与上面的代码保持一致,可以理解为一种隐含的实现(因为实际类并没有实现任何接口)
- 编译Hello.java、MyAgent.java两个文件:
javac Hello.java MyAgent.java
- 创建符合javaagent要求的Manifest文件:
Manifest-Version: 1.0 Created-By: 1.8.0_202 (Oracle Corporation) Premain-Class: MyAgent
这里的Permain-Class的值要指向MyAgent类的全路径路名,因为我没有创建包,所以仅写了类名。末尾的空行不可省略!
- 使用我们自己编写的Manifest文件将MyAgent.class文件打成jar包:
jar cvfm java-agent-demo.jar mymanifest MyAgent.class
打包时使用<m>参数制定我们自己编写的Manifest文件(我的文件名叫mymanifest)
- 用带有-javaagent参数的java命令运行Hello.class文件:
java -javaagent:./java-agent-demo.jar Hello
-javaagent后面的参数是我们生成的jar包。
上面的代码运行后将得到下面的结果:
MyAgent:Hello Main Method
Main Method:Hello Agent
MyAgent的方法在Main方法之前调用了,准确的说是在Hello.class类加载之前,下面我们简单修改Hello.java代码对其进行验证:
public class Hello{
static{
System.out.println("Static Block:Hello ClassLoader");//引入静态块
}
public static void main(String[] args){
System.out.println("Main Method:Hello Agent");
}
}
运行结果如下:
MyAgent:Hello Main Method
Static Block:Hello ClassLoader
Main Method:Hello Agent
我们知道静态快的调用是在类第一次加载时,从运行结果中我们可以看到,静态块调用在MyAgent的permain方法之后。
二、带有参数的javaagent
没一个javaagent可以接收一个字符串参数,这个参数就是前面permain参数列表中的agentOps,我们简单修改前面的代码:
import java.lang.instrument.Instrumentation;
public class MyAgent{
public static void premain(String agentOps,Instrumentation inst){
System.out.println("MyAgent"+agentOps+":Hello Main Method");//使用agentOps参数
}
}
重新编译MyAgent.java文件并打包后使用下面的命令运行Hello:
java -javaagent:java-agent-demo.jar=参数 Hello
可得到如下结果:
MyAgent参数:Hello Main Method
Static Block:Hello ClassLoader
Main Method:Hello Agent
从结果中我们可以看到我们传递的参数出现在了执行结果中。
三、多个javaagent的叠加
javaagent参数可以重复使用,调用多个javaagent,这里我们使用同一个带有不同参数的javaagent进行演示,使用下面的命令运行Hello:
java -javaagent:java-agent-demo.jar=1 -javaagent:java-agent-demo.jar=2 -javaagent:java-agent-demo.jar=3 Hello
可以得到如下的运行结果:
MyAgent1:Hello Main Method
MyAgent2:Hello Main Method
MyAgent3:Hello Main Method
Static Block:Hello ClassLoader
Main Method:Hello Agent
总结:至此我们已经完成了基本的javaagent实现,后续将借助IDE和构建工具进行更加深入的讲解(学习),文中若有不当之处还请不吝赐教。