commit 33dc0c7510ec75bff340d3a921aa447d6f7fcb32
parent 2bb055f4642bdcc02c24117be6794ac73c420e90
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Mon, 22 Apr 2024 23:35:48 +0200

feat(composer_hooks): armv7 support

Diffstat:
Mcommon/src/main/assets/lang/en_US.json | 4++--
Mcore/src/main/assets/composer/loader.js | 6------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/ComposerHooks.kt | 29+++++++----------------------
Mnative/jni/src/hooks/composer_hook.h | 31+++++++++++++++++++++----------
Mnative/jni/src/library.cpp | 23+++++++----------------
Mnative/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt | 3++-
6 files changed, 39 insertions(+), 57 deletions(-)

diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json @@ -772,7 +772,7 @@ "properties": { "composer_hooks": { "name": "Composer Hooks", - "description": "Injects code into the Composer cross-platform UI framework (arm64 only)", + "description": "Injects code into the Composer cross-platform UI framework", "properties": { "bypass_camera_roll_limit": { "name": "Bypass Camera Roll Limit", @@ -780,7 +780,7 @@ }, "composer_console": { "name": "Composer Console", - "description": "Allows you to execute JavaScript code in Composer" + "description": "Allows you to execute JavaScript code in Composer (arm64 only)" }, "composer_logs": { "name": "Composer Logs", diff --git a/core/src/main/assets/composer/loader.js b/core/src/main/assets/composer/loader.js @@ -1,9 +1,3 @@ -const callExport = require('composer_core/src/DeviceBridge')[EXPORTED_FUNCTION_NAME]; - -if (!callExport) { - return "No export function found"; -} - const config = callExport("getConfig"); if (config.composerLogs) { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/ComposerHooks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/ComposerHooks.kt @@ -186,21 +186,16 @@ class ComposerHooks: Feature("ComposerHooks", loadParams = FeatureLoadParams.ACT return } - val hookResult = context.native.composerEval(""" - (() => { try { const EXPORTED_FUNCTION_NAME = "$exportedFunctionName"; $loaderScript + context.native.setComposerLoader(""" + (() => { const callExport = require('composer_core/src/DeviceBridge')["$exportedFunctionName"]; try { $loaderScript } catch (e) { - return e.toString() + "\n" + e.stack; + try { + callExport("log", "error", e.toString() + "\n" + e.stack); + } catch (t) {} } - return "success"; - })() + })(); """.trimIndent().trim()) - if (hookResult != "success") { - context.shortToast(("Composer loader failed : $hookResult").also { - context.log.error(it) - }) - } - if (config.composerConsole.get()) { injectConsole() } @@ -221,16 +216,6 @@ class ComposerHooks: Feature("ComposerHooks", loadParams = FeatureLoadParams.ACT } } - var composerThreadTask: (() -> Unit)? = null - - findClass("com.snap.composer.callable.ComposerFunctionNative").hook("nativePerform", HookStage.BEFORE) { - composerThreadTask?.invoke() - composerThreadTask = null - } - - context.coroutineScope.launch { - context.native.waitForComposer() - composerThreadTask = ::loadHooks - } + loadHooks() } } \ No newline at end of file diff --git a/native/jni/src/hooks/composer_hook.h b/native/jni/src/hooks/composer_hook.h @@ -93,20 +93,32 @@ namespace ComposerHook { static uintptr_t global_instance; static JSContext *global_ctx; + static std::string* composer_loader; - HOOK_DEF(JSValue, js_eval, uintptr_t instance, JSContext *ctx, uintptr_t this_obj, uint8_t *input, uintptr_t input_len, const char *filename, unsigned int flags, unsigned int scope_idx) { + HOOK_DEF(JSValue, js_eval, uintptr_t instance, JSContext *ctx, uintptr_t this_obj, char *input, uintptr_t input_len, const char *filename, unsigned int flags, unsigned int scope_idx) { if (global_instance == 0 || global_ctx == nullptr) { global_instance = instance; global_ctx = ctx; + LOGD("Injecting composer loader"); + + composer_loader->resize(composer_loader->size() + input_len); + memcpy((void*) (composer_loader->c_str() + composer_loader->size() - input_len), input, input_len); + + input = (char*) composer_loader->c_str(); + input_len = composer_loader->size(); + } else { + if (composer_loader != nullptr) { + delete composer_loader; + composer_loader = nullptr; + } } - return js_eval_original(instance, ctx, this_obj, input, input_len, filename, flags, scope_idx); } - void waitForComposer(JNIEnv *, jobject) { - while (global_instance == 0 || global_ctx == nullptr) { - usleep(10000); - } + void setComposerLoader(JNIEnv *env, jobject, jstring code) { + auto code_str = env->GetStringUTFChars(code, nullptr); + composer_loader = new std::string(code_str, env->GetStringUTFLength(code)); + env->ReleaseStringUTFChars(code, code_str); } jstring composerEval(JNIEnv *env, jobject, jstring script) { @@ -117,7 +129,7 @@ namespace ComposerHook { auto script_str = env->GetStringUTFChars(script, nullptr); auto length = env->GetStringUTFLength(script); - auto jsvalue = js_eval_original(global_instance, global_ctx, (uintptr_t) &global_ctx->global_obj, (uint8_t *) script_str, length, "<input>", 0, 0); + auto jsvalue = js_eval_original(global_instance, global_ctx, (uintptr_t) &global_ctx->global_obj, (char *) script_str, length, "<eval>", 0, 0); env->ReleaseStringUTFChars(script, script_str); if (jsvalue.tag == JS_TAG_STRING) { @@ -157,12 +169,11 @@ namespace ComposerHook { } void init() { - if (!ARM64) return; auto js_eval_ptr = util::find_signature( common::client_module.base, common::client_module.size, - "00 E4 00 6F 29 00 80 52 76 00 04 8B", - -0x28 + ARM64 ? "00 E4 00 6F 29 00 80 52 76 00 04 8B" : "A1 B0 07 92 81 46", + ARM64 ? -0x28 : -0x7 ); if (js_eval_ptr == 0) { LOGE("js_eval_ptr signature not found"); diff --git a/native/jni/src/library.cpp b/native/jni/src/library.cpp @@ -2,7 +2,6 @@ #include <string> #include <dobby.h> #include <vector> -#include <thread> #include "logger.h" #include "common.h" @@ -33,21 +32,13 @@ bool JNICALL init(JNIEnv *env, jobject clazz) { util::remap_sections(BUILD_PACKAGE); - auto threads = std::vector<std::thread>(); - - #define RUN(body) \ - threads.push_back(std::thread([&] { body; })) - - RUN(UnaryCallHook::init(env)); - RUN(AssetHook::init(env)); - RUN(FstatHook::init()); - RUN(SqliteMutexHook::init()); - RUN(DuplexHook::init(env)); + UnaryCallHook::init(env); + AssetHook::init(env); + FstatHook::init(); + SqliteMutexHook::init(); + DuplexHook::init(env); if (common::native_config->composer_hooks) { - RUN(ComposerHook::init()); - } - for (auto &thread : threads) { - thread.join(); + ComposerHook::init(); } LOGD("Native initialized"); @@ -94,7 +85,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *_) { methods.push_back({"init", "()Z", (void *)init}); methods.push_back({"loadConfig", "(L" BUILD_NAMESPACE "/NativeConfig;)V", (void *)load_config}); methods.push_back({"lockDatabase", "(Ljava/lang/String;Ljava/lang/Runnable;)V", (void *)lock_database}); - methods.push_back({"waitForComposer", "()V", (void *) ComposerHook::waitForComposer}); + methods.push_back({"setComposerLoader", "(Ljava/lang/String;)V", (void *) ComposerHook::setComposerLoader}); methods.push_back({"composerEval", "(Ljava/lang/String;)Ljava/lang/String;",(void *) ComposerHook::composerEval}); env->RegisterNatives(env->FindClass(std::string(BUILD_NAMESPACE "/NativeLib").c_str()), methods.data(), methods.size()); diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt @@ -2,6 +2,7 @@ package me.rhunk.snapenhance.nativelib import android.util.Log +@Suppress("KotlinJniMissingFunction") class NativeLib { var nativeUnaryCallCallback: (NativeRequestData) -> Unit = {} var nativeShouldLoadAsset: (String) -> Boolean = { true } @@ -62,6 +63,6 @@ class NativeLib { private external fun init(): Boolean private external fun loadConfig(config: NativeConfig) private external fun lockDatabase(name: String, callback: Runnable) - external fun waitForComposer() + external fun setComposerLoader(code: String) external fun composerEval(code: String): String? } \ No newline at end of file