feat(experiments): disable composer modules

This commit is contained in:
rhunk
2023-11-26 17:47:54 +01:00
parent 284417b87d
commit a6d5d289a4
9 changed files with 62 additions and 3 deletions

View File

@@ -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"
}
}
},

View File

@@ -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() }
}

View File

@@ -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()
)
)
}

View File

@@ -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")
}
}
}
}

View File

@@ -109,6 +109,7 @@ class FeatureManager(
HalfSwipeNotifier::class,
DisableConfirmationDialogs::class,
Stories::class,
DisableComposerModules::class,
)
initializeFeatures()

View File

@@ -3,4 +3,5 @@
typedef struct {
bool disable_bitmoji;
bool disable_metrics;
bool hook_asset_open;
} native_config_t;

View File

@@ -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 *_) {

View File

@@ -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,
)

View File

@@ -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)