从零开始的Java RASP实现(一)

2023-10-08 03:10
文章标签 java 实现 从零开始 rasp

本文主要是介绍从零开始的Java RASP实现(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录
  • 0 从零开始的Java RASP实现(一)
  • 1 javaagent
    • 1.1 Main方法启动前
      • 概念介绍:
      • 如何使用
      • 创建agent
      • 创建main
    • 1.2 JVM启动后
      • attach机制
      • 启动一个长时间运行的jvm
      • 打包一个agentmain代理jar
      • 运用attach
  • 参考

0 从零开始的Java RASP实现(一)

听寒小说网 https://www.3286.info

本科毕设做过Python的RASP之后,对这项技术很有兴趣,当时OpenRASP开始出现,并且Java的实现非常接近真正的运行时防御的概念。一直没有时间和足够的动力学习Java,最近一口气学了不少Java相关的东西,准备从反序列化和RASP两个方向继续深入学一下Java。这边笔记主要也是记录整个学习过程,目标是仿照OpenRASP的java实现,完成一个Java的RASP。

1 javaagent

1.1 Main方法启动前

概念介绍:

javaagent是java命令提供的一个参数,这个参数可以指定一个jar包,在真正的程序没有运行之前先运行指定的jar包。并且对jar包有两个要求:

  • jar包的MANIFEST.MF文件必须指定Premain-Class
  • Premain-Class指定的类必须实现premain()方法。

这个premain方法会在java命令行指定的main函数之前运行。

在java命令参数种,还可以看到其它参数,例如

-agentlib:<libname>[=<选项>]加载本机代理库 <libname>, 例如 -agentlib:hprof另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<选项>]按完整路径名加载本机代理库
-javaagent:<jarpath>[=<选项>]加载 Java 编程语言代理, 请参阅 java.lang.instrument

javaagent可以指定很多个,jvm会依次执行不同的jar。前面提到的premain方法有两种定义方式

public static void premain(String agentArgs, Instrumentation inst)public static void premain(String agentArgs)

根据sun.instrument.InstrumentationImpl 的源代码,可以知道,会优先调用第一种写法。这种方法可以在JDK1.5及之后的版本使用

如何使用

使用javaagent需要几个步骤:

  • 定义一个MANIFEST.MF文件,必须包含Premain-Class选项,也需要加入Can-Redefine-Classes和Can-Retransform-Classes选项,后面两个选项看名字就知道意思
  • 创建Premain-Class指定的类,类中包含premain方法,该方法可以进一步加载RASP实现原理
  • 将MANIFEST.MF和写好的各种类打包成jar
  • 启动java时,添加-javaagent:xx.jar,即可让java先自动执行写好的premain方法

在premain方法执行时,获取的Instrumentation对象还会加载大部分类,包括后面main方法执行时需要加载的各种类,但是抓不到系统类。也就是说,在这个位置在main方法执行前,就可以拦截或者重写类,结合ASM、javassist、cglib方式就可以实现对类的改写或者插桩。

创建agent

现在来实现一下,目录结构如下

-java-agent
----src
----|----main
----|----------java
----|--------------com
----|-------------------bitterz
----|----------------------PreMain
----|pom.xml

pom.xml使用idea创建maven项目自带的pom.xml即可,然后加入如下配置

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive><manifestEntries><Premain-Class>com.bitterz.PreMain</Premain-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration></plugin></plugins>
</build>

修改里面的premain即可,这样配置之后,直接使用idea的maven->package就可以打包成一个可以使用的agent.jar。

com.bitterz.PreMain如下:

