jni安全利用的简单学习
首先定义一个最简单的类
public class EvilClass { public static native String execCmd(String cmd); }
因为我是MacOs端,在当前目录执行
javac EvilClass.java javac -h . EvilClass.java
生成 EvilClass.h 文件
/* DO NOT EDIT THIS FILE - it is machine generated */ #include </Users/这里找不到,可以直接通过绝对路径添加头文件/jni.h> #include </Users/这里找不到,可以直接通过绝对路径添加头文件/jni_md.h> /* Header for class EvilClass */ #ifndef INCLUDED_EVILCLASS // 修改了预处理器宏名,避免以下划线开头 #define INCLUDED_EVILCLASS #ifdef __cplusplus extern "C" { #endif /* * Class: EvilClass * Method: execCmd * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_EvilClass_execCmd (JNIEnv *, jclass, jstring); #ifdef __cplusplus } #endif #endif // INCLUDED_EVILCLASS
查找的命令如下
find $JAVA_HOME -name "jni.h"
并且需要针对CmakeList.txt进行修改
键入C代码
// // Created by water aka on 2024/9/23. // #include <string.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include "EvilClass.h" int execmd(const char *cmd, char *result) { char buffer[1024*12]; //定义缓冲区 FILE *pipe = popen(cmd, "r"); //打开管道,并执行命令 if(!pipe) return 0; //打开管道,并执行命令 while (!feof(pipe)) { if(fgets(buffer , 128 , pipe)) { //将管道输出到result中 strcat(result, buffer); } } pclose(pipe); return 1; } JNIEXPORT jstring JNICALL Java_EvilClass_execCmd(JNIEnv *env, jclass 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 -fPIC -I"/Library/对应CmakeList.txt的路径/include" \ -I"/Library//Library/对应CmakeList.txt的路径/include/darwin" \ -shared -o libcmd.jnilib EvilClass.c
后续通过System.load
构建java,调用即可
这里有一个坑点,因为刚接触JNI,所以没发现,相同的代码在不同类下,不能够执行命令,例如
原因是JNI 方法签名不匹配
JNI 方法签名是由类名、包名和方法名组合而成的。如果类名不一样,即使方法内容相同,JNI 也会因为签名不匹配而无法正确调用方法。
对于类 EvilClass
,在 JNI 中,方法名是 Java_EvilClass_execCmd
,而对于类 Eclass
,方法名应该是 Java_Eclass_execCmd
。
也就是Java_EvilClass_execCmd
和Java_Eclass_execCmd
的区别
如果 Java 类是 EvilClass
,那么 JNI 方法的签名应该是:
JNIEXPORT jstring JNICALL Java_EvilClass_execCmd(JNIEnv *, jclass, jstring);
如果 Java 类是 Eclass
,那么 JNI 方法的签名应该是:
JNIEXPORT jstring JNICALL Java_Eclass_execCmd(JNIEnv *, jclass, jstring);