RASP简单实现
字数 1703 2025-08-24 07:48:22

Javassist与RASP实现详解

一、Javassist基础

1.1 Javassist简介

Javassist (JAVA programming ASSISTant) 是一个用于在Java中编辑字节码的类库,它使Java程序能够在运行时定义新类,并在JVM加载时修改类文件。与反射类似,但开销更低。

1.2 核心API

ClassPool相关

  • getDefault(): 返回默认的单例ClassPool
  • appendClassPath, insertClassPath: 添加类搜索路径
  • get(), getCtClass(): 根据类路径获取CtClass对象
  • makeClass(): 创建新类

CtClass操作

  • freeze(): 冻结类使其不可修改
  • isFrozen(): 判断类是否被冻结
  • defrost(): 解冻类使其可修改
  • prune(): 删除不必要的属性减少内存占用
  • detach(): 从ClassPool中删除类
  • writeFile(): 生成.class文件
  • toClass(): 通过类加载器加载CtClass

CtMethod操作

  • insertBefore(): 在方法起始位置插入代码
  • insertAfter(): 在所有return前插入代码
  • insertAt(): 在指定位置插入代码
  • setBody(): 设置方法体内容
  • make(): 创建新方法

1.3 基本操作示例

1. 创建类

ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.makeClass("Javassist.Hello");
ctClass.writeFile();

2. 添加属性

CtField name = new CtField(cp.get("java.lang.String"), "name", ctClass);
name.setModifiers(Modifier.PUBLIC);
ctClass.addField(name, CtField.Initializer.constant("Sentiment"));

3. 添加方法

CtMethod ctMethod = new CtMethod(CtClass.voidType, "Hello1", 
    new CtClass[]{CtClass.intType, CtClass.charType}, ctClass);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("System.out.println(\"This is test !\");");
ctClass.addMethod(ctMethod);

4. 添加构造器

CtConstructor cons = new CtConstructor(
    new CtClass[]{cp.getCtClass("java.lang.String")}, ctClass);
cons.setBody("{name=\"Sentiment\";}");
ctClass.addConstructor(cons);

5. 修改已有类

CtClass ctClass = cp.get("Javassist.Test");
CtConstructor test = ctClass.getConstructors()[0];
test.setBody("{System.out.println(\"Changing.\");}");
ctClass.writeFile();

6. 加载字节码

ctClass.toBytecode();
ctClass.toClass().newInstance();

1.4 特殊变量

标识符 作用
$0, $1, $2... $0代表this,$1$2代表方法参数
$args 方法参数数组(Object[])
`

\[` | 所有方法参数简写 | | `$r` | 返回结果类型(用于强制转换) | | `$_` | 方法返回值 | | `$sig` | 参数类型对象数组 | | `$type` | 返回值类型 | | `$class` | 正在修改的类 | ## 二、RASP实现 ### 2.1 RASP简介 RASP(Runtime application self-protection)是应用程序运行时防护技术,与传统WAF的主要区别在于防护层级更底层,能在功能调用前或调用时获取方法参数等信息进行安全判定。 #### RASP与WAF对比 **优点:** 1. 误报率低:基于应用程序上下文精确分析 2. 保护全面:能监控输入和输出 **缺点:** 1. 性能损耗(约5%) 2. 部署成本高(需针对不同技术栈) ### 2.2 双亲委派问题 Java类加载采用双亲委派机制: 1. BootstrapClassLoader(C++实现) 2. ExtClassLoader(父类为null) 3. AppClassLoader(父类为ExtClassLoader) 默认情况下,premain和agentmain由AppClassLoader加载,当需要修改BootstrapClassLoader加载的类时会出现问题。 **解决方案:** ```java inst.appendToBootstrapClassLoaderSearch(new JarFile(localJarPath)); ``` ### 2.3 RASP实现示例 #### 目标:Hook ProcessBuilder执行cmd **Main.java** ```java public class Main { public static void main(String[] args) throws IOException { ProcessBuilder command = new ProcessBuilder().command("cmd", "/c", "chdir"); Process process = command.start(); InputStream inputStream = process.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); System.out.println(bufferedReader.readLine()); } } ``` **PreMainDemo.java** ```java public class PreMainDemo { public static void premain(String agentArgs, Instrumentation inst) throws IOException, UnmodifiableClassException { // 先加载ProcessBuilder类 ProcessBuilder processBuilder = new ProcessBuilder(); Class[] classes = inst.getAllLoadedClasses(); for (Class aClass : classes) { if (aClass.getName().equals("java.lang.ProcessBuilder") && inst.isModifiableClass(aClass)) { inst.addTransformer(new TransformerDemo(), true); inst.retransformClasses(aClass); } } } } ``` **TransformerDemo.java** ```java public class TransformerDemo implements ClassFileTransformer { public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { byte[] bytes = null; if (className.equals("java/lang/ProcessBuilder")) { ClassPool cp = ClassPool.getDefault(); CtClass ctClass = null; try { ctClass = cp.get("java.lang.ProcessBuilder"); CtMethod[] methods = ctClass.getMethods(); String source = "if ($0.command.get(0).equals(\"cmd\")){\n" + " System.out.println(\"Dangerous!\");\n" + " System.out.println($0);\n" + " return null;\n" + "}"; for (CtMethod method : methods) { if (method.getName().equals("start")) { method.insertBefore(source); break; } } bytes = ctClass.toBytecode(); } catch (Exception e) { e.printStackTrace(); } finally { if (ctClass != null) { ctClass.detach(); } } } return bytes; } } ``` ### 2.4 打包配置 **Main.jar的pom配置** ```xml org.apache.maven.plugins maven-jar-plugin RASP.Main ``` **agent.jar的pom配置** ```xml org.apache.maven.plugins maven-jar-plugin maven-assembly-plugin RASP.PreMainDemo true true jar-with-dependencies make-assembly package single ``` ### 2.5 执行命令 ```bash java -javaagent:AgentMemory-1.0-SNAPSHOT-jar-with-dependencies.jar=Sentiment -jar AgentMemory-1.0-SNAPSHOT.jar ``` ## 三、高级技巧 ### 3.1 ExprEditor修改代码 使用`ExprEditor`可以精确修改方法体内的表达式: ```java ctMethod.instrument(new ExprEditor(){ public void edit(MethodCall m) throws CannotCompileException { if (m.getClassName().equals("java.io.PrintStream") && m.getMethodName().equals("print")){ m.replace("System.out.println( \]

);");
}
}
});


