feat: save overridden snaps in chat

This commit is contained in:
rhunk
2023-08-28 03:57:35 +02:00
parent 9b7ff40302
commit 514c5f186a
7 changed files with 70 additions and 39 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
LOGD("native initialized");
//signature might change in the future (unstable for now)
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");
}
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;

View File

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

View File

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