当前位置: 首页 > news >正文

RASP hook插桩原理解析

  • javaagent技术,实现提前加载类字节码
  • 实现hook,插桩技术
    • javassist技术
    • ASM字节码技术
  • 像加载jar,有两种方式
    • premain启动前加载:每次变动jar包内容,都需要进行重启服务器
    • 利用java的动态attch加载原理,采用probe技术,实现动态加载jar包:容易重写方法

十分清晰的插桩流程图(使用了javaAgent、插桩技术)


java文件运行原理

OpenRASP是怎么进行插桩操作

  • 启动时候进行了插桩操作,当有类被 ClassLoader 加载时候,所以会把该类的字节码先交给自定义的 Transformer 处理
  • 自定义 Transformer 会判断该类是否为需要 hook 的类,如果是会将该类交给 javassist 字节码处理框架进行处理,javassist
  • javassist 框架会将类的字节码依照事件驱动模型逐步解析每个方法,当触发了我们需要 hook 的方法,我们会在方法的开头或者结尾插入进入检测函数的字节码
  • 把 hook 好的字节码返回给 transformer 从而载入虚拟机
个人理解【插桩】
  • 无论是哪种插桩方式,都是以类加载后,修改class文件的字节码为主;
  • 可以看出来,OpenRasp是借助javassist进行对关注类两头监听,查看里面的行为是否违规;
  • 大致流程就是一个关注类被加载了,拿这个类的class文件,对文件的字节码进行修改成自己想要的class,然后在放回去,载入JVM,进行执行,原本那个class文件就直接放弃了;
  • 好处就是源代码没有修改,只是修改了class文件。

注意:OpenRASP是启动前加载的,所以所有的类模块加载都是在启动前就已经换成自己想要的字节码了,每当调用关注类时,就不会调用原来的代码,而是调用编写好的代码强化的代码模块

OpenRasp是如何进行请求处理

  1. 服务器收到一个请求,从而进入了服务器的请求 hook 点
  2. 服务器发起SQL查询
  3. 进入 SQLStatementHook 点,我们挂钩了 execute、executeUpdate、executeQuery 等方法,从该方法进入检测流程如下:
    1. 判断当前线程是否为请求线程(第一步标记的),如果是继续下面检测
    2. 采集 connection_id(这个字段仅JDBC支持)、SQL 语句以及数据库类型 等信息
    3. 构建参数信息,调用本地插件和 JS 插件进行安全检测,JS 插件由 Rhino 引擎执行,Rhino 引擎执行是 mozilla 为 java 提供的 JavaScript引擎,该引擎会将 JS 代码编译为 java 的 class 字节码在 JVM 中运行,Rhino 引擎文档
    4. 根据插件的执行结果决定是拦截请求、放行还是仅打印日志
  4. 进入 SQLResultSetHook 点,我们挂钩了 resultSet.next 方法
    1. 调用本地插件检查是否发生拖库行为,默认策略为一次查询结果超过500条就报警
  5. 若决定拦截攻击
    1. 输出报警日志到 logs/alarm.log
    2. 如果header还没有发出,默认使用 302 跳转到拦截页面
    3. 如果body还没有发出,则重置未发送的body
    4. 输出自定义拦截页面跳转js脚本


javassist技术

在一个测试jar包执行前,提前执行编写好的permain函数,将原本的测试jar的字节码,替换成自己想要的字节码,然后再放回去,JVM只执行我修改好的class文件,转义后,这个代码就拥有自我监控和拦截功能,就达成了无入侵自我保护。

ASM技术和Javassist技术的差异

先说说相同点
这两个技术都是java字节码修改技术,都可以对未加入JVM的class文件,进行修改。
不同之处
ASM主要是通过字节码进行修改,而javassist是通过java代码进行修改的
直接看下他们之间代码,就懂了
同一份java代码,看下他们二者的区别