package com.bitterz;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;public class PreMain {public static void premain(String agentArgs, Instrumentation inst){System.out.println("agentArgs:" + agentArgs);inst.addTransformer(new DefineTransformer(), true);}static class DefineTransformer implements ClassFileTransformer{@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {System.out.println("premain load Class: " + className);  // 注意这里的输出return classfileBuffer;}}
}

创建main

然后再创建一个要执行的main

package com.bitterz;public class Main {public static void main(String[] args) {System.out.println("Main.main() in test project");}
}

同样修改pom.xml配置,只需要再<manifestEntries>标签下添加一个<Main-Class>即可用idea的maven打包成一个jar,并且指定了main类。

两个项目用maven打包后,在命令行执行一下

可以看到premain先输出了加载的各种类,包括com.bitterz.Main,然后才是真正的main执行,最后结束时,premain还加载了一些结束时需要的类。到此,在启动main方法前,实现对后续类的加载或进一步修改操作,已经有了雏形

1.2 JVM启动后

前面的方法需要在main函数启动前,执行agent,但有些时候,jvm已经启动了,而且服务不能轻易暂停,但这个时候还是想对jvm中的类做一些修改应该怎么办呢?

attach机制

这就要引入attch机制了,jdk1.6之后在Instrumentation中添加了一种agentmain的代理方法,可以在main函数执行之后再运行。和premain函数一样,开发者可以编写一个包含agentmain函数的Java类,它也有两种写法:

public static void agentmain (String agentArgs, Instrumentation inst)public static void agentmain (String agentArgs)

同样的,带有Instrumentation的方法会被优先调用,开发者必须再MANIFEST.MF文件中设置Agent-Class来指定包含agentmain函数的类。

这种attach机制的具体实现在com.sun.tools.attach中,有如下两个类:

  • VirtualMachine 字面意义表示一个Java 虚拟机,也就是程序需要监控的目标虚拟机,提供了获取系统信息(比如获取内存dump、线程dump,类信息统计(比如已加载的类以及实例个数等), loadAgent,Attach 和 Detach (Attach 动作的相反行为,从 JVM 上面解除一个代理)等方法,可以实现的功能可以说非常之强大 。该类允许我们通过给attach方法传入一个jvm的pid(进程id),远程连接到jvm上

    代理类注入操作只是它众多功能中的一个,通过loadAgent方法向jvm注册一个代理程序agent,在该agent的代理程序中会得到一个Instrumentation实例,该实例可以 在class加载前改变class的字节码,也可以在class加载后重新加载。在调用Instrumentation实例的方法时,这些方法会使用ClassFileTransformer接口中提供的方法进行处理。

  • VirtualMachineDescriptor 则是一个描述虚拟机的容器类,配合 VirtualMachine 类完成各种功能

具体实现过程:通过VirtualMachine类的attach(pid)方法,便可以attach到一个运行中的java进程上,之后便可以通过loadAgent(agentJarPath)来将agent的jar包注入到对应的进程,然后对应的进程会调用agentmain方法。

从jdk根目录的lib/tools.jar源码一路追踪了一下VirtualMachine.attach方法,发现传入一个pid之后,在Windows下会通过WindowsVirtualMachine这个类的构造函数,调用native方法,实现对jvm进程的attach

启动一个长时间运行的jvm

底层方法还得靠c/c++来实现(滑稽),了解这个机制之后,回到前面的agentmain的实现流程。首先启动一个一直运行的jvm

package com.bitterz;public class Main {public static void main(String[] args) throws InterruptedException {System.out.println("Main.main() in test project start!!");Thread.sleep(300000000);System.out.println("Main.main() in test project end!!");}
}

打包一个agentmain代理jar

启动之后,把再写一个agentmain,并且还要写好MANIFEST.MF文件配置,代码和pom.xml如下:

package com.bitterz;import java.lang.instrument.Instrumentation;public class AgentMain {public static void agentmain(String agentArgs, Instrumentation instrumentation) {System.out.println("agentmain start!");System.out.println(instrumentation.toString());}
}
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><configuration><archive><manifestEntries><Agent-Class>com.bitterz.AgentMain</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration></plugin></plugins>
</build>

使用maven package打包成一个jar文件即可

运用attach

再开启动另一个java程序,使用进程号attach到前面一直运行的jvm,并使用loadAgent给这个jvm添加代理jar:

package com.bitterz.attach;import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;import java.io.IOException;public class AttachTest {public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {VirtualMachine attach = VirtualMachine.attach("12244");  // 命令行找到这个jvm的进程号attach.loadAgent("C:\Users\helloworld\Desktop\java learn\java-attach\target\java-attach-1.0-SNAPSHOT.jar");attach.detach();}
}

运行这个java文件,会看到前面一直sleep运行的jvm会输出agentmain里面给定的输出!

这里也就说明,通过attach机制,我们可以对指定运行中的jvm添加agent,而在premain方法中获取到Instrumentation对象,通过对Instrumentation对象添加transformer类,可以实现类转换(Class Transform),也就是在transform函数中结合修改字节码的方法(ASM、Javassist、cglib等)可以进一步实现RASP!

后续的文章将会写一写如何实现这些对指定类方法的Hook,以及如何运行时对底层函数的参数进行过滤。

参考

https://www.cnblogs.com/rickiyang/p/11368932.html

https://www.cnblogs.com/kendoziyu/p/maven-auto-build-javaagent-jar.html

Java底层防护 - OpenRASP核心源码浅析

这篇关于从零开始的Java RASP实现(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/162289

相关文章

Java中JSON格式反序列化为Map且保证存取顺序一致的问题

《Java中JSON格式反序列化为Map且保证存取顺序一致的问题》:本文主要介绍Java中JSON格式反序列化为Map且保证存取顺序一致的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未... 目录背景问题解决方法总结背景做项目涉及两个微服务之间传数据时,需要提供方将Map类型的数据序列化为co

Java Lambda表达式的使用详解

《JavaLambda表达式的使用详解》:本文主要介绍JavaLambda表达式的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言二、Lambda表达式概述1. 什么是Lambda表达式?三、Lambda表达式的语法规则1. 无参数的Lambda表

java中Optional的核心用法和最佳实践

《java中Optional的核心用法和最佳实践》Java8中Optional用于处理可能为null的值,减少空指针异常,:本文主要介绍java中Optional核心用法和最佳实践的相关资料,文中... 目录前言1. 创建 Optional 对象1.1 常规创建方式2. 访问 Optional 中的值2.1

Spring Boot 整合 Apache Flink 的详细过程

《SpringBoot整合ApacheFlink的详细过程》ApacheFlink是一个高性能的分布式流处理框架,而SpringBoot提供了快速构建企业级应用的能力,下面给大家介绍Spri... 目录Spring Boot 整合 Apache Flink 教程一、背景与目标二、环境准备三、创建项目 & 添

Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析

《Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析》InstantiationAwareBeanPostProcessor是Spring... 目录一、什么是InstantiationAwareBeanPostProcessor?二、核心方法解

IDEA如何实现远程断点调试jar包

《IDEA如何实现远程断点调试jar包》:本文主要介绍IDEA如何实现远程断点调试jar包的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录问题步骤总结问题以jar包的形式运行Spring Boot项目时报错,但是在IDEA开发环境javascript下编译

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

Spring @RequestMapping 注解及使用技巧详解

《Spring@RequestMapping注解及使用技巧详解》@RequestMapping是SpringMVC中定义请求映射规则的核心注解,用于将HTTP请求映射到Controller处理方法... 目录一、核心作用二、关键参数说明三、快捷组合注解四、动态路径参数(@PathVariable)五、匹配请

Java -jar命令如何运行外部依赖JAR包

《Java-jar命令如何运行外部依赖JAR包》在Java应用部署中,java-jar命令是启动可执行JAR包的标准方式,但当应用需要依赖外部JAR文件时,直接使用java-jar会面临类加载困... 目录引言:外部依赖JAR的必要性一、问题本质:类加载机制的限制1. Java -jar的默认行为2. 类加

Java进程CPU使用率过高排查步骤详细讲解

《Java进程CPU使用率过高排查步骤详细讲解》:本文主要介绍Java进程CPU使用率过高排查的相关资料,针对Java进程CPU使用率高的问题,我们可以遵循以下步骤进行排查和优化,文中通过代码介绍... 目录前言一、初步定位问题1.1 确认进程状态1.2 确定Java进程ID1.3 快速生成线程堆栈二、分析