From 1000e02d49127f409299292163721846bbba130b Mon Sep 17 00:00:00 2001 From: Sven Date: Tue, 20 Jan 2026 16:13:37 +0100 Subject: [PATCH] Add support for FileDispatcherImpl/UnixNativeDispatcher --- CMakeLists.txt | 2 +- src/fdi_hook.c | 80 +++++++++++++++++++++++++++++++++++++ src/fdi_hook.h | 17 ++++++++ src/fis_hook.c | 100 +++++++++++++---------------------------------- src/fis_hook.h | 32 +++++++-------- src/substagent.c | 29 ++++++++++---- src/utils.c | 53 +++++++++++++++++++++++++ src/utils.h | 1 + 8 files changed, 216 insertions(+), 98 deletions(-) create mode 100644 src/fdi_hook.c create mode 100644 src/fdi_hook.h diff --git a/CMakeLists.txt b/CMakeLists.txt index da68a21..6b2b440 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ add_custom_target(build_info ALL find_package(JNI REQUIRED) -add_library(substagent SHARED src/substagent.c src/fis_hook.c src/utils.c) +add_library(substagent SHARED src/substagent.c src/fis_hook.c src/fdi_hook.c src/utils.c) add_library(stb INTERFACE) target_include_directories(stb INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/external/stb) diff --git a/src/fdi_hook.c b/src/fdi_hook.c new file mode 100644 index 0000000..6d99823 --- /dev/null +++ b/src/fdi_hook.c @@ -0,0 +1,80 @@ +#include "fdi_hook.h" +#include "stb_ds.h" +#include "utils.h" +#include +#include + +und_open0_t und_real_open0 = NULL; + +fdi_read0_t fdi_real_read0 = NULL; + +struct file { + jint key; + struct file_data value; +} *fdi_files_map = NULL; + +jint JNICALL und_open0_hook(JNIEnv *env, jobject thiz, jlong path_address, jint flags, jint mode) { + jint fd = und_real_open0(env, thiz, path_address, flags, mode); + const char *path = (const char *)(intptr_t)path_address; + if (!path || !is_config_path(path)) { + return fd; + } + + FILE *file = fopen(path, "rb"); + if (!file) { + jclass fileNotFoundException = + (*env)->FindClass(env, "java/io/FileNotFoundException"); + (*env)->ThrowNew(env, fileNotFoundException, path); + return fd; + } + fseek(file, 0, SEEK_END); + size_t fsize = ftell(file); + if (fsize == 0) { + return fd; + } + fseek(file, 0, SEEK_SET); + + char *data = malloc(fsize + 1); + fread(data, fsize, 1, file); + data[fsize] = '\0'; + + parse_file(&data, fsize); + + size_t fd_len = strlen(data); + + struct file_data file_data; + file_data.data = data; + file_data.length = fd_len; + file_data.index = 0; + file_data.freed = 0; + hmput(fdi_files_map, fd, file_data); + + return fd; +} + +jint JNICALL fdi_read0_hook(JNIEnv *env, jobject thiz, jobject fdObject, jlong address, jint len) { + jclass class = (*env)->GetObjectClass(env, fdObject); + jfieldID fieldID = (*env)->GetFieldID(env, class, "fd", "I"); + jint fd = (*env)->GetIntField(env, fdObject, fieldID); + + struct file *file = hmgetp(fdi_files_map, fd); + if (file->key != fd) { + return fdi_real_read0(env, thiz, fdObject, address, len); + } else if (file->value.freed) { + return -1; + } + + struct file_data *file_data = &file->value; + + int n = MIN(len, file_data->length - file_data->index); + memcpy((void *)(intptr_t)address, file_data->data + file_data->index, n); + + if (n == file_data->length - file_data->index) { + free(file_data->data); + file_data->freed = 1; + } else { + file_data->index += n; + } + + return n; +} \ No newline at end of file diff --git a/src/fdi_hook.h b/src/fdi_hook.h new file mode 100644 index 0000000..4123d62 --- /dev/null +++ b/src/fdi_hook.h @@ -0,0 +1,17 @@ +#ifndef FDI_HOOK_H +#define FDI_HOOK_H + +#include +#include + +typedef jint (JNICALL *und_open0_t)(JNIEnv*, jobject, jlong, jint, jint); +extern und_open0_t und_real_open0; + +typedef jint (JNICALL *fdi_read0_t)(JNIEnv*, jobject, jobject, jlong, jint); +extern fdi_read0_t fdi_real_read0; + +jint JNICALL und_open0_hook(JNIEnv*, jobject, jlong, jint, jint); + +jint JNICALL fdi_read0_hook(JNIEnv*, jobject, jobject, jlong, jint); + +#endif \ No newline at end of file diff --git a/src/fis_hook.c b/src/fis_hook.c index 9455aa8..c5473a3 100644 --- a/src/fis_hook.c +++ b/src/fis_hook.c @@ -3,19 +3,19 @@ #define STB_DS_IMPLEMENTATION #include "stb_ds.h" -readBytes_t fis_real_readBytes = NULL; -open0_t fis_real_open0 = NULL; -read0_t fis_real_read0 = NULL; -length0_t fis_real_length0 = NULL; -position0_t fis_real_position0 = NULL; -skip0_t fis_real_skip0 = NULL; -available0_t fis_real_available0 = NULL; -close_t fis_real_close = NULL; +fis_readBytes_t fis_real_readBytes = NULL; +fis_open0_t fis_real_open0 = NULL; +fis_read0_t fis_real_read0 = NULL; +fis_length0_t fis_real_length0 = NULL; +fis_position0_t fis_real_position0 = NULL; +fis_skip0_t fis_real_skip0 = NULL; +fis_available0_t fis_real_available0 = NULL; +fis_close_t fis_real_close = NULL; struct file{ jint key; struct file_data value; -} *files_map = NULL; +} *fis_files_map = NULL; jint JNICALL fis_readBytes_hook(JNIEnv* env, jobject thiz, jbyteArray buf, jint off, jint len) { if (!has_tag(thiz)) { @@ -27,7 +27,7 @@ jint JNICALL fis_readBytes_hook(JNIEnv* env, jobject thiz, jbyteArray buf, jint jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method); - struct file *file = hmgetp(files_map, hash); + struct file *file = hmgetp(fis_files_map, hash); if (file->key != hash || file->value.freed) { return -1; } @@ -86,66 +86,20 @@ void JNICALL fis_open0_hook(JNIEnv* env, jobject thiz, jstring jpath) { } fseek(file, 0, SEEK_SET); - char *file_data = malloc(fsize + 1); - fread(file_data, fsize, 1, file); - file_data[fsize] = '\0'; + char *data = malloc(fsize + 1); + fread(data, fsize, 1, file); + data[fsize] = '\0'; - int dollar_sign_matched_index = -1; - char env_var[4096]; - env_var[0] = '\0'; - for (size_t i = 0; i < fsize; i++) { - char c = file_data[i]; - if (dollar_sign_matched_index != -1) { - if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c == 95) { - size_t len = strlen(env_var); - env_var[len] = c; - env_var[len + 1] = '\0'; - } else { - if (strlen(env_var) == 0) continue; - char* value = getenv(env_var); - if (!value) { - dollar_sign_matched_index = -1; - env_var[0] = '\0'; - continue; - } + parse_file(&data, fsize); - size_t env_len = i - dollar_sign_matched_index; + size_t fd_len = strlen(data); - char* new_data = substitute(value, dollar_sign_matched_index, i, env_len, file_data); - free(file_data); - file_data = new_data; - - dollar_sign_matched_index = -1; - env_var[0] = '\0'; - continue; - } - } else { - if (c == '$') { - dollar_sign_matched_index = i; - } - } - } - if (dollar_sign_matched_index != -1) { - if (strlen(env_var) != 0) { - char* value = getenv(env_var); - if (value) { - size_t env_len = fsize - dollar_sign_matched_index; - - char* new_data = substitute(value, dollar_sign_matched_index, fsize, env_len, file_data); - free(file_data); - file_data = new_data; - } - } - } - - size_t fd_len = strlen(file_data); - - struct file_data fd; - fd.data = file_data; - fd.length = fd_len; - fd.index = 0; - fd.freed = 0; - hmput(files_map, hash, fd); + struct file_data file_data; + file_data.data = data; + file_data.length = fd_len; + file_data.index = 0; + file_data.freed = 0; + hmput(fis_files_map, hash, file_data); (*env)->ReleaseStringUTFChars(env, jpath, path); } @@ -159,7 +113,7 @@ jint JNICALL fis_read0_hook(JNIEnv* env, jobject thiz) { jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method); - struct file *file = hmgetp(files_map, hash); + struct file *file = hmgetp(fis_files_map, hash); if (file->key != hash || file->value.freed) { return -1; } @@ -183,7 +137,7 @@ jlong JNICALL fis_length0_hook(JNIEnv* env, jobject thiz) { jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method); - struct file *file = hmgetp(files_map, hash); + struct file *file = hmgetp(fis_files_map, hash); if (file->key != hash) { return -1; } @@ -201,7 +155,7 @@ jlong JNICALL fis_position0_hook(JNIEnv* env, jobject thiz) { jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method); - struct file *file = hmgetp(files_map, hash); + struct file *file = hmgetp(fis_files_map, hash); if (file->key != hash) { return -1; } @@ -219,7 +173,7 @@ jlong JNICALL fis_skip0_hook(JNIEnv* env, jobject thiz, jlong n) { jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method); - struct file *file = hmgetp(files_map, hash); + struct file *file = hmgetp(fis_files_map, hash); if (file->key != hash) { return 0; } @@ -248,7 +202,7 @@ jint JNICALL fis_available0_hook(JNIEnv* env, jobject thiz) { jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method); - struct file *file = hmgetp(files_map, hash); + struct file *file = hmgetp(fis_files_map, hash); if (file->key != hash) { return 0; } @@ -267,7 +221,7 @@ void JNICALL fis_close_hook(JNIEnv* env, jobject thiz) { jint hash = (*env)->CallIntMethod(env, thiz, hash_code_method); - struct file *file = hmgetp(files_map, hash); + struct file *file = hmgetp(fis_files_map, hash); if (file->key != hash) { return; } diff --git a/src/fis_hook.h b/src/fis_hook.h index 717c537..b38f6e6 100644 --- a/src/fis_hook.h +++ b/src/fis_hook.h @@ -4,22 +4,22 @@ #include #include -typedef void (JNICALL *open0_t)(JNIEnv*, jobject, jstring); -typedef jint (JNICALL *readBytes_t)(JNIEnv*, jobject, jbyteArray, jint, jint); -typedef jint (JNICALL *read0_t)(JNIEnv*, jobject); -typedef jlong (JNICALL *length0_t)(JNIEnv*, jobject); -typedef jlong (JNICALL *position0_t)(JNIEnv*, jobject); -typedef jlong (JNICALL *skip0_t)(JNIEnv*, jobject, jlong); -typedef jint (JNICALL *available0_t)(JNIEnv*, jobject); -typedef void (JNICALL *close_t)(JNIEnv*, jobject); -extern readBytes_t fis_real_readBytes; -extern open0_t fis_real_open0; -extern read0_t fis_real_read0; -extern length0_t fis_real_length0; -extern position0_t fis_real_position0; -extern skip0_t fis_real_skip0; -extern available0_t fis_real_available0; -extern close_t fis_real_close; +typedef void (JNICALL *fis_open0_t)(JNIEnv*, jobject, jstring); +typedef jint (JNICALL *fis_readBytes_t)(JNIEnv*, jobject, jbyteArray, jint, jint); +typedef jint (JNICALL *fis_read0_t)(JNIEnv*, jobject); +typedef jlong (JNICALL *fis_length0_t)(JNIEnv*, jobject); +typedef jlong (JNICALL *fis_position0_t)(JNIEnv*, jobject); +typedef jlong (JNICALL *fis_skip0_t)(JNIEnv*, jobject, jlong); +typedef jint (JNICALL *fis_available0_t)(JNIEnv*, jobject); +typedef void (JNICALL *fis_close_t)(JNIEnv*, jobject); +extern fis_readBytes_t fis_real_readBytes; +extern fis_open0_t fis_real_open0; +extern fis_read0_t fis_real_read0; +extern fis_length0_t fis_real_length0; +extern fis_position0_t fis_real_position0; +extern fis_skip0_t fis_real_skip0; +extern fis_available0_t fis_real_available0; +extern fis_close_t fis_real_close; jint JNICALL fis_readBytes_hook(JNIEnv* env, jobject thiz, jbyteArray buf, jint off, jint len); void JNICALL fis_open0_hook(JNIEnv* env, jobject thiz, jstring jpath); diff --git a/src/substagent.c b/src/substagent.c index b0cc985..125a717 100644 --- a/src/substagent.c +++ b/src/substagent.c @@ -7,6 +7,7 @@ #include #include #include "build_info.h" +#include "fdi_hook.h" #include "fis_hook.h" #include "utils.h" @@ -25,37 +26,49 @@ static void JNICALL onNativeMethodBind(jvmtiEnv* jvmti_env, JNIEnv* env, jthread if (strcmp(csig, "Ljava/io/FileInputStream;") == 0) { // private native void open0(String name) if (strcmp(mname, "open0") == 0 && strcmp(msig, "(Ljava/lang/String;)V") == 0) { - fis_real_open0 = (open0_t)address; + fis_real_open0 = (fis_open0_t)address; *new_address_ptr = (void*)&fis_open0_hook; // private native int readBytes(byte b[], int off, int len) } else if (strcmp(mname, "readBytes") == 0 && strcmp(msig, "([BII)I") == 0) { - fis_real_readBytes = (readBytes_t)address; + fis_real_readBytes = (fis_readBytes_t)address; *new_address_ptr = (void*)&fis_readBytes_hook; // private native int read0 } else if (strcmp(mname, "read0") == 0 && strcmp(msig, "()I") == 0) { - fis_real_read0 = (read0_t)address; + fis_real_read0 = (fis_read0_t)address; *new_address_ptr = (void*)&fis_read0_hook; // private native long length0() } else if (strcmp(mname, "length0") == 0 && strcmp(msig, "()J") == 0) { - fis_real_length0 = (length0_t)address; + fis_real_length0 = (fis_length0_t)address; *new_address_ptr = (void*)&fis_length0_hook; // private native long position0() } else if (strcmp(mname, "position0") == 0 && strcmp(msig, "()J") == 0) { - fis_real_position0 = (position0_t)address; + fis_real_position0 = (fis_position0_t)address; *new_address_ptr = (void*)&fis_position0_hook; // private native long skip0(long n) } else if (strcmp(mname, "skip0") == 0 && strcmp(msig, "(J)J") == 0) { - fis_real_skip0 = (skip0_t)address; + fis_real_skip0 = (fis_skip0_t)address; *new_address_ptr = (void*)&fis_skip0_hook; // private native int available0() } else if (strcmp(mname, "available0") == 0 && strcmp(msig, "()I") == 0) { - fis_real_available0 = (available0_t)address; + fis_real_available0 = (fis_available0_t)address; *new_address_ptr = (void*)&fis_available0_hook; // public void close() } else if (strcmp(mname, "close") == 0 && strcmp(msig, "()V") == 0) { - fis_real_close = (close_t)address; + fis_real_close = (fis_close_t)address; *new_address_ptr = (void*)&fis_close_hook; } + } else if (strcmp(csig, "Lsun/nio/fs/UnixNativeDispatcher;") == 0) { + // private static native int open0(long pathAddress, int flags, int mode) + if (strcmp(mname, "open0") == 0 && strcmp(msig, "(JII)I") == 0) { + und_real_open0 = (und_open0_t)address; + *new_address_ptr = (void*)&und_open0_hook; + } + } else if (strcmp(csig, "Lsun/nio/ch/FileDispatcherImpl;") == 0) { + // static native int read0(FileDescriptor fd, long address, int len) + if (strcmp(mname, "read0") == 0 && strcmp(msig, "(Ljava/io/FileDescriptor;JI)I") == 0) { + fdi_real_read0 = (fdi_read0_t)address; + *new_address_ptr = (void*)&fdi_read0_hook; + } } } diff --git a/src/utils.c b/src/utils.c index 7419c5c..191c5da 100644 --- a/src/utils.c +++ b/src/utils.c @@ -42,4 +42,57 @@ char* substitute(const char* value, int dollar_sign_matched_index, size_t i, siz assert(dollar_sign_matched_index+value_len+after_len == new_len); new_data[new_len] = '\0'; return new_data; +} + +void parse_file(char **data_ptr, size_t fsize) { + char *data = *data_ptr; + int dollar_sign_matched_index = -1; + char env_var[4096]; + env_var[0] = '\0'; + for (size_t i = 0; i < fsize; i++) { + char c = data[i]; + if (dollar_sign_matched_index != -1) { + if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c == 95) { + size_t len = strlen(env_var); + env_var[len] = c; + env_var[len + 1] = '\0'; + } else { + if (strlen(env_var) == 0) continue; + char* value = getenv(env_var); + if (!value) { + dollar_sign_matched_index = -1; + env_var[0] = '\0'; + continue; + } + + size_t env_len = i - dollar_sign_matched_index; + + char* new_data = substitute(value, dollar_sign_matched_index, i, env_len, data); + free(data); + data = new_data; + *data_ptr = new_data; + + dollar_sign_matched_index = -1; + env_var[0] = '\0'; + continue; + } + } else { + if (c == '$') { + dollar_sign_matched_index = i; + } + } + } + if (dollar_sign_matched_index != -1) { + if (strlen(env_var) != 0) { + char* value = getenv(env_var); + if (value) { + size_t env_len = fsize - dollar_sign_matched_index; + + char* new_data = substitute(value, dollar_sign_matched_index, fsize, env_len, data); + free(data); + data = new_data; + *data_ptr = new_data; + } + } + } } \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index e6e50be..7f2a3ba 100644 --- a/src/utils.h +++ b/src/utils.h @@ -10,6 +10,7 @@ void tag(jobject obj); int has_tag(jobject obj); int is_config_path(const char* path); char* substitute(const char* value, int dollar_sign_matched_index, size_t i, size_t env_len, const char *data); +void parse_file(char **data_ptr, size_t fsize); struct file_data { char* data;