### 3.2 方法调用前后插入代码

```java
ctMethod.insertBefore("System.out.println(\"我在前面插入:\"+$1);");
ctMethod.insertAfter("System.out.println(\"我在后面插入了:\"+$2);");

四、总结

本文详细介绍了Javassist的基本用法和RASP的实现原理,包括:

  1. Javassist的核心API和基本操作
  2. RASP的核心概念和实现方式
  3. 双亲委派问题及解决方案
  4. 完整的RASP示例实现
  5. 高级代码修改技巧

通过Javassist可以灵活地修改字节码,结合Java Agent技术可以实现强大的运行时防护功能。

Javassist与RASP实现详解 一、Javassist基础 1.1 Javassist简介 Javassist (JAVA programming ASSISTant) 是一个用于在Java中编辑字节码的类库,它使Java程序能够在运行时定义新类,并在JVM加载时修改类文件。与反射类似,但开销更低。 1.2 核心API ClassPool相关 getDefault() : 返回默认的单例ClassPool appendClassPath , insertClassPath : 添加类搜索路径 get() , getCtClass() : 根据类路径获取CtClass对象 makeClass() : 创建新类 CtClass操作 freeze() : 冻结类使其不可修改 isFrozen() : 判断类是否被冻结 defrost() : 解冻类使其可修改 prune() : 删除不必要的属性减少内存占用 detach() : 从ClassPool中删除类 writeFile() : 生成.class文件 toClass() : 通过类加载器加载CtClass CtMethod操作 insertBefore() : 在方法起始位置插入代码 insertAfter() : 在所有return前插入代码 insertAt() : 在指定位置插入代码 setBody() : 设置方法体内容 make() : 创建新方法 1.3 基本操作示例 1. 创建类 2. 添加属性 3. 添加方法 4. 添加构造器 5. 修改已有类 6. 加载字节码 1.4 特殊变量 | 标识符 | 作用 | |--------|------| | $0 , $1 , $2 ... | $0 代表this, $1 、 $2 代表方法参数 | | $args | 方法参数数组(Object[ ]) | | $$ | 所有方法参数简写 | | $r | 返回结果类型(用于强制转换) | | $_ | 方法返回值 | | $sig | 参数类型对象数组 | | $type | 返回值类型 | | $class | 正在修改的类 | 二、RASP实现 2.1 RASP简介 RASP(Runtime application self-protection)是应用程序运行时防护技术,与传统WAF的主要区别在于防护层级更底层,能在功能调用前或调用时获取方法参数等信息进行安全判定。 RASP与WAF对比 优点: 误报率低:基于应用程序上下文精确分析 保护全面:能监控输入和输出 缺点: 性能损耗(约5%) 部署成本高(需针对不同技术栈) 2.2 双亲委派问题 Java类加载采用双亲委派机制: BootstrapClassLoader(C++实现) ExtClassLoader(父类为null) AppClassLoader(父类为ExtClassLoader) 默认情况下,premain和agentmain由AppClassLoader加载,当需要修改BootstrapClassLoader加载的类时会出现问题。 解决方案: 2.3 RASP实现示例 目标:Hook ProcessBuilder执行cmd Main.java PreMainDemo.java TransformerDemo.java 2.4 打包配置 Main.jar的pom配置 agent.jar的pom配置 2.5 执行命令 三、高级技巧 3.1 ExprEditor修改代码 使用 ExprEditor 可以精确修改方法体内的表达式: 3.2 方法调用前后插入代码 四、总结 本文详细介绍了Javassist的基本用法和RASP的实现原理,包括: Javassist的核心API和基本操作 RASP的核心概念和实现方式 双亲委派问题及解决方案 完整的RASP示例实现 高级代码修改技巧 通过Javassist可以灵活地修改字节码,结合Java Agent技术可以实现强大的运行时防护功能。