commit a6d5d289a42a3f9c8b618f85613e549c4d17445b parent 284417b87da057a05f51878ad4c12fbd3966e673 Author: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:47:54 +0100 feat(experiments): disable composer modules Diffstat:
9 files changed, 63 insertions(+), 3 deletions(-)
diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json @@ -622,6 +622,10 @@ "hidden_snapchat_plus_features": { "name": "Hidden Snapchat Plus Features", "description": "Enables unreleased/beta Snapchat Plus features\nMight not work on older Snapchat versions" + }, + "disable_composer_modules": { + "name": "Disable Composer Modules", + "description": "Prevents selected composer modules from being loaded\nNames must be separated by a comma" } } }, diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt @@ -30,4 +30,5 @@ class Experimental : ConfigContainer() { "added_by_qr_code", "added_by_community", ) { addNotices(FeatureNotice.BAN_RISK) } + val disableComposerModules = string("disable_composer_modules") { requireRestart(); nativeHooks() } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/ModContext.kt @@ -149,7 +149,8 @@ class ModContext( native.loadNativeConfig( NativeConfig( disableBitmoji = config.experimental.nativeHooks.disableBitmoji.get(), - disableMetrics = config.global.disableMetrics.get() + disableMetrics = config.global.disableMetrics.get(), + hookAssetOpen = config.experimental.disableComposerModules.get().isNotEmpty() ) ) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/DisableComposerModules.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/DisableComposerModules.kt @@ -0,0 +1,22 @@ +package me.rhunk.snapenhance.core.features.impl.experiments + +import me.rhunk.snapenhance.core.features.Feature +import me.rhunk.snapenhance.core.features.FeatureLoadParams + +class DisableComposerModules : Feature("Disable Composer Modules", FeatureLoadParams.INIT_SYNC) { + override fun init() { + val disabledComposerModules = context.config.experimental.disableComposerModules.get().takeIf { it.isNotEmpty() } + ?.replace(" ", "") + ?.split(",") + ?: return + + context.native.nativeShouldLoadAsset = callback@{ assetName -> + if (!assetName.endsWith(".composermodule")) return@callback true + val moduleName = assetName.replace(".composermodule", "") + disabledComposerModules.contains(moduleName).not().also { + if (it) context.log.debug("Loading $moduleName composer module") + else context.log.warn("Skipping $moduleName composer module") + } + } + } +}+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/manager/impl/FeatureManager.kt @@ -109,6 +109,7 @@ class FeatureManager( HalfSwipeNotifier::class, DisableConfirmationDialogs::class, Stories::class, + DisableComposerModules::class, ) initializeFeatures() diff --git a/native/jni/src/config.h b/native/jni/src/config.h @@ -3,4 +3,5 @@ typedef struct { bool disable_bitmoji; bool disable_metrics; + bool hook_asset_open; } native_config_t; \ No newline at end of file diff --git a/native/jni/src/library.cpp b/native/jni/src/library.cpp @@ -2,6 +2,7 @@ #include <string> #include <dobby.h> #include <vector> +#include <android/asset_manager.h> #include "logger.h" #include "config.h" @@ -16,9 +17,14 @@ static native_config_t *native_config; static JavaVM *java_vm; +static jobject native_lib_object; static jmethodID native_lib_on_unary_call_method; +static jmethodID native_lib_on_asset_load; + +// original functions static void *(*unaryCall_original)(void *, const char *, grpc::grpc_byte_buffer **, void *, void *, void *); static auto fstat_original = (int (*)(int, struct stat *)) nullptr; +static AAsset* (*AAssetManager_open_original)(AAssetManager*, const char*, int) = nullptr; static int fstat_hook(int fd, struct stat *buf) { char name[256]; @@ -40,7 +46,6 @@ static int fstat_hook(int fd, struct stat *buf) { return fstat_original(fd, buf); } -static jobject native_lib_object; static void *unaryCall_hook(void *unk1, const char *uri, grpc::grpc_byte_buffer **buffer_ptr, void *unk4, void *unk5, void *unk6) { // request without reference counter can be hooked using xposed ig @@ -91,6 +96,19 @@ static void *unaryCall_hook(void *unk1, const char *uri, grpc::grpc_byte_buffer return unaryCall_original(unk1, uri, buffer_ptr, unk4, unk5, unk6); } +static AAsset* AAssetManager_open_hook(AAssetManager* mgr, const char* filename, int mode) { + if (native_config->hook_asset_open) { + JNIEnv *env = nullptr; + java_vm->GetEnv((void **)&env, JNI_VERSION_1_6); + + if (!env->CallBooleanMethod(native_lib_object, native_lib_on_asset_load, env->NewStringUTF(filename))) { + return nullptr; + } + } + + return AAssetManager_open_original(mgr, filename, mode); +} + void JNICALL init(JNIEnv *env, jobject clazz, jobject classloader) { LOGD("Initializing native"); // config @@ -99,6 +117,7 @@ void JNICALL init(JNIEnv *env, jobject clazz, jobject classloader) { // native lib object native_lib_object = env->NewGlobalRef(clazz); 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"); @@ -128,6 +147,7 @@ void JNICALL init(JNIEnv *env, jobject clazz, jobject classloader) { LOGE("can't find unaryCall signature"); } + DobbyHook((void *) AAssetManager_open, (void *) AAssetManager_open_hook, (void **) &AAssetManager_open_original); LOGD("Native initialized"); } @@ -137,6 +157,7 @@ void JNICALL load_config(JNIEnv *env, jobject _, jobject config_object) { native_config->disable_bitmoji = GET_CONFIG_BOOL("disableBitmoji"); native_config->disable_metrics = GET_CONFIG_BOOL("disableMetrics"); + native_config->hook_asset_open = GET_CONFIG_BOOL("hookAssetOpen"); } extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *_) { diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeConfig.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeConfig.kt @@ -2,5 +2,6 @@ package me.rhunk.snapenhance.nativelib data class NativeConfig( val disableBitmoji: Boolean = false, - val disableMetrics: Boolean = false + val disableMetrics: Boolean = false, + val hookAssetOpen: Boolean = false, ) \ No newline at end of file diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt @@ -4,6 +4,8 @@ import android.util.Log class NativeLib { var nativeUnaryCallCallback: (NativeRequestData) -> Unit = {} + var nativeShouldLoadAsset: (String) -> Boolean = { true } + companion object { var initialized = false private set @@ -33,6 +35,11 @@ class NativeLib { return null } + @Suppress("unused") + private fun shouldLoadAsset(name: String) = runCatching { + nativeShouldLoadAsset(name) + }.getOrNull() ?: true + fun loadNativeConfig(config: NativeConfig) { if (!initialized) return loadConfig(config)