jni安全利用的簡單學習

A8k1a4發表於2024-09-23

首先定義一個最簡單的類

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進行修改
jni安全利用的簡單學習

鍵入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 方法簽名不匹配

JNI 方法簽名是由類名、包名和方法名組合而成的。如果類名不一樣,即使方法內容相同,JNI 也會因為簽名不匹配而無法正確呼叫方法。

對於類 EvilClass,在 JNI 中,方法名是 Java_EvilClass_execCmd,而對於類 Eclass,方法名應該是 Java_Eclass_execCmd

也就是Java_EvilClass_execCmdJava_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);

相關文章