// 修改前public void test(String args) {System.out.println("rasp Test,正常通过!!");}
// 修改后
public void test(String var1) {SqlFilter var3 = new SqlFilter();if (var3.filter(var1)) {throw new SQLException("invalid sql because of security check");} else {System.out.println("rasp Test,正常通过!!");}}// 添加部分SqlFilter var3 = new SqlFilter();if (var3.filter(var1)) {throw new SQLException("invalid sql because of security check");} else {// 原本代码 XXXX}

看下ASM进行插桩

        mv.visitTypeInsn(NEW,"com/wu/javaagent01/filter/SqlFilter");mv.visitInsn(DUP);mv.visitMethodInsn(INVOKESPECIAL,"com/wu/javaagent01/filter/SqlFilter","<init>","()V",false);mv.visitVarInsn(ASTORE,3);mv.visitVarInsn(ALOAD,3);mv.visitVarInsn(ALOAD,1);mv.visitMethodInsn(INVOKEVIRTUAL,"com/wu/javaagent01/filter/SqlFilter", "filter","(Ljava/lang/Object;)Z",false);mv.visitJumpInsn(IFEQ, l92);mv.visitTypeInsn(NEW, "java/sql/SQLException");mv.visitInsn(DUP);mv.visitLdcInsn("invalid sql because of security check");mv.visitMethodInsn(INVOKESPECIAL, "java/sql/SQLException", "<init>", "(Ljava/lang/String;)V", false);mv.visitInsn(ATHROW);mv.visitLabel(l92);mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);

再看看javassist对这个代码如何插桩的

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("YourClassName"); // 替换成你的类名CtMethod ctMethod = ctClass.getDeclaredMethod("checkListFiles");CtClass exceptionClass = classPool.get("java.sql.SQLException");// 创建异常抛出代码块
CtConstructor exceptionConstructor = exceptionClass.getDeclaredConstructor(new CtClass[] { classPool.get("java.lang.String") });
CtConstructor ctConstructor = ctClass.getClassInitializer();
ctConstructor.insertAfter("{" +"if (condition) {" +"    try {" +"        throw new java.sql.SQLException(\"invalid sql because of security check\");" +"    } catch (java.sql.SQLException e) {" +"        e.printStackTrace();" +"    }" +"}");// 保存修改后的字节码ctClass.writeFile();
} catch (Exception e) {e.printStackTrace();
}
http://www.lryc.cn/news/174599.html

相关文章:

  • Pygame中Sprite的使用方法6-5
  • 浅谈为什么多态只能是指针或引用
  • js看代码说输出
  • Java笔记:使用javassist修改class文件内方法
  • 华为云云耀云服务器L实例评测 |云服务器性能评测
  • iphone的safari浏览器实现全屏的pwa模式,并修改顶部状态栏背景颜色
  • springboot对接rabbitmq并且实现动态创建队列和消费
  • Spring的后处理器-BeanFactoryPostprocessor
  • Flutter 必备知识点
  • 什么是FMEA(失效模式和影响分析)?
  • Redis面试题(三)
  • Python错误处理指南:优雅应对异常情况
  • MySQL学习笔记12
  • 【owt】构建m79的owt-client-native:使用vs2017
  • Cpp/Qt-day020918Qt
  • Spring面试题10:Spring的XMLBeanFactory怎么使用
  • 自定义数据类型
  • 产品团队的需求验证和确认
  • 【JVM】类加载的过程
  • Golang 结构化日志包 log/slog 详解(四):分组、上下文和属性值类型
  • 小白学Python:提取Word中的所有图片,只需要1行代码
  • pip修改位于用户目录下的缓存目录
  • 更新、修改
  • 山西电力市场日前价格预测【2023-09-25】
  • 从collections库的Counter类看items()方法和enumerate()方法
  • 2023-09-24 LeetCode每日一题(LRU 缓存)
  • 《计算机视觉中的多视图几何》笔记(10)
  • 【一、虚拟机vmware安装】
  • uniapp 离线打包 plus.runtime.install 安装页面不弹起
  • Docker 自动化部署(保姆级教程)