feat: experimental native hooks

This commit is contained in:
rhunk 2023-08-25 19:44:42 +02:00
parent b004f92b9c
commit c0225919e9
17 changed files with 185 additions and 2 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "dobby"]
path = native/jni/external/dobby
url = https://github.com/jmpews/Dobby

View File

@ -42,4 +42,5 @@ dependencies {
implementation(libs.androidx.documentfile)
implementation(project(":mapper"))
implementation(project(":native"))
}

View File

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

View File

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

View File

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

View File

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

View File

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

1
native/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

28
native/build.gradle.kts Normal file
View File

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

16
native/jni/CMakeLists.txt Normal file
View File

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

1
native/jni/external/dobby vendored Submodule

@ -0,0 +1 @@
Subproject commit b0176de574104726bb68dff3b77ee666300fc338

6
native/jni/src/config.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
typedef struct {
bool disable_bitmoji;
bool disable_metrics;
} native_config_t;

View File

@ -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;
}

10
native/jni/src/logger.h Normal file
View File

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

View File

@ -0,0 +1,6 @@
package me.rhunk.snapenhance.nativelib
data class NativeConfig(
val disableBitmoji: Boolean = false,
val disableMetrics: Boolean = false
)

View File

@ -0,0 +1,10 @@
package me.rhunk.snapenhance.nativelib
class NativeLib {
fun init() {
System.loadLibrary("nativelib")
}
external fun loadConfig(config: NativeConfig)
}

View File

@ -20,3 +20,4 @@ rootProject.name = "SnapEnhance"
include(":core")
include(":app")
include(":mapper")
include(":native")