Add support for FileDispatcherImpl/UnixNativeDispatcher

This commit is contained in:
SKBotNL committed 2026-01-20 16:13:37 +01:00
1 parent 312a76344b
commit 1000e02d49
8 files changed
+216 -98

No files matched your search

+1 -1
View File
@@ -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)
+80
View File
@@ -0,0 +1,80 @@
#include "fdi_hook.h"
#include "stb_ds.h"
#include "utils.h"
#include <stdint.h>
#include <string.h>
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;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef FDI_HOOK_H
#define FDI_HOOK_H
#include <jvmti.h>
#include <jni.h>
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
+27 -73
View File
@@ -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;
}
+16 -16
View File
@@ -4,22 +4,22 @@
#include <jvmti.h>
#include <jni.h>
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);
+21 -8
View File
@@ -7,6 +7,7 @@
#include <string.h>
#include <stdio.h>
#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;
}
}
}
+53
View File
@@ -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;
}
}
}
}
+1
View File
@@ -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;