Java安全(十) JavaAgent

Java Agent机制

在JDK1.5版本开始,Java增加了Instrumentation(Java Agent API)JVMTI(JVM Tool Interface)功能,该功能可以实现JVM再加载某个class文件对其字节码进行修改,也可以对已经加载的字节码进行一个重新的加载。

利用该机制能够实现许多技术,如RASP、内存马、IDEA破解。

Java Agent有两种运行模式:

  1. 启动Java程序时添加-javaagent(Instrumentation API实现方式)-agentpath/-agentlib(JVMTI的实现方式)参数,如java -javaagent:/data/XXX.jar Test
  2. JDK1.6新增了attach(附加方式)方式,可以对运行中的Java进程附加Agent

第一种方式只能在程序启动时指定Agent文件,而attach方式可以在Java程序运行后根据进程ID动态注入AgentJVM

前置知识

使用Java Agent会加载一个jar包(我们的程序也必须打包成一个jar包)。如下有一些规范:

  • jar文件中必须包含/META-INF/MANIFEST.MF文件
  • MANIFEST.MF文件中必须定义好Premain-Class(Agent模式)或Agent-Class:(Attach模式)
  • 如果我们需要修改已经被JVM加载过的类的字节码,那么还需要设置在MANIFEST.MF中添加Can-Retransform-Classes: trueCan-Redefine-Classes: true

Java Agent和普通的Java类并没有任何区别,普通的Java程序中规定了main方法为程序入口,而Java Agent则将premain(Agent模式)和agentmain(Attach模式)作为了Agent程序的入口,如下:

public static void premain(String args, Instrumentation inst) {
   }
public static void agentmain(String args, Instrumentation inst) {
   }

简单来说就是在运行main方法前会去加载-javaagent指定的jar包里面的Premain-Class类中的premain方法。

premain方法其实还可以简写成:(agentmain同样)

public static void premain(String agentArgs)

JVM会去优先加载带 Instrumentation 签名的premain方法,加载成功忽略无签名的,如果第一种没有,则加载第二种方法。

在这里插入图片描述

java.lang.instrument这个包提供了Java运行时,动态修改系统中的Class类型的功能。

这里面有2个重要的接口InstrumentationClassFileTransformer

在这里插入图片描述

Instrumentation

java.lang.instrument.Instrumentation是监测运行在JVM程序的Java API,用一下javasec里面的一张图

在这里插入图片描述

利用该类可以实现如下功能:

  1. 动态添加或移除自定义的ClassFileTransformeraddTransformer/removeTransformer),JVM会在类加载时调用Agent中注册的ClassFileTransformer
  2. 动态修改classpathappendToBootstrapClassLoaderSearchappendToSystemClassLoaderSearch),将Agent程序添加到BootstrapClassLoaderSystemClassLoaderSearch(对应的是ClassLoader类的getSystemClassLoader方法,默认是sun.misc.Launcher$AppClassLoader)中搜索;
  3. 动态获取所有JVM已加载的类(getAllLoadedClasses);
  4. 动态获取某个类加载器已实例化的所有类(getInitiatedClasses)。
  5. 重定义某个已加载的类的字节码(redefineClasses)。
  6. 动态设置JNI前缀(setNativeMethodPrefix),可以实现Hook native方法。
  7. 重新加载某个已经被JVM加载过的类字节码retransformClasses)。

ClassFileTransformer

java.lang.instrument.ClassFileTransformer是一个转换类文件的代理接口,我们可以在获取到Instrumentation对象后通过addTransformer方法添加自定义类文件转换器。

我们可以使用addTransformer注册一个我们自定义的TransformerJava Agent,当有新的类被JVM加载时JVM会自动回调用我们自定义的Transformer类的transform方法,传入该类的transform信息(类名、类加载器、类字节码等),我们可以根据传入的类信息决定是否需要修改类字节码。修改完字节码后我们将新的类字节码返回给JVMJVM会验证类和相应的修改是否合法,如果符合类加载要求JVM会加载我们修改后的类字节码。

在这里插入图片描述

