从零开始的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实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Python实现批量CSV转Excel的高性能处理方案

《Python实现批量CSV转Excel的高性能处理方案》在日常办公中,我们经常需要将CSV格式的数据转换为Excel文件,本文将介绍一个基于Python的高性能解决方案,感兴趣的小伙伴可以跟随小编一... 目录一、场景需求二、技术方案三、核心代码四、批量处理方案五、性能优化六、使用示例完整代码七、小结一、

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2