Openrasp源码分析
Openrasp是百度关于rasp技术的开源项目,由于工作需要,之前对rasp的源码进行了简单的分析。文章是之前就写好的,现在放出了,希望对大家有写帮助。
OpenRASP中java引擎的源码分析
安装包解压后目录结构如下:

RaspInstall.jar是OpenRASP中java引擎的安装(卸载)运行文件,在终端中执行以下命令进行安装(以tomcat为例):
java -jar RaspInstall.jar -install <tomcat_root>
值得注意的是:<tomcat_root>不是webapps目录,而是tomcat的根目录
rasp目录夹中java引擎的资源文件,目录结构如下图:

conf文件夹下是rasp.properties配置文件,里面的配置参数用于指定是否开启基线检测、拦截后跳转地址等等。
plugins文件夹下是official.js文件,OpenRASP是通过JavaScript插件来实现检测逻辑的,而official.js就是这个检测插件。
rasp.jar和rasp-engine.jar是java引擎的核心文件
一:RaspInstall.jar
反编译RaspInstall.jar文件后目录如下:

简单看下App.class的代码

以安装操作为例,获取命令行第二个参数(安装时指定web容器的根目录)后,先调用了newInstallerFactory()方法对操作系统类型进行判断,然后返回不同系统的安装工厂类(都继承了InstallerFactory类)

随后调用了工厂类父类InstallerFactory的getInstaller(File ServerRoot)方法

父类中getInstaller(File ServerRoot)就是对web容器做了一个判断,然后再调用子工厂类的getInstaller(String aerverName,String serverRoot)方法,方法中调用了不同的web容器安装类

后面就是具体的安装操作了,包括在web服务器下创建rasp目录,将资源文件放入进去;添加环境变量;配置容器启动参数(例如java引擎是通过java agent机制实现的,应用启动时需要指定-javaagent参数)。
二:rasp.jar
rasp.jar文件就是一个javaagent,会在web应用的main函数运行之前先运行。目录结构如下:

之前也说了rasp.jar文件就是一个javaagent,而Agent.java就是这个javaagent的入口类,内容如下:

Premain(String agentArg, Instrumentation inst)就是javaagent的入口函数。
函数中先调用了JarFileHelper.addJarToBootstrap(inst)

函数中将当前jar文件加入到了jdk的根路径下,优先加载。这是因为当去 hook 像 java.io.File 这样由 BootstrapClassLoader 加载的类的时候,无法从该类调用非 BootstrapClassLoader 加载的类中的接口,所以 agent.jar 会先将自己添加到 BootstrapClassLoader 的ClassPath下,这样 hook 由 BootstrapClassLoader 加载的类的时候就能够成功调用到 agent.jar 中的检测入口。
随后调用了ModuleLoader.load(agentArg, inst),用于加载所有的rasp模块,目前只有一个模块就是rasp-engine.jar.(目前还不是很能理解多个模块的用意)

方法中new了一个ModuleLoader(agentArg, inst),用于构造所有的模块。

大概意思就是调用各rasp模块入口类中的start方法。
三:rasp-engine.jar
先从rasp-engine.jar的入口文件EngineBoot.java中的入口方法start看起。

先是判断应用的conf目录下是否有日志配置文件rasp-log4j.xml,如果不存在则释放log4j日志配置文件,也就是将资源文件夹resources下的rasp-log4j.xml.default文件中的注释去掉,文件名修改后复制到conf文件夹下。
readVersion()用于通过/META/MANIFEST.MF文件读取当前openrasp的版本。
接下来调用了JsPluginManager.init(),用于初始化插件系统以及更新插件,跟进去看看:

JSContextFactory.init()主要用于JS上下文类的初始化,由于涉及到Rhino框架(一个开源的脚本引擎框架)的一些东西,没有深入了解了。目前知道的是有加载了资源文件夹resources下environment文件夹下的js文件,以及初始化一些属性。updatePlugin()方法中更新插件引擎。initFileWatcher()初始化一个文件时间的监视器,监测plugin目录下的js文件是否有更新。
回到start方法中,初始化JS引擎后,接下来调用了CheckerManager.init(),该方法用于初始化Checker管理器,也就是将所有的checker类实例都放入EnumMap中,方便之后通过Type来判断调用各自的Check类中的check()方法。最后就是调用initTransformer(inst)之后整个启动流程就结束了,这个方法用于初始化类字节码转换器,里面主要做了两件事(分为两部分):1.给类加载操作进行了插桩操作,当类加载的时候会先进入agent进行处理 2.对于初始化前就已经加载的类中执行了retransform处理。跟入函数:

第一部分:函数内先构造了一个自定义类字节码转换器,该转换器的构造函数中会自动扫描所有标有@HookAnnotation的类,并放入HashMap中,方便后面比较类加载器加载的类是否是我们想要hook的类。
接下来通过addTransformer将这个定义类字节码转换器添加到Instrumentation中,(这里涉及到javaagent的知识点,当Instrument读取字节码文件后会自动调用回调函数ClassFileLoadHook,该回调函数中做了很多事,其中一件就是调用ClassFileTransformer接口实现类对象中的transform方法)在这里也就是调用了CustomClassTransformer类中的transform方法,跟进该方法

