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