commit c0225919e977a3e29a46737b348babc3bfeae5b8
parent b004f92b9cd9751fa0f22403b8b07ae426cd58cd
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Fri, 25 Aug 2023 19:44:42 +0200
feat: experimental native hooks
Diffstat:
17 files changed, 188 insertions(+), 3 deletions(-)
diff --git a/.gitmodules b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "dobby"]
+ path = native/jni/external/dobby
+ url = https://github.com/jmpews/Dobby
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
@@ -42,4 +42,5 @@ dependencies {
implementation(libs.androidx.documentfile)
implementation(project(":mapper"))
+ implementation(project(":native"))
}
\ No newline at end of file
diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json
@@ -249,6 +249,10 @@
"name": "Disable Metrics",
"description": "Disables some analytics data sent to Snapchat"
},
+ "disable_bitmoji": {
+ "name": "Disable Bitmoji",
+ "description": "Disables friends profile bitmoji for the whole app"
+ },
"block_ads": {
"name": "Block Ads",
"description": "Prevent ads from being displayed"
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt
@@ -21,6 +21,8 @@ import me.rhunk.snapenhance.database.DatabaseAccess
import me.rhunk.snapenhance.features.Feature
import me.rhunk.snapenhance.manager.impl.ActionManager
import me.rhunk.snapenhance.manager.impl.FeatureManager
+import me.rhunk.snapenhance.nativelib.NativeConfig
+import me.rhunk.snapenhance.nativelib.NativeLib
import me.rhunk.snapenhance.util.download.HttpServer
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
@@ -44,6 +46,7 @@ class ModContext {
val config by modConfig
val event = EventBus(this)
val eventDispatcher = EventDispatcher(this)
+ val native = NativeLib()
val translation = LocaleWrapper()
val features = FeatureManager(this)
@@ -121,6 +124,16 @@ class ModContext {
fun reloadConfig() {
modConfig.loadFromBridge(bridgeClient)
+ runCatching {
+ native.loadConfig(
+ NativeConfig(
+ disableBitmoji = config.global.disableBitmoji.get(),
+ disableMetrics = config.global.disableMetrics.get()
+ )
+ )
+ }.onFailure {
+ Logger.xposedLog("Failed to load native config", it)
+ }
}
fun getConfigLocale(): String {
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt
@@ -97,6 +97,12 @@ class SnapEnhance {
private suspend fun init() {
measureTime {
with(appContext) {
+ runCatching {
+ native.init()
+ }.onFailure {
+ Logger.xposedLog("Failed to init native", it)
+ return
+ }
reloadConfig()
withContext(appContext.coroutineDispatcher) {
translation.userLocale = getConfigLocale()
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt
@@ -8,6 +8,7 @@ class Global : ConfigContainer() {
val snapchatPlus = boolean("snapchat_plus") { addNotices(FeatureNotice.MAY_BAN) }
val autoUpdater = unique("auto_updater", "EVERY_LAUNCH", "DAILY", "WEEKLY").apply { set("DAILY") }
val disableMetrics = boolean("disable_metrics")
+ val disableBitmoji = boolean("disable_bitmoji") { addNotices(FeatureNotice.UNSTABLE) }
val blockAds = boolean("block_ads")
val disableVideoLengthRestrictions = boolean("disable_video_length_restrictions") { addNotices(FeatureNotice.MAY_BAN) }
val disableGooglePlayDialogs = boolean("disable_google_play_dialogs")
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt
@@ -56,7 +56,7 @@ class EventBus(
event.context = context
- subscribers[event::class]?.forEach { listener ->
+ subscribers[event::class]?.toTypedArray()?.forEach { listener ->
@Suppress("UNCHECKED_CAST")
runCatching {
(listener as IListener<T>).handle(event)
diff --git a/native/.gitignore b/native/.gitignore
@@ -0,0 +1 @@
+/build+
\ No newline at end of file
diff --git a/native/build.gradle.kts b/native/build.gradle.kts
@@ -0,0 +1,28 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+ alias(libs.plugins.androidLibrary)
+ alias(libs.plugins.kotlinAndroid)
+}
+
+android {
+ namespace = "me.rhunk.snapenhance.nativelib"
+ compileSdk = 34
+
+ defaultConfig {
+ externalNativeBuild {
+ cmake {
+ cppFlags("")
+ }
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path("jni/CMakeLists.txt")
+ version = "3.22.1"
+ }
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
diff --git a/native/jni/CMakeLists.txt b/native/jni/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.22.1)
+
+project("nativelib")
+
+set(DOBBY_GENERATE_SHARED OFF)
+add_subdirectory(external/dobby)
+
+add_library(${CMAKE_PROJECT_NAME} SHARED
+ src/library.cpp
+ )
+
+target_link_libraries(${CMAKE_PROJECT_NAME}
+ android
+ log
+ dobby_static
+ )
diff --git a/native/jni/external/dobby b/native/jni/external/dobby
@@ -0,0 +1 @@
+Subproject commit b0176de574104726bb68dff3b77ee666300fc338
diff --git a/native/jni/src/config.h b/native/jni/src/config.h
@@ -0,0 +1,6 @@
+#pragma once
+
+typedef struct {
+ bool disable_bitmoji;
+ bool disable_metrics;
+} native_config_t;+
\ No newline at end of file
diff --git a/native/jni/src/library.cpp b/native/jni/src/library.cpp
@@ -0,0 +1,76 @@
+#include <jni.h>
+#include <string>
+#include <dobby.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fstream>
+#include <vector>
+
+#include "logger.h"
+#include "config.h"
+
+static native_config_t *native_config;
+
+static auto fstat_original = fstat;
+static int fstat_hook(int fd, struct stat *buf) {
+ char name[256];
+ memset(name, 0, 256);
+ snprintf(name, sizeof(name), "/proc/self/fd/%d", fd);
+ readlink(name, name, sizeof(name));
+
+ auto fileName = std::string(name);
+
+ //prevent blizzardv2 metrics
+ if (native_config->disable_metrics && fileName.find("files/blizzardv2/queues") != std::string::npos) {
+ unlink(name);
+ return -1;
+ }
+
+ //prevent bitmoji to load
+ if (native_config->disable_bitmoji && fileName.find("com.snap.file_manager_4_SCContent") != std::string::npos) {
+ return -1;
+ }
+
+ return fstat_original(fd, buf);
+}
+
+
+#define GET_BOOL_FIELD(env, clazz, field) env->GetBooleanField(clazz, env->GetFieldID(clazz, field, "Z"))
+
+extern "C" JNIEXPORT void JNICALL
+loadConfig(JNIEnv *env, jobject clazz, jobject config_object) {
+ auto native_config_class = env->GetObjectClass(config_object);
+
+ native_config->disable_bitmoji = GET_BOOL_FIELD(env, native_config_class, "disableBitmoji");
+ native_config->disable_metrics = GET_BOOL_FIELD(env, native_config_class, "disableMetrics");
+
+ LOGD("config loaded");
+}
+
+//jni onload
+extern "C" JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM *vm, void *reserved) {
+ LOGD("initializing native");
+ // config
+ native_config = new native_config_t;
+
+ // hooks
+ DobbyHook((void *) fstat_original,(void *) fstat_hook,(void **) &fstat_original);
+
+ // register native methods
+ JNIEnv *env = nullptr;
+ vm->GetEnv((void **) &env, JNI_VERSION_1_6);
+
+ auto methods = std::vector<JNINativeMethod>();
+ methods.push_back({"loadConfig", "(Lme/rhunk/snapenhance/nativelib/NativeConfig;)V", (void *) loadConfig});
+
+ env->RegisterNatives(
+ env->FindClass("me/rhunk/snapenhance/nativelib/NativeLib"),
+ methods.data(),
+ methods.size()
+ );
+
+ LOGD("native initialized");
+
+ return JNI_VERSION_1_6;
+}
diff --git a/native/jni/src/logger.h b/native/jni/src/logger.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <android/log.h>
+
+#define LOG_TAG "SnapEnhanceNative"
+
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+
diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeConfig.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeConfig.kt
@@ -0,0 +1,6 @@
+package me.rhunk.snapenhance.nativelib
+
+data class NativeConfig(
+ val disableBitmoji: Boolean = false,
+ val disableMetrics: Boolean = false
+)
diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt
@@ -0,0 +1,10 @@
+package me.rhunk.snapenhance.nativelib
+
+class NativeLib {
+ fun init() {
+ System.loadLibrary("nativelib")
+ }
+
+
+ external fun loadConfig(config: NativeConfig)
+}+
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
@@ -19,4 +19,5 @@ dependencyResolutionManagement {
rootProject.name = "SnapEnhance"
include(":core")
include(":app")
-include(":mapper")-
\ No newline at end of file
+include(":mapper")
+include(":native")