commit 514c5f186a616f8896824c38211e444aa6dae892
parent 9b7ff403025ffafe6682c3641f9ea86ea261c10c
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Mon, 28 Aug 2023 03:57:35 +0200

feat: save overridden snaps in chat

Diffstat:
Mcore/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt | 19+++++++++++++------
Acore/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/UnaryCallEvent.kt | 9+++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt | 21+++++++++++++++++++++
Mcore/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt | 1+
Mnative/jni/src/library.cpp | 45++++++++++++++++++++++-----------------------
Mnative/jni/src/util.h | 11+++--------
Mnative/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt | 2+-
7 files changed, 70 insertions(+), 38 deletions(-)

diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -10,6 +10,7 @@ import me.rhunk.snapenhance.bridge.BridgeClient import me.rhunk.snapenhance.bridge.SyncCallback import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.core.eventbus.events.impl.SnapWidgetBroadcastReceiveEvent +import me.rhunk.snapenhance.core.eventbus.events.impl.UnaryCallEvent import me.rhunk.snapenhance.core.messaging.MessagingFriendInfo import me.rhunk.snapenhance.core.messaging.MessagingGroupInfo import me.rhunk.snapenhance.data.SnapClassCache @@ -97,12 +98,6 @@ class SnapEnhance { private suspend fun init() { measureTime { with(appContext) { - runCatching { - native.initOnce(appContext.androidContext.classLoader) - }.onFailure { - Logger.xposedLog("Failed to init native", it) - return - } reloadConfig() withContext(appContext.coroutineDispatcher) { translation.userLocale = getConfigLocale() @@ -126,6 +121,18 @@ class SnapEnhance { private fun onActivityCreate() { measureTime { with(appContext) { + runCatching { + native.initOnce(appContext.androidContext.classLoader) + native.nativeUnaryCallCallback = { request -> + event.post(UnaryCallEvent(request.uri, request.buffer))?.also { + request.buffer = it.buffer + request.canceled = it.canceled + } + } + }.onFailure { + Logger.xposedLog("Failed to init native", it) + } + features.onActivityCreate() actionManager.init() } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/UnaryCallEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/UnaryCallEvent.kt @@ -0,0 +1,8 @@ +package me.rhunk.snapenhance.core.eventbus.events.impl + +import me.rhunk.snapenhance.core.eventbus.events.AbstractHookEvent + +class UnaryCallEvent( + val uri: String, + var buffer: ByteArray +) : AbstractHookEvent()+ \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt @@ -1,11 +1,13 @@ package me.rhunk.snapenhance.features.impl.tweaks import me.rhunk.snapenhance.core.eventbus.events.impl.SendMessageWithContentEvent +import me.rhunk.snapenhance.core.eventbus.events.impl.UnaryCallEvent import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.ui.ViewAppearanceHelper +import me.rhunk.snapenhance.util.protobuf.ProtoEditor import me.rhunk.snapenhance.util.protobuf.ProtoReader class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadParams = FeatureLoadParams.INIT_SYNC) { @@ -19,6 +21,25 @@ class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadPara it } + context.event.subscribe(UnaryCallEvent::class) { event -> + if (event.uri != "/messagingcoreservice.MessagingCoreService/CreateContentMessage") return@subscribe + event.buffer = ProtoEditor(event.buffer).apply { + //remove the mas view time + edit(4, 4, 11, 5, 2) { + remove(8) + addBuffer(6, byteArrayOf()) + } + //make snaps savable in chat + edit(4) { + val savableState = firstOrNull(7)?.value ?: return@edit + if (savableState == 2L) { + remove(7) + addVarInt(7, 3) + } + } + }.toByteArray() + } + context.event.subscribe(SendMessageWithContentEvent::class, { context.config.messaging.galleryMediaSendOverride.get() }) { event -> diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt @@ -15,6 +15,7 @@ class EditorContext( fun addVarInt(id: Int, value: Int) = addVarInt(id, value.toLong()) fun addVarInt(id: Int, value: Long) = addWire(Wire(id, WireType.VARINT, value)) fun addBuffer(id: Int, value: ByteArray) = addWire(Wire(id, WireType.LENGTH_DELIMITED, value)) + fun add(id: Int, content: ProtoWriter.() -> Unit) = addBuffer(id, ProtoWriter().apply(content).toByteArray()) fun addString(id: Int, value: String) = addBuffer(id, value.toByteArray()) fun addFixed64(id: Int, value: Long) = addWire(Wire(id, WireType.FIXED64, value)) fun addFixed32(id: Int, value: Int) = addWire(Wire(id, WireType.FIXED32, value)) diff --git a/native/jni/src/library.cpp b/native/jni/src/library.cpp @@ -1,7 +1,6 @@ #include <jni.h> #include <string> #include <dobby.h> -#include <unistd.h> #include <vector> #include "logger.h" @@ -13,6 +12,7 @@ static native_config_t *native_config; static JavaVM *java_vm; static auto fstat_original = (int (*)(int, struct stat *)) nullptr; + static int fstat_hook(int fd, struct stat *buf) { char name[256]; memset(name, 0, 256); @@ -49,7 +49,7 @@ unaryCall_hook(void *unk1, const char *uri, grpc::grpc_byte_buffer **buffer_ptr, void *unk5, void *unk6) { auto slice_buffer = (*buffer_ptr)->slice_buffer; // request without reference counter can be hooked using xposed ig - if (slice_buffer->ref_counter == nullptr) { + if (slice_buffer->ref_counter == 0) { return unaryCall_original(unk1, uri, buffer_ptr, unk4, unk5, unk6); } @@ -70,8 +70,8 @@ unaryCall_hook(void *unk1, const char *uri, grpc::grpc_byte_buffer **buffer_ptr, auto is_canceled = env->GetBooleanField(native_request_data_object, env->GetFieldID(native_request_data_class, "canceled", "Z")); - if (is_canceled) { + LOGD("canceled request for %s", uri); return nullptr; } @@ -81,34 +81,32 @@ unaryCall_hook(void *unk1, const char *uri, grpc::grpc_byte_buffer **buffer_ptr, auto new_buffer_length = env->GetArrayLength((jbyteArray) new_buffer); auto new_buffer_data = env->GetByteArrayElements((jbyteArray) new_buffer, nullptr); + LOGD("rewrote request for %s (length: %d)", uri, new_buffer_length); //we need to allocate a new ref_counter struct and copy the old ref_counter and the new_buffer to it - const auto ref_counter_struct_size = + const static auto ref_counter_struct_size = (uintptr_t) slice_buffer->data - (uintptr_t) slice_buffer->ref_counter; - auto new_ref_counter = (void *) malloc(ref_counter_struct_size + new_buffer_length); + auto new_ref_counter = malloc(ref_counter_struct_size + new_buffer_length); //copy the old ref_counter and the native_request_data_object memcpy(new_ref_counter, slice_buffer->ref_counter, ref_counter_struct_size); memcpy((void *) ((uintptr_t) new_ref_counter + ref_counter_struct_size), new_buffer_data, new_buffer_length); //free the old ref_counter - free((void *) slice_buffer->ref_counter); + free(slice_buffer->ref_counter); //update the slice_buffer slice_buffer->ref_counter = new_ref_counter; slice_buffer->length = new_buffer_length; slice_buffer->data = (uint8_t *) ((uintptr_t) new_ref_counter + ref_counter_struct_size); - - env->ReleaseByteArrayElements((jbyteArray) native_request_data_object, new_buffer_data, 0); } return unaryCall_original(unk1, uri, buffer_ptr, unk4, unk5, unk6); } -extern "C" JNIEXPORT void JNICALL -init(JNIEnv *env, jobject clazz, jobject classloader) { - LOGD("initializing native"); +void JNICALL init(JNIEnv *env, jobject clazz, jobject classloader) { + LOGD("Initializing native"); // config native_config = new native_config_t; @@ -125,34 +123,35 @@ init(JNIEnv *env, jobject clazz, jobject classloader) { LOGE("libclient not found"); return; } - client_module.base -= 0x1000; - LOGD("libclient: offset: 0x%x size: 0x%x", client_module.base, client_module.size); + //client_module.base -= 0x1000; // debugging purposes + LOGD("libclient.so offset=%u, size=%u", client_module.base, client_module.size); // hooks DobbyHook((void *) DobbySymbolResolver("libc.so", "fstat"), (void *) fstat_hook, (void **) &fstat_original); + //signature might change in the future (unstable for now) - DobbyHook((void *) util::find_signature(client_module.base, client_module.size, - "FD 7B BA A9 FC 6F 01 A9 FA 67 02 A9 F8 5F 03 A9 F6 57 04 A9 F4 4F 05 A9 FD 03 00 91 FF 43 13 D1"), - (void *) unaryCall_hook, - (void **) &unaryCall_original); + auto unaryCall_func = util::find_signature(client_module.base, client_module.size, + "FD 7B BA A9 FC 6F 01 A9 FA 67 02 A9 F8 5F 03 A9 F6 57 04 A9 F4 4F 05 A9 FD 03 00 91 FF 43 13 D1"); + if (unaryCall_func != 0) { + DobbyHook((void *) unaryCall_func, (void *) unaryCall_hook, (void **) &unaryCall_original); + } else { + LOGE("can't find unaryCall signature"); + } - LOGD("native initialized"); + LOGD("Native initialized"); } -void JNICALL load_config(JNIEnv *env, jobject clazz, jobject config_object) { +void JNICALL load_config(JNIEnv *env, jobject _, jobject config_object) { auto native_config_clazz = env->GetObjectClass(config_object); #define GET_CONFIG_BOOL(name) env->GetBooleanField(config_object, env->GetFieldID(native_config_clazz, name, "Z")) native_config->disable_bitmoji = GET_CONFIG_BOOL("disableBitmoji"); native_config->disable_metrics = GET_CONFIG_BOOL("disableMetrics"); - - LOGD("config loaded"); } -//jni onload extern "C" JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved) { +JNI_OnLoad(JavaVM *vm, void *_) { java_vm = vm; // register native methods JNIEnv *env = nullptr; diff --git a/native/jni/src/util.h b/native/jni/src/util.h @@ -1,14 +1,12 @@ #pragma once #include <unistd.h> -#include <dlfcn.h> -#include <elf.h> namespace util { typedef struct { uintptr_t base; size_t size; - } ModuleInfo; + } module_info_t; static void hexDump(void *ptr, uint8_t line_length, uint32_t lines) { auto *p = (unsigned char *) ptr; @@ -24,16 +22,13 @@ namespace util { } } - static ModuleInfo get_module(const char *libname) { - char path[256]; + static module_info_t get_module(const char *libname) { char buff[256]; int len_libname = strlen(libname); - FILE *file; uintptr_t addr = 0; size_t size = 0; - snprintf(path, sizeof path, "/proc/%d/smaps", getpid()); - file = fopen(path, "rt"); + auto file = fopen("/proc/self/smaps", "rt"); if (file == NULL) return {0, 0}; diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt @@ -12,7 +12,7 @@ class NativeLib { @Suppress("unused") private fun onNativeUnaryCall(uri: String, buffer: ByteArray): NativeRequestData? { - Log.d("SnapEnhance", "onNativeUnaryCall: uri=$uri, bufferSize=${buffer.size}, buffer=${buffer.contentToString()}") + // Log.d("SnapEnhance", "onNativeUnaryCall: uri=$uri, bufferSize=${buffer.size}, buffer=${buffer.contentToString()}") val nativeRequestData = NativeRequestData(uri, buffer) runCatching { nativeUnaryCallCallback(nativeRequestData)