commit fe9164b423cbaa4ff6af6cb3e9d89b4d83ed0b5e
parent 94b519614d0e00af0623be239a7ba4d3e5708643
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Thu, 18 Jan 2024 09:18:06 +0100

fix: native crash

Diffstat:
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt | 19+++++++++++++------
Mnative/jni/src/library.cpp | 14+++++---------
Mnative/jni/src/util.h | 74++++++++++++--------------------------------------------------------------
3 files changed, 30 insertions(+), 77 deletions(-)

diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/SnapEnhance.kt @@ -135,8 +135,8 @@ class SnapEnhance { reloadConfig() actionManager.init() initConfigListener() + initNative() scope.launch(Dispatchers.IO) { - initNative() translation.userLocale = getConfigLocale() translation.loadFromCallback { locale -> bridgeClient.fetchLocales(locale) @@ -170,16 +170,23 @@ class SnapEnhance { private fun initNative() { // don't initialize native when not logged in if (!appContext.database.hasArroyo()) return - appContext.native.apply { - if (appContext.config.experimental.nativeHooks.globalState != true) return@apply - initOnce(appContext.androidContext.classLoader) - nativeUnaryCallCallback = { request -> + if (appContext.config.experimental.nativeHooks.globalState != true) return + + lateinit var unhook: () -> Unit + Runtime::class.java.declaredMethods.first { + it.name == "loadLibrary0" && it.parameterTypes.contentEquals(arrayOf(ClassLoader::class.java, Class::class.java, String::class.java)) + }.hook(HookStage.AFTER) { param -> + val libName = param.arg<String>(2) + if (libName != "client") return@hook + unhook() + appContext.native.initOnce(appContext.androidContext.classLoader) + appContext.native.nativeUnaryCallCallback = { request -> appContext.event.post(NativeUnaryCallEvent(request.uri, request.buffer)) { request.buffer = buffer request.canceled = canceled } } - } + }.also { unhook = { it.unhook() } } } private fun initConfigListener() { diff --git a/native/jni/src/library.cpp b/native/jni/src/library.cpp @@ -71,11 +71,10 @@ static void *unaryCall_hook(void *unk1, const char *uri, grpc::grpc_byte_buffer return nullptr; } - auto new_buffer = env->GetObjectField(native_request_data_object, env->GetFieldID(native_request_data_class, "buffer", "[B")); - auto new_buffer_length = env->GetArrayLength((jbyteArray)new_buffer); - auto new_buffer_data = env->GetByteArrayElements((jbyteArray)new_buffer, nullptr); + auto new_buffer = (jbyteArray)env->GetObjectField(native_request_data_object, env->GetFieldID(native_request_data_class, "buffer", "[B")); + auto new_buffer_length = env->GetArrayLength(new_buffer); + auto new_buffer_data = env->GetByteArrayElements(new_buffer, nullptr); - LOGD("rewrote request for %s (length: %d)", uri, new_buffer_length); //we need to allocate a new ref_counter struct and copy the old ref_counter and the new_buffer to it const static auto ref_counter_struct_size = (uintptr_t)slice_buffer->data - (uintptr_t)slice_buffer->ref_counter; @@ -119,17 +118,13 @@ void JNICALL init(JNIEnv *env, jobject clazz, jobject classloader) { native_lib_on_unary_call_method = env->GetMethodID(env->GetObjectClass(clazz), "onNativeUnaryCall", "(Ljava/lang/String;[B)L" BUILD_NAMESPACE "/NativeRequestData;"); native_lib_on_asset_load = env->GetMethodID(env->GetObjectClass(clazz), "shouldLoadAsset", "(Ljava/lang/String;)Z"); - // load libclient.so - util::load_library(env, classloader, "client"); auto client_module = util::get_module("libclient.so"); if (client_module.base == 0) { - LOGE("libclient not found"); + LOGE("libclient not loaded!"); return; } - // client_module.base -= 0x1000; - // debugging purposes LOGD("libclient.so base=0x%0lx, size=0x%0lx", client_module.base, client_module.size); // hooks @@ -142,6 +137,7 @@ void JNICALL init(JNIEnv *env, jobject clazz, jobject classloader) { ); if (unaryCall_func != 0) { + LOGD("found unaryCall at 0x%0lx", unaryCall_func); DobbyHook((void *)unaryCall_func, (void *)unaryCall_hook, (void **)&unaryCall_original); } else { LOGE("can't find unaryCall signature"); diff --git a/native/jni/src/util.h b/native/jni/src/util.h @@ -8,25 +8,11 @@ namespace util { size_t size; } module_info_t; - static void hexDump(void *ptr, uint8_t line_length, uint32_t lines) { - auto *p = (unsigned char *) ptr; - for (uint8_t i = 0; i < lines; i++) { - std::string line; - for (uint8_t j = 0; j < line_length; j++) { - char buf[3]; - sprintf(buf, "%02x", p[i * line_length + j]); - line += buf; - line += " "; - } - LOGI("%s", line.c_str()); - } - } - static module_info_t get_module(const char *libname) { char buff[256]; int len_libname = strlen(libname); - uintptr_t addr = 0; - size_t size = 0; + uintptr_t start_offset = 0; + uintptr_t end_offset = 0; auto file = fopen("/proc/self/smaps", "rt"); if (file == NULL) @@ -47,25 +33,20 @@ namespace util { continue; } - if (addr == 0 && flags[0] == 'r' && flags[2] == 'x') { - addr = start - offset; + if (flags[0] != 'r' || flags[2] != 'x') { + continue; } - if (addr != 0) { - size += end - start; + + if (start_offset == 0) { + start_offset = start; } + end_offset = end; } fclose(file); - return {addr, size}; - } - - void load_library(JNIEnv *env, jobject classLoader, const char *libName) { - auto runtimeClass = env->FindClass("java/lang/Runtime"); - auto getRuntimeMethod = env->GetStaticMethodID(runtimeClass, "getRuntime", - "()Ljava/lang/Runtime;"); - auto runtime = env->CallStaticObjectMethod(runtimeClass, getRuntimeMethod); - auto loadLibraryMethod = env->GetMethodID(runtimeClass, "loadLibrary0", - "(Ljava/lang/ClassLoader;Ljava/lang/String;)V"); - env->CallVoidMethod(runtime, loadLibraryMethod, classLoader, env->NewStringUTF(libName)); + if (start_offset == 0) { + return {0, 0}; + } + return { start_offset, end_offset - start_offset }; } uintptr_t find_signature(uintptr_t module_base, uintptr_t size, const std::string &pattern, int offset = 0) { @@ -96,35 +77,4 @@ namespace util { } return 0; } - - std::vector<uintptr_t> find_signatures(uintptr_t module_base, uintptr_t size, const std::string &pattern, int offset = 0) { - std::vector<uintptr_t> results; - std::vector<char> bytes; - std::vector<char> mask; - - for (size_t i = 0; i < pattern.size(); i += 3) { - if (pattern[i] == '?') { - bytes.push_back(0); - mask.push_back('?'); - } else { - bytes.push_back(std::stoi(pattern.substr(i, 2), nullptr, 16)); - mask.push_back('x'); - } - } - - for (size_t i = 0; i < size; i++) { - bool found = true; - for (size_t j = 0; j < bytes.size(); j++) { - if (mask[j] == '?' || bytes[j] == *(char *) (module_base + i + j)) { - continue; - } - found = false; - break; - } - if (found) { - results.push_back(module_base + i + offset); - } - } - return results; - } } \ No newline at end of file