该方法中主要是对目前类加载器所加载的类是否属于众多我们想要hook类的一种,如果是则调用对应hook类中的transformClass方法。在for循环中,取出了所有的hook类并通过hook类中各自的isClassMatched方法域当前类加载器所加载的类相比较,如果是我们想要hook的类,则通过AbstractClassHook(所有hook类的父类)中的transformClass方法来调用对应hook类中的hookMethod方法,该方法就是用于将待转换的类转换后以字节码数组的形式返回,这里的转换就是通过操作字节码在想要hook的类的关键函数前后加入安全检测代码。
这里以XXEHook这个hook类为例,跟进它hookMethod方法:

先是调用了getInvokeStaticSrc方法,该方法通过输入的参数获取静态方法的代码字符串并返回(在原方法基础上加了一些捕获异常的代码),接下来的insertBefore方法是对javassit框架中CtBehavior类的insertBefore方法做了包装,主要内容是通过字节码的形式在指定方法前加入指定的安全检测代码(也就是插桩技术,具体如果操作字节码是交给了javassit框架在做,我们只需要调用它的接口并指定想要插桩的函数插入的代码就行),该方法的第一个参数当前类加载器所加载的类的字节码(这个地方有点说不清楚!),第二个参数指定该类中想要进行插桩操作的方法的方法名,第三个参数是对该类中想要进行插桩操作的方法的参数以及返回类型的描述((Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V代表该方法有四个参数且都是String类型,返回void),第四个参数就是我们想要插入的代码字符串了。
具体的插入的什么安全检测代码,我们之后再回过来看,先不继续跟进了。这里附上一张官网关于Hook Class流程的说明:

回到initTransformer(inst)函数中来看第二部分:

显示调用了instrumentation.getAllLoadedClasses()用于获取所有加载过的类,for循环中对这些类做了一个判断,是否为需要hook的类,如果是则调用inst.retransformClasses(Class cls)像jvm发起重新转换的请求。
到这里整个openrasp启动流程已经结束了,流程图如下:

现在再回过头看看具体插入的安全检测代码是什么样子的,还是以XXEHook这个hook类为例。XXEHook类中插入的代码是checkXXE这个方法:

先对expandedSystemId参数做了校验,是否为空或者是否为当前线程已触发过检测的expandedSystemId。
如果是第一次触发检测通过XXEHook.getLocalExpandedSystemIds().add(expandedSystemId)将当前expandedSystemId加入到HashSet中,代表已经检测过。。。。(未完待续)
整个rasp-engine.jar的目录结构如下

hook文件夹目录结构如下

Plugin文件夹目录如下

Tool文件夹目录如下

Openrasp源码分析的更多相关文章
- ABP源码分析一:整体项目结构及目录
		ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ... 
- HashMap与TreeMap源码分析
		1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ... 
- nginx源码分析之网络初始化
		nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ... 
- zookeeper源码分析之五服务端(集群leader)处理请求流程
		leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ... 
- zookeeper源码分析之四服务端(单机)处理请求流程
		上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ... 
- zookeeper源码分析之三客户端发送请求流程
		znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ... 
- java使用websocket,并且获取HttpSession,源码分析
		转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ... 
- ABP源码分析二:ABP中配置的注册和初始化
		一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ... 
- ABP源码分析三:ABP Module
		Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ... 
随机推荐
- 读文件/写文件。http请求。读取文件列表。
			package transfor; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import j ... 
- BigDecimal类型转换
			djjfbr.setMoney(new BigDecimal(djjfbillrecord.getMoney())); 
- 分布式session解决——Spring-data-redis
			1.如果没有集成shiro来管理session,可以直接使用spring-session 2.若集成了shiro,需要Spring-data-redis (或 shiro-redis) 3.nginx ... 
- JS对象与原型链
			每个函数都存在一个prototype的属性,然后这个属性值为一个对象,我们称之为原型对象 每个对象都存在着一个隐藏的属性"__proto__" 这个属性引用了创建这个对象的函数的p ... 
- 杭电1532----Drainage Ditches『最大流』
			/* 网络流的最大流问题 刚学习Dinic算法.模版题 */ #include <cstring> #include <cstdio> #include <queue&g ... 
- GUI Design Studio的使用方法
			一.GUI Design Studio的介绍 GUI DesignStudio 是一个给应用软件设计图形用户界面的专业工具,它可在画基于web形态的原型时,可以用 Axure RP. Balsamiq ... 
- nodejs+koa在header里面添加header信息
			参考:https://koa.bootcss.com/ ctx.append('resultCode', '0000'); ctx.append('resultMessage', 'success') ... 
- react-native run-android时 SDK location not found.报错
			报错 原因 缺少local.properties文件(SDK location) 解决 方法一:在android Studio中打开项目android目录,会自动创建local.properties文 ... 
- Codechef August Challenge 2018 : Chef at the River
			传送门 (要是没有tjm(Sakits)的帮忙,我还真不知道啥时候能做出来 结论是第一次带走尽可能少的动物,使未带走的动物不冲突,带走的这个数量就是最优解. 首先这个数量肯定是下界,更少的话连第一次都 ... 
- Windows中使用ssh利用公钥登入远程服务器
			方式:使用 Winscp 密钥登录 我们平时开发多会使用 ftp 来上传下载文件,尤其是很多 Linux 环境下. 其实 Linux 默认是不提供 ftp 的,需要你额外安装 FTP 服务 ... 