该接口中有只有一个transform方法,里面的参数内容对应的信息分别是:

ClassLoader loader              	定义要转换的类加载器;如果是引导加载器,则为 null
String   className           		加载的类名,:java/lang/Runtime
Class<?> classBeingRedefined 		如果是被重定义或重转换触发,则为重定义或重转换的类;如果是类加载,则为 null
ProtectionDomain protectionDomain   要定义或重定义的类的保护域
byte[]  classfileBuffer     		类文件格式的输入字节缓冲区(不得修改)

重写transform方法注意事项:

  1. ClassLoader如果是被Bootstrap ClassLoader(引导类加载器)所加载那么loader参数的值是空。
  2. 修改类字节码时需要特别注意插入的代码在对应的ClassLoader中可以正确的获取到,否则会报ClassNotFoundException,比如修改java.io.FileInputStream(该类由Bootstrap ClassLoader加载)时插入了我们检测代码,那么我们将必须保证FileInputStream能够获取到我们的检测代码类。
  3. JVM类名的书写方式路径方式:java/lang/String而不是我们常用的类名方式:java.lang.String
  4. 类字节必须符合JVM校验要求,如果无法验证类字节码会导致JVM崩溃或者VerifyError(类验证错误)
  5. 如果修改的是retransform类(修改已被JVM加载的类),修改后的类字节码不得新增方法修改方法参数类成员变量
  6. addTransformer时如果没有传入retransform参数(默认是false)就算MANIFEST.MF中配置了Can-Redefine-Classes: true而且手动调用了retransformClasses方法也一样无法retransform
  7. 卸载transform时需要使用创建时的Instrumentation实例。

技术实现

看了一堆概念不如代码案例来得实在

JVM运行前(Agent模式)

创建我们的agent类

package com.study.agent;

import java.lang.instrument.Instrumentation;

public class Agent {
   
    public static void premain(String agentArgs, Instrumentation inst) {
   
        System.out.println("args: "+agentArgs);
        inst.addTransformer(new OurTransformer(),true);//add a Transformer
    }
}

OurTransformer类

package com.study.agent;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class OurTransformer implements ClassFileTransformer {
   
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
   
        System.out
Java Agent是Arthas使用的技术,是Skywalking使用的技术,是一份分重要的技术。 课程的稀缺性在此之前,市面上并没有针对Java Agent进行系统介绍的课程。 通过搜索引擎查找,会发现与Java Agent相关的内容大多是个人知识总结分享的内容。这些内容有如下特点:内容质量不一详略程度不一学习难度千差万别总体上来说,学习者很难有一个整体认知、系统学习的过程。 课程的设计目标 在构思课程内容时,本课程带有以下目标:课程学习梯度:从简单到复杂,让学习者有一个循序渐进的理解过程。构造完整、统一的知识体系:不是零散的知识点堆砌,而是有一个统一的贯穿始终的知识框架。具有可操作性的代码示例,不只是讲概念,更注意于实践。课程内容安排 本课程通过四章内容对Java Agent相关知识进行讲解:第一章,介绍Agent Jar的三个组成部分:Manifest、Agent Class和ClassFileTransformer。第二章,介绍Agent Jar的两种启动方式:从命令行启动和使用Attach机制启动。第三章,介绍如何利用Instrumentation API来实现Agent Jar的功能。第四章,Java Agent的应用与技巧。 通过本课程的学习,让同学们更好地建立起一个完整的知识体系:  讲师介绍我叫刘森,南京师范大学研究生毕业,2015年获得信息系统项目管理师(高级),2014年获得系统集成项目管理工程师(中级)。 目前,我的课程都是围绕着“Java字节码”技术展开: 《Java Agent基础篇》是在一个运行JVM当中提供修改字节码的机会《Java ASM系列》(免费课程)是一个操作字节码的类库《Java 8 ClassFile》专注于字节码的理论知识,入选为“51CTO数字化人才证书项目认证课程” 因此,我对字节码技术有较为深入的研究和理解,大家想学习字节码的技术可以找我:字节码技术找刘森,轻松学习又省心~~~ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值