Java探针-javaagent由浅入深(一)

前言(可忽略):近来因为公司要对我所负责的系统进行安全性的检测,项目需要引入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,这样更有助于对其原理的理解

  1. 创建一个普通的带有Main方法的Java文件:
     
    public class Hello{
        public static void main(String[] args){
            System.out.println("Main Method:Hello Agent");
        }
    }
    

     

  2. 创建一个带有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方法不是单纯的方法名一样,参数列表也需与上面的代码保持一致,可以理解为一种隐含的实现(因为实际类并没有实现任何接口)

  3. 编译Hello.java、MyAgent.java两个文件:
    javac Hello.java MyAgent.java

     

  4. 创建符合javaagent要求的Manifest文件:
    Manifest-Version: 1.0
    Created-By: 1.8.0_202 (Oracle Corporation)
    Premain-Class: MyAgent
    
    

    这里的Permain-Class的值要指向MyAgent类的全路径路名,因为我没有创建包,所以仅写了类名。末尾的空行不可省略!

  5. 使用我们自己编写的Manifest文件将MyAgent.class文件打成jar包:
    jar cvfm java-agent-demo.jar mymanifest MyAgent.class 

    打包时使用参数制定我们自己编写的Manifest文件(我的文件名叫mymanifest)

  6. 用带有-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和构建工具进行更加深入的讲解(学习),文中若有不当之处还请不吝赐教。

你可能感兴趣的:(Java,javaagent)