mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-04-29 22:24:35 +02:00
feat: rust native
Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
parent
17ddfa5b9c
commit
28d1ea2a43
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "dobby"]
|
||||
path = native/jni/external/dobby
|
||||
url = https://github.com/jmpews/Dobby
|
@ -4,6 +4,7 @@ plugins {
|
||||
alias(libs.plugins.androidLibrary) apply false
|
||||
alias(libs.plugins.kotlinAndroid) apply false
|
||||
alias(libs.plugins.compose.compiler) apply false
|
||||
alias(libs.plugins.rust.android) apply false
|
||||
}
|
||||
|
||||
var versionName = "2.1.0"
|
||||
@ -12,7 +13,7 @@ var versionCode = 210
|
||||
rootProject.ext.set("appVersionName", versionName)
|
||||
rootProject.ext.set("appVersionCode", versionCode)
|
||||
rootProject.ext.set("applicationId", "me.rhunk.snapenhance")
|
||||
rootProject.ext.set("buildHash", properties["debug_build_hash"] ?: java.security.SecureRandom().nextLong(1000000000, 99999999999).toString(16))
|
||||
rootProject.ext.set("buildHash", properties["debug_build_hash"] ?: java.security.SecureRandom().nextLong(Long.MAX_VALUE / 1000L, Long.MAX_VALUE).toString(16))
|
||||
|
||||
tasks.register("getVersion") {
|
||||
doLast {
|
||||
|
@ -23,7 +23,7 @@ android {
|
||||
buildConfigField("int", "VERSION_CODE", "${rootProject.ext["appVersionCode"]}")
|
||||
buildConfigField("String", "APPLICATION_ID", "\"${rootProject.ext["applicationId"]}\"")
|
||||
buildConfigField("long", "BUILD_TIMESTAMP", "${System.currentTimeMillis()}L")
|
||||
buildConfigField("String", "BUILD_HASH", "\"${rootProject.ext["buildHash"]}\"")
|
||||
buildConfigField("String", "BUILD_HASH", "\"${rootProject.ext["buildHash"]}\".toString()")
|
||||
val gitHash = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine("git", "rev-parse", "HEAD")
|
||||
|
@ -33,6 +33,7 @@ enum class InternalFileHandleType(
|
||||
MAPPINGS("mappings", "mappings.json"),
|
||||
MESSAGE_LOGGER("message_logger", "message_logger.db", isDatabase = true),
|
||||
PINNED_BEST_FRIEND("pinned_best_friend", "pinned_best_friend.txt"),
|
||||
NATIVE_SIG_CACHE("native_sig_cache", "native_sig_cache.txt"),
|
||||
SIF("sif", "libsif.so");
|
||||
|
||||
fun resolve(context: Context): File = if (isDatabase) {
|
||||
|
@ -6,6 +6,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import me.rhunk.snapenhance.bridge.storage.FileHandleManager
|
||||
import me.rhunk.snapenhance.common.BuildConfig
|
||||
import me.rhunk.snapenhance.common.Constants
|
||||
import me.rhunk.snapenhance.common.bridge.FileHandleScope
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileWrapper
|
||||
import me.rhunk.snapenhance.common.logger.AbstractLogger
|
||||
@ -15,7 +16,7 @@ import me.rhunk.snapenhance.mapper.ClassMapper
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class MappingsWrapper(
|
||||
fileHandleManager: LazyBridgeValue<FileHandleManager>
|
||||
private val fileHandleManager: LazyBridgeValue<FileHandleManager>
|
||||
): InternalFileWrapper(fileHandleManager, InternalFileHandleType.MAPPINGS, defaultValue = "{}") {
|
||||
private lateinit var context: Context
|
||||
private var mappingUniqueHash: Long = 0
|
||||
@ -68,6 +69,10 @@ class MappingsWrapper(
|
||||
|
||||
fun refresh() {
|
||||
mappingUniqueHash = getUniqueBuildId()
|
||||
|
||||
// reset native signature cache
|
||||
fileHandleManager.value.getFileHandle(FileHandleScope.INTERNAL.key, InternalFileHandleType.NATIVE_SIG_CACHE.key).delete()
|
||||
|
||||
val classMapper = ClassMapper(*mappers.values.toTypedArray())
|
||||
|
||||
runCatching {
|
||||
|
@ -193,6 +193,16 @@ class SnapEnhance {
|
||||
}
|
||||
|
||||
private fun initNative() {
|
||||
val nativeSigCacheFileHandle = appContext.fileHandlerManager.getFileHandle(FileHandleScope.INTERNAL.key, InternalFileHandleType.NATIVE_SIG_CACHE.key).toWrapper()
|
||||
|
||||
val oldSignatureCache = nativeSigCacheFileHandle.readBytes()
|
||||
.takeIf {
|
||||
it.isNotEmpty()
|
||||
}?.toString(Charsets.UTF_8)?.also {
|
||||
appContext.native.signatureCache = it
|
||||
appContext.log.verbose("old signature cache $it")
|
||||
}
|
||||
|
||||
val lateInit = appContext.native.initOnce {
|
||||
nativeUnaryCallCallback = { request ->
|
||||
appContext.event.post(NativeUnaryCallEvent(request.uri, request.buffer)) {
|
||||
@ -201,6 +211,14 @@ class SnapEnhance {
|
||||
}
|
||||
}
|
||||
appContext.reloadNativeConfig()
|
||||
}.let { init ->
|
||||
{
|
||||
init()
|
||||
appContext.native.signatureCache.takeIf { it != oldSignatureCache }?.let {
|
||||
appContext.log.verbose("new signature cache $it")
|
||||
nativeSigCacheFileHandle.writeBytes(it.toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (appContext.bridgeClient.getDebugProp("disable_sif", "false") != "true") {
|
||||
|
@ -23,6 +23,7 @@ material3 = "1.2.1"
|
||||
okhttp = "5.0.0-alpha.14"
|
||||
rhino = "1.7.15"
|
||||
rhino-android = "1.6.0"
|
||||
rust-android = "0.9.4"
|
||||
|
||||
[libraries]
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||
@ -59,5 +60,6 @@ androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
||||
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "compose-compiler" }
|
||||
rust-android = { id = "org.mozilla.rust-android-gradle.rust-android", version.ref = "rust-android" }
|
||||
|
||||
[bundles]
|
3
native/.gitignore
vendored
3
native/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/build
|
||||
/.cxx
|
||||
/.cxx
|
||||
/rust/target
|
@ -1,4 +1,5 @@
|
||||
plugins {
|
||||
alias(libs.plugins.rust.android)
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.kotlinAndroid)
|
||||
}
|
||||
@ -8,43 +9,16 @@ val nativeName = rootProject.ext.get("buildHash")
|
||||
android {
|
||||
namespace = rootProject.ext["applicationId"].toString() + ".nativelib"
|
||||
compileSdk = 34
|
||||
buildToolsVersion = "34.0.0"
|
||||
ndkVersion = "26.3.11579264"
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
buildConfigField("String", "NATIVE_NAME", "\"$nativeName\"")
|
||||
packaging {
|
||||
jniLibs {
|
||||
excludes += "**/libdobby.so"
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments += listOf(
|
||||
"-DOBFUSCATED_NAME=$nativeName",
|
||||
"-DBUILD_PACKAGE=${rootProject.ext["applicationId"]}",
|
||||
"-DBUILD_NAMESPACE=${namespace!!.replace(".", "/")}"
|
||||
)
|
||||
}
|
||||
ndk {
|
||||
//noinspection ChromeOsAbiSupport
|
||||
abiFilters += properties["debug_abi_filters"]?.toString()?.split(",")
|
||||
?: listOf("arm64-v8a", "armeabi-v7a")
|
||||
}
|
||||
}
|
||||
buildConfigField("String", "NATIVE_NAME", "\"$nativeName\".toString()")
|
||||
minSdk = 28
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path("jni/CMakeLists.txt")
|
||||
version = "3.22.1"
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
@ -53,4 +27,36 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cargo {
|
||||
module = "rust"
|
||||
libname = nativeName.toString()
|
||||
targetIncludes = arrayOf("libsnapenhance.so")
|
||||
targets = listOf("arm64", "arm")
|
||||
}
|
||||
|
||||
fun getNativeFiles() = File(projectDir, "build/rustJniLibs/android").listFiles()?.flatMap { abiFolder ->
|
||||
abiFolder.takeIf { it.isDirectory }?.listFiles()?.toList() ?: emptyList()
|
||||
}
|
||||
|
||||
tasks.register("cleanNatives") {
|
||||
doLast {
|
||||
println("Cleaning native files")
|
||||
getNativeFiles()?.forEach { file ->
|
||||
file.deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("preBuild").configure {
|
||||
dependsOn("cleanNatives", "cargoBuild")
|
||||
doLast {
|
||||
getNativeFiles()?.forEach { file ->
|
||||
if (file.name.endsWith(".so")) {
|
||||
println("Renaming ${file.absolutePath}")
|
||||
file.renameTo(File(file.parent, "lib$nativeName.so"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif(CCACHE_FOUND)
|
||||
|
||||
project("nativelib")
|
||||
|
||||
set(DOBBY_GENERATE_SHARED OFF)
|
||||
add_subdirectory(external/dobby)
|
||||
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
||||
src/library.cpp
|
||||
)
|
||||
|
||||
add_compile_definitions(BUILD_NAMESPACE="${BUILD_NAMESPACE}")
|
||||
add_compile_definitions(BUILD_PACKAGE="${BUILD_PACKAGE}")
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
||||
android
|
||||
log
|
||||
dobby_static
|
||||
)
|
||||
|
||||
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${OBFUSCATED_NAME})
|
1
native/jni/external/dobby
vendored
1
native/jni/external/dobby
vendored
@ -1 +0,0 @@
|
||||
Subproject commit b0176de574104726bb68dff3b77ee666300fc338
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "util.h"
|
||||
|
||||
#ifdef __aarch64__
|
||||
#define ARM64 true
|
||||
#else
|
||||
#define ARM64 false
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool disable_bitmoji;
|
||||
bool disable_metrics;
|
||||
bool composer_hooks;
|
||||
char custom_emoji_font_path[256];
|
||||
} native_config_t;
|
||||
|
||||
namespace common {
|
||||
static JavaVM *java_vm;
|
||||
static jobject native_lib_object;
|
||||
|
||||
static util::module_info_t client_module;
|
||||
static native_config_t *native_config = new native_config_t;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <dobby.h>
|
||||
|
||||
|
||||
static pthread_mutex_t hook_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void inline SafeHook(void *addr, void *hook, void **original) {
|
||||
pthread_mutex_lock(&hook_mutex);
|
||||
DobbyHook(addr, hook, original);
|
||||
pthread_mutex_unlock(&hook_mutex);
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
|
||||
namespace ComposerHook {
|
||||
enum {
|
||||
JS_TAG_FIRST = -11,
|
||||
JS_TAG_BIG_DECIMAL = -11,
|
||||
JS_TAG_BIG_INT = -10,
|
||||
JS_TAG_BIG_FLOAT = -9,
|
||||
JS_TAG_SYMBOL = -8,
|
||||
JS_TAG_STRING = -7,
|
||||
JS_TAG_MODULE = -3,
|
||||
JS_TAG_FUNCTION_BYTECODE = -2,
|
||||
JS_TAG_OBJECT = -1,
|
||||
|
||||
JS_TAG_INT = 0,
|
||||
JS_TAG_BOOL = 1,
|
||||
JS_TAG_NULL = 2,
|
||||
JS_TAG_UNDEFINED = 3,
|
||||
JS_TAG_UNINITIALIZED = 4,
|
||||
JS_TAG_CATCH_OFFSET = 5,
|
||||
JS_TAG_EXCEPTION = 6,
|
||||
JS_TAG_FLOAT64 = 7,
|
||||
};
|
||||
|
||||
typedef struct JSRefCountHeader {
|
||||
int ref_count;
|
||||
} JSRefCountHeader;
|
||||
|
||||
struct JSString {
|
||||
JSRefCountHeader header;
|
||||
uint32_t len : 31;
|
||||
uint8_t is_wide_char : 1;
|
||||
uint32_t hash : 30;
|
||||
uint8_t atom_type : 2;
|
||||
uint32_t hash_next;
|
||||
|
||||
union {
|
||||
uint8_t str8[0];
|
||||
uint16_t str16[0];
|
||||
} u;
|
||||
};
|
||||
|
||||
typedef union JSValueUnion {
|
||||
int32_t int32;
|
||||
double float64;
|
||||
void *ptr;
|
||||
} JSValueUnion;
|
||||
|
||||
typedef struct JSValue {
|
||||
JSValueUnion u;
|
||||
int64_t tag;
|
||||
} JSValue;
|
||||
|
||||
typedef struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
} list_head;
|
||||
|
||||
struct JSGCObjectHeader {
|
||||
int ref_count;
|
||||
uint8_t gc_obj_type : 4;
|
||||
uint8_t mark : 4;
|
||||
uint8_t dummy1;
|
||||
uint16_t dummy2;
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
struct JSContext {
|
||||
JSGCObjectHeader header;
|
||||
void *rt;
|
||||
struct list_head link;
|
||||
|
||||
uint16_t binary_object_count;
|
||||
int binary_object_size;
|
||||
|
||||
JSValue *array_shape;
|
||||
JSValue *class_proto;
|
||||
JSValue function_proto;
|
||||
JSValue function_ctor;
|
||||
JSValue array_ctor;
|
||||
JSValue regexp_ctor;
|
||||
JSValue promise_ctor;
|
||||
JSValue native_error_proto[8];
|
||||
JSValue iterator_proto;
|
||||
JSValue async_iterator_proto;
|
||||
JSValue array_proto_values;
|
||||
JSValue throw_type_error;
|
||||
JSValue eval_obj;
|
||||
|
||||
JSValue global_obj;
|
||||
JSValue global_var_obj;
|
||||
};
|
||||
|
||||
static void* global_instance;
|
||||
static JSContext *global_ctx;
|
||||
static std::string* composer_loader;
|
||||
|
||||
HOOK_DEF(JSValue, js_eval, void* instance, JSContext *ctx, void* this_obj, char *input, uintptr_t input_len, const char *filename, unsigned int flags, unsigned int scope_idx) {
|
||||
if (global_instance == nullptr || global_ctx == nullptr) {
|
||||
global_instance = instance;
|
||||
global_ctx = ctx;
|
||||
|
||||
if (composer_loader != nullptr) {
|
||||
LOGD("Injecting composer loader");
|
||||
composer_loader->resize(composer_loader->size() + input_len);
|
||||
memcpy((void*) (composer_loader->c_str() + composer_loader->size() - input_len), input, input_len);
|
||||
|
||||
input = (char*) composer_loader->c_str();
|
||||
input_len = composer_loader->size();
|
||||
}
|
||||
} else {
|
||||
if (composer_loader != nullptr) {
|
||||
delete composer_loader;
|
||||
composer_loader = nullptr;
|
||||
}
|
||||
}
|
||||
return js_eval_original(instance, ctx, this_obj, input, input_len, filename, flags, scope_idx);
|
||||
}
|
||||
|
||||
void setComposerLoader(JNIEnv *env, jobject, jstring code) {
|
||||
global_instance = nullptr;
|
||||
global_ctx = nullptr;
|
||||
auto code_str = env->GetStringUTFChars(code, nullptr);
|
||||
if (composer_loader != nullptr) delete composer_loader;
|
||||
composer_loader = new std::string(code_str, env->GetStringUTFLength(code));
|
||||
env->ReleaseStringUTFChars(code, code_str);
|
||||
}
|
||||
|
||||
jstring composerEval(JNIEnv *env, jobject, jstring script) {
|
||||
if (!ARM64) return env->NewStringUTF("Architecture not supported");
|
||||
if (global_instance == 0 || global_ctx == nullptr) {
|
||||
return env->NewStringUTF("Composer not ready");
|
||||
}
|
||||
|
||||
auto script_str = env->GetStringUTFChars(script, nullptr);
|
||||
auto length = env->GetStringUTFLength(script);
|
||||
auto jsvalue = js_eval_original(global_instance, global_ctx, (void*) &global_ctx->global_obj, (char *) script_str, length, "<eval>", 0, 0);
|
||||
env->ReleaseStringUTFChars(script, script_str);
|
||||
|
||||
if (jsvalue.tag == JS_TAG_STRING) {
|
||||
auto str = (JSString *) jsvalue.u.ptr;
|
||||
return env->NewStringUTF((const char *) str->u.str8);
|
||||
}
|
||||
|
||||
std::string result;
|
||||
switch (jsvalue.tag) {
|
||||
case JS_TAG_INT:
|
||||
result = std::to_string(jsvalue.u.int32);
|
||||
break;
|
||||
case JS_TAG_BOOL:
|
||||
result = jsvalue.u.int32 ? "true" : "false";
|
||||
break;
|
||||
case JS_TAG_NULL:
|
||||
result = "null";
|
||||
break;
|
||||
case JS_TAG_UNDEFINED:
|
||||
result = "undefined";
|
||||
break;
|
||||
case JS_TAG_OBJECT:
|
||||
result = "[object Object]";
|
||||
break;
|
||||
case JS_TAG_EXCEPTION:
|
||||
result = "Failed to evaluate script";
|
||||
break;
|
||||
case JS_TAG_FLOAT64:
|
||||
result = std::to_string(jsvalue.u.float64);
|
||||
break;
|
||||
default:
|
||||
result = "[unknown tag " + std::to_string(jsvalue.tag) + "]";
|
||||
break;
|
||||
}
|
||||
|
||||
return env->NewStringUTF(result.c_str());
|
||||
}
|
||||
|
||||
void init() {
|
||||
auto js_eval_ptr = util::find_signature(
|
||||
common::client_module.base,
|
||||
common::client_module.size,
|
||||
ARM64 ? "00 E4 00 6F 29 00 80 52 76 00 04 8B" : "A1 B0 07 92 81 46",
|
||||
ARM64 ? -0x28 : -0x7
|
||||
);
|
||||
if (js_eval_ptr == 0) {
|
||||
LOGE("js_eval_ptr signature not found");
|
||||
return;
|
||||
}
|
||||
SafeHook((void*) js_eval_ptr, (void *) js_eval, (void **) &js_eval_original);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace CustomEmojiFont {
|
||||
HOOK_DEF(int, open_hook, const char *pathname, int flags, mode_t mode) {
|
||||
auto custom_path = common::native_config->custom_emoji_font_path;
|
||||
if (strstr(pathname, "/system/fonts/NotoColorEmoji.ttf") != 0 && custom_path[0] != 0) {
|
||||
struct stat buffer;
|
||||
if (stat(custom_path, &buffer) == 0) {
|
||||
pathname = custom_path;
|
||||
}
|
||||
}
|
||||
return open_hook_original(pathname, flags, mode);
|
||||
}
|
||||
|
||||
void init() {
|
||||
SafeHook((void *) DobbySymbolResolver("libc.so", "open"), (void *)open_hook, (void **)&open_hook_original);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace DuplexHook {
|
||||
HOOK_DEF(jboolean, IsSameObject, JNIEnv * env, jobject obj1, jobject obj2) {
|
||||
if (obj1 == nullptr || obj2 == nullptr) return IsSameObject_original(env, obj1, obj2);
|
||||
|
||||
auto clazz = env->FindClass("java/lang/Class");
|
||||
if (!env->IsInstanceOf(obj1, clazz)) return IsSameObject_original(env, obj1, obj2);
|
||||
|
||||
jstring obj1ClassName = (jstring) env->CallObjectMethod(obj1, env->GetMethodID(clazz, "getName", "()Ljava/lang/String;"));
|
||||
const char* obj1ClassNameStr = env->GetStringUTFChars(obj1ClassName, nullptr);
|
||||
|
||||
if (strstr(obj1ClassNameStr, "com.snapchat.client.duplex.MessageHandler") != 0) {
|
||||
env->ReleaseStringUTFChars(obj1ClassName, obj1ClassNameStr);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
env->ReleaseStringUTFChars(obj1ClassName, obj1ClassNameStr);
|
||||
return IsSameObject_original(env, obj1, obj2);
|
||||
}
|
||||
|
||||
void init(JNIEnv* env) {
|
||||
SafeHook((void *)env->functions->IsSameObject, (void *)IsSameObject, (void **)&IsSameObject_original);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace FstatHook {
|
||||
HOOK_DEF(int, fstat_hook, int fd, struct stat *buf) {
|
||||
char name[256];
|
||||
memset(name, 0, sizeof(name));
|
||||
snprintf(name, sizeof(name), "/proc/self/fd/%d", fd);
|
||||
readlink(name, name, sizeof(name));
|
||||
|
||||
std::string fileName(name);
|
||||
|
||||
if (common::native_config->disable_metrics && fileName.find("files/blizzardv2/queues") != std::string::npos) {
|
||||
unlink(name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (common::native_config->disable_bitmoji && fileName.find("com.snap.file_manager_4_SCContent") != std::string::npos) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fstat_hook_original(fd, buf);
|
||||
}
|
||||
|
||||
void init() {
|
||||
SafeHook((void *)DobbySymbolResolver("libc.so", "fstat"), (void *)fstat_hook, (void **)&fstat_hook_original);
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace LinkerHook {
|
||||
static auto linker_openat_hooks = std::map<std::string, std::pair<uintptr_t, size_t>>();
|
||||
|
||||
void JNICALL addLinkerSharedLibrary(JNIEnv *env, jobject, jstring path, jbyteArray content) {
|
||||
const char *path_str = env->GetStringUTFChars(path, nullptr);
|
||||
jsize content_len = env->GetArrayLength(content);
|
||||
jbyte *content_ptr = env->GetByteArrayElements(content, nullptr);
|
||||
|
||||
auto allocated_content = (jbyte *) malloc(content_len);
|
||||
memcpy(allocated_content, content_ptr, content_len);
|
||||
linker_openat_hooks[path_str] = std::make_pair((uintptr_t) allocated_content, content_len);
|
||||
|
||||
LOGD("added linker hook for %s, size=%d", path_str, content_len);
|
||||
|
||||
env->ReleaseStringUTFChars(path, path_str);
|
||||
env->ReleaseByteArrayElements(content, content_ptr, JNI_ABORT);
|
||||
}
|
||||
|
||||
HOOK_DEF(int, linker_openat, int dirfd, const char *pathname, int flags, mode_t mode) {
|
||||
for (const auto &item: linker_openat_hooks) {
|
||||
if (strstr(pathname, item.first.c_str())) {
|
||||
LOGD("found openat hook for %s", pathname);
|
||||
static auto memfd_create = (int (*)(const char *, unsigned int)) DobbySymbolResolver("libc.so", "memfd_create");
|
||||
auto fd = memfd_create("me.rhunk.snapenhance", 0);
|
||||
LOGD("memfd created: %d", fd);
|
||||
|
||||
if (fd == -1) {
|
||||
LOGE("memfd_create failed: %d", errno);
|
||||
return -1;
|
||||
}
|
||||
if (write(fd, (void *) item.second.first, item.second.second) == -1) {
|
||||
LOGE("write failed: %d", errno);
|
||||
return -1;
|
||||
}
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
free((void *) item.second.first);
|
||||
linker_openat_hooks.erase(item.first);
|
||||
|
||||
LOGD("memfd written");
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
return linker_openat_original(dirfd, pathname, flags, mode);
|
||||
}
|
||||
|
||||
void init() {
|
||||
DobbyHook((void *) DobbySymbolResolver(ARM64 ? "linker64" : "linker", "__dl___openat"), (void *) linker_openat, (void **) &linker_openat_original);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace SqliteMutexHook {
|
||||
typedef struct {
|
||||
pthread_mutex_t mutex;
|
||||
} sqlite3_mutex;
|
||||
|
||||
typedef struct {
|
||||
void* pVfs;
|
||||
void* pVdbe;
|
||||
void* pDfltColl;
|
||||
sqlite3_mutex* mutex;
|
||||
} sqlite3;
|
||||
|
||||
static std::map<std::string, sqlite3_mutex *> mutex_map = {};
|
||||
|
||||
HOOK_DEF(int, sqlite3_open_hook, const char *filename, sqlite3 **ppDb, unsigned int flags, const char *zVfs) {
|
||||
auto result = sqlite3_open_hook_original(filename, ppDb, flags, zVfs);
|
||||
if (result == 0) {
|
||||
auto mutex = (*ppDb)->mutex;
|
||||
if (mutex == nullptr) return result;
|
||||
|
||||
auto last_slash = strrchr(filename, '/') + 1;
|
||||
if (last_slash > filename) {
|
||||
LOGD("sqlite3_open_hook: %s", last_slash);
|
||||
mutex_map[last_slash] = mutex;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void init() {
|
||||
auto open_database_sig = util::find_signature(
|
||||
common::client_module.base, common::client_module.size,
|
||||
ARM64 ? "FF FF 00 A9 3F 00 00 F9" : "9A 46 90 46 78 44 89 46 05 68",
|
||||
ARM64 ? -0x3C : -0xd
|
||||
);
|
||||
if (open_database_sig == 0) {
|
||||
LOGE("sqlite3 openDatabase sig not found");
|
||||
return;
|
||||
}
|
||||
SafeHook((void *) open_database_sig, (void *) sqlite3_open_hook, (void **) &sqlite3_open_hook_original);
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../util.h"
|
||||
|
||||
namespace UnaryCallHook {
|
||||
namespace grpc {
|
||||
typedef struct {
|
||||
void* ref_counter;
|
||||
size_t length;
|
||||
uint8_t* data;
|
||||
} ref_counted_slice_byte_buffer;
|
||||
|
||||
typedef struct {
|
||||
void* reserved;
|
||||
void* type;
|
||||
void* compression;
|
||||
ref_counted_slice_byte_buffer *slice_buffer;
|
||||
} grpc_byte_buffer;
|
||||
}
|
||||
|
||||
static jmethodID native_lib_on_unary_call_method;
|
||||
|
||||
HOOK_DEF(void *, unaryCall_hook, void *unk1, const char *uri, grpc::grpc_byte_buffer **buffer_ptr, void *unk4, void *unk5, void *unk6) {
|
||||
// request without reference counter can be hooked using xposed ig
|
||||
auto slice_buffer = (*buffer_ptr)->slice_buffer;
|
||||
if (slice_buffer->ref_counter == 0) {
|
||||
return unaryCall_hook_original(unk1, uri, buffer_ptr, unk4, unk5, unk6);
|
||||
}
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
common::java_vm->GetEnv((void **)&env, JNI_VERSION_1_6);
|
||||
|
||||
auto jni_buffer_array = env->NewByteArray(slice_buffer->length);
|
||||
env->SetByteArrayRegion(jni_buffer_array, 0, slice_buffer->length, (jbyte *)slice_buffer->data);
|
||||
|
||||
auto native_request_data_object = env->CallObjectMethod(common::native_lib_object, native_lib_on_unary_call_method, env->NewStringUTF(uri), jni_buffer_array);
|
||||
|
||||
if (native_request_data_object != nullptr) {
|
||||
auto native_request_data_class = env->GetObjectClass(native_request_data_object);
|
||||
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;
|
||||
}
|
||||
|
||||
auto new_buffer = (jbyteArray)env->GetObjectField(native_request_data_object, env->GetFieldID(native_request_data_class, "buffer", "[B"));
|
||||
auto new_buffer_length = env->GetArrayLength(new_buffer);
|
||||
auto new_buffer_data = env->GetByteArrayElements(new_buffer, nullptr);
|
||||
|
||||
//we need to allocate a new ref_counter struct and copy the old ref_counter and the new_buffer to it
|
||||
const static auto ref_counter_struct_size = (uintptr_t)slice_buffer->data - (uintptr_t)slice_buffer->ref_counter;
|
||||
|
||||
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(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);
|
||||
}
|
||||
|
||||
return unaryCall_hook_original(unk1, uri, buffer_ptr, unk4, unk5, unk6);
|
||||
}
|
||||
|
||||
void init(JNIEnv *env) {
|
||||
auto unaryCall_func = util::find_signature(
|
||||
common::client_module.base, common::client_module.size,
|
||||
ARM64 ? "A8 03 1F F8 C2 00 00 94" : "0A 90 00 F0 3F F9",
|
||||
ARM64 ? -0x48 : -0x37
|
||||
);
|
||||
|
||||
native_lib_on_unary_call_method = env->GetMethodID(env->GetObjectClass(common::native_lib_object), "onNativeUnaryCall", "(Ljava/lang/String;[B)L" BUILD_NAMESPACE "/NativeRequestData;");
|
||||
|
||||
if (unaryCall_func != 0) {
|
||||
SafeHook((void *)unaryCall_func, (void *)unaryCall_hook, (void **)&unaryCall_hook_original);
|
||||
} else {
|
||||
LOGE("Can't find unaryCall signature");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <dobby.h>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
#include "logger.h"
|
||||
#include "common.h"
|
||||
#include "dobby_helper.h"
|
||||
#include "hooks/linker_hook.h"
|
||||
#include "hooks/unary_call.h"
|
||||
#include "hooks/fstat_hook.h"
|
||||
#include "hooks/sqlite_mutex.h"
|
||||
#include "hooks/duplex_hook.h"
|
||||
#include "hooks/composer_hook.h"
|
||||
#include "hooks/custom_emoji_font.h"
|
||||
|
||||
bool JNICALL init(JNIEnv *env, jobject clazz) {
|
||||
LOGD("Initializing native");
|
||||
using namespace common;
|
||||
|
||||
native_lib_object = env->NewGlobalRef(clazz);
|
||||
client_module = util::get_module("libclient.so");
|
||||
|
||||
if (client_module.base == 0) {
|
||||
LOGD("libclient.so not found, trying split_config");
|
||||
client_module = util::get_module(("split_config." + std::string(ARM64 ? "arm64_v8a" : "armeabi-v7a") + ".apk").c_str());
|
||||
if (client_module.base == 0) {
|
||||
LOGE("can't find split_config!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOGD("client_module offset=0x%lx, size=0x%zx", client_module.base, client_module.size);
|
||||
|
||||
auto threads = std::vector<std::thread>();
|
||||
|
||||
#define RUN(body) threads.push_back(std::thread([&] { body; }))
|
||||
|
||||
RUN(UnaryCallHook::init(env));
|
||||
RUN(FstatHook::init());
|
||||
RUN(SqliteMutexHook::init());
|
||||
RUN(DuplexHook::init(env));
|
||||
if (common::native_config->custom_emoji_font_path[0] != 0) {
|
||||
RUN(CustomEmojiFont::init());
|
||||
}
|
||||
if (common::native_config->composer_hooks) {
|
||||
RUN(ComposerHook::init());
|
||||
}
|
||||
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
LOGD("Native initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
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"))
|
||||
auto native_config = common::native_config;
|
||||
|
||||
native_config->disable_bitmoji = GET_CONFIG_BOOL("disableBitmoji");
|
||||
native_config->disable_metrics = GET_CONFIG_BOOL("disableMetrics");
|
||||
native_config->composer_hooks = GET_CONFIG_BOOL("composerHooks");
|
||||
|
||||
memset(native_config->custom_emoji_font_path, 0, sizeof(native_config->custom_emoji_font_path));
|
||||
auto custom_emoji_font_path = env->GetObjectField(config_object, env->GetFieldID(native_config_clazz, "customEmojiFontPath", "Ljava/lang/String;"));
|
||||
if (custom_emoji_font_path != nullptr) {
|
||||
auto custom_emoji_font_path_str = env->GetStringUTFChars((jstring) custom_emoji_font_path, nullptr);
|
||||
strncpy(native_config->custom_emoji_font_path, custom_emoji_font_path_str, sizeof(native_config->custom_emoji_font_path));
|
||||
env->ReleaseStringUTFChars((jstring) custom_emoji_font_path, custom_emoji_font_path_str);
|
||||
}
|
||||
}
|
||||
|
||||
void JNICALL lock_database(JNIEnv *env, jobject, jstring database_name, jobject runnable) {
|
||||
auto database_name_str = env->GetStringUTFChars(database_name, nullptr);
|
||||
auto mutex = SqliteMutexHook::mutex_map[database_name_str];
|
||||
env->ReleaseStringUTFChars(database_name, database_name_str);
|
||||
|
||||
if (mutex != nullptr) {
|
||||
auto lock_result = pthread_mutex_lock(&mutex->mutex);
|
||||
if (lock_result != 0) {
|
||||
LOGE("pthread_mutex_lock failed: %d", lock_result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
env->CallVoidMethod(runnable, env->GetMethodID(env->GetObjectClass(runnable), "run", "()V"));
|
||||
|
||||
if (mutex != nullptr) {
|
||||
pthread_mutex_unlock(&mutex->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *_) {
|
||||
common::java_vm = vm;
|
||||
JNIEnv *env = nullptr;
|
||||
vm->GetEnv((void **)&env, JNI_VERSION_1_6);
|
||||
|
||||
auto methods = std::vector<JNINativeMethod>();
|
||||
methods.push_back({"init", "()Z", (void *)init});
|
||||
methods.push_back({"loadConfig", "(L" BUILD_NAMESPACE "/NativeConfig;)V", (void *)load_config});
|
||||
methods.push_back({"lockDatabase", "(Ljava/lang/String;Ljava/lang/Runnable;)V", (void *)lock_database});
|
||||
methods.push_back({"setComposerLoader", "(Ljava/lang/String;)V", (void *) ComposerHook::setComposerLoader});
|
||||
methods.push_back({"composerEval", "(Ljava/lang/String;)Ljava/lang/String;",(void *) ComposerHook::composerEval});
|
||||
methods.push_back({"addLinkerSharedLibrary", "(Ljava/lang/String;[B)V", (void *) LinkerHook::addLinkerSharedLibrary});
|
||||
|
||||
LinkerHook::init();
|
||||
|
||||
env->RegisterNatives(env->FindClass(std::string(BUILD_NAMESPACE "/NativeLib").c_str()), methods.data(), methods.size());
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#define LOG_TAG "SnapEnhanceNative"
|
||||
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define HOOK_DEF(ret, func, ...) ret (*func##_original)(__VA_ARGS__); ret func(__VA_ARGS__)
|
||||
|
||||
namespace util {
|
||||
typedef struct {
|
||||
uintptr_t base;
|
||||
size_t size;
|
||||
} module_info_t;
|
||||
|
||||
static module_info_t get_module(const char *libname) {
|
||||
char buff[256];
|
||||
int len_libname = strlen(libname);
|
||||
uintptr_t start_offset = 0;
|
||||
uintptr_t end_offset = 0;
|
||||
|
||||
auto file = fopen("/proc/self/smaps", "rt");
|
||||
if (file == NULL)
|
||||
return {0, 0};
|
||||
|
||||
while (fgets(buff, sizeof buff, file) != NULL) {
|
||||
int len = strlen(buff);
|
||||
if (len > 0 && buff[len - 1] == '\n') {
|
||||
buff[--len] = '\0';
|
||||
}
|
||||
if (len <= len_libname || memcmp(buff + len - len_libname, libname, len_libname)) {
|
||||
continue;
|
||||
}
|
||||
size_t start, end, offset;
|
||||
char flags[4];
|
||||
if (sscanf(buff, "%zx-%zx %c%c%c%c %zx", &start, &end,
|
||||
&flags[0], &flags[1], &flags[2], &flags[3], &offset) != 7) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flags[0] != 'r' || flags[2] != 'x') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (start_offset == 0) {
|
||||
start_offset = start;
|
||||
}
|
||||
end_offset = end;
|
||||
}
|
||||
fclose(file);
|
||||
if (start_offset == 0) {
|
||||
return {0, 0};
|
||||
}
|
||||
return { start_offset, end_offset - start_offset };
|
||||
}
|
||||
|
||||
static uintptr_t find_signature(uintptr_t module_base, uintptr_t size, const std::string &pattern, int offset = 0) {
|
||||
std::vector<char> bytes;
|
||||
std::vector<char> mask;
|
||||
for (size_t i = 0; i < pattern.size(); i += 3) {
|
||||
if (pattern[i] == '?') {
|
||||
bytes.push_back(0);
|
||||
mask.push_back('?');
|
||||
} else {
|
||||
bytes.push_back(std::stoi(pattern.substr(i, 2), nullptr, 16));
|
||||
mask.push_back('x');
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
bool found = true;
|
||||
for (size_t j = 0; j < bytes.size(); j++) {
|
||||
if (mask[j] == '?' || bytes[j] == *(char *) (module_base + i + j)) {
|
||||
continue;
|
||||
}
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
return module_base + i + offset;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
724
native/rust/Cargo.lock
generated
Normal file
724
native/rust/Cargo.lock
generated
Normal file
@ -0,0 +1,724 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937"
|
||||
|
||||
[[package]]
|
||||
name = "android_logger"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"env_filter",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dobby-rs"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0b8fbf3688f3584f3f87ec7024032d81da6e8b868a1d23d8a6c0a399e7c9935"
|
||||
dependencies = [
|
||||
"dobby-sys",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dobby-sys"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cdffdeaa52be950db80677f22f549050675f226b77e97fdbe10bb2dd846ac7b"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6dc8c8ff84895b051f07a0e65f975cf225131742531338752abfb324e4449ff"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"cfg-if",
|
||||
"combine",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"procfs-core",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs-core"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"chrono",
|
||||
"hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.120"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snapenhance"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"android_logger",
|
||||
"dobby-rs",
|
||||
"jni",
|
||||
"log",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"procfs",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
19
native/rust/Cargo.toml
Normal file
19
native/rust/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "snapenhance"
|
||||
version = "0.1.0"
|
||||
authors = ["rhunk"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
android_logger = "0.14.1"
|
||||
dobby-rs = "0.1.0"
|
||||
jni = "0.21.1"
|
||||
log = "0.4.22"
|
||||
nix = { version = "0.29.0", features = ["fs"] }
|
||||
once_cell = "1.19.0"
|
||||
paste = "1.0.15"
|
||||
procfs = "0.16.0"
|
||||
serde_json = "1.0.120"
|
3
native/rust/build.rs
Normal file
3
native/rust/build.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=static=c++");
|
||||
}
|
49
native/rust/src/common.rs
Normal file
49
native/rust/src/common.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use jni::{objects::GlobalRef, JavaVM};
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
|
||||
use crate::mapped_lib::MappedLib;
|
||||
|
||||
static NATIVE_LIB_INSTANCE: OnceCell<GlobalRef> = OnceCell::new();
|
||||
static JAVA_VM: OnceCell<usize> = OnceCell::new();
|
||||
|
||||
pub static CLIENT_MODULE: Lazy<MappedLib> = Lazy::new(|| {
|
||||
let mut client_module = MappedLib::new("libclient.so".into());
|
||||
|
||||
if let Err(error) = client_module.search() {
|
||||
warn!("Unable to find libclient.so: {}", error);
|
||||
|
||||
client_module = MappedLib::new("split_config.arm".into());
|
||||
|
||||
if let Err(error) = client_module.search() {
|
||||
panic!("Unable to find split_config.arm: {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
client_module
|
||||
});
|
||||
|
||||
|
||||
pub fn set_native_lib_instance(instance: GlobalRef) {
|
||||
NATIVE_LIB_INSTANCE.set(instance).expect("NativeLib instance already set");
|
||||
}
|
||||
|
||||
pub fn native_lib_instance() -> GlobalRef {
|
||||
NATIVE_LIB_INSTANCE.get().expect("NativeLib instance not set").clone()
|
||||
}
|
||||
|
||||
pub fn set_java_vm(vm: *mut jni::sys::JavaVM) {
|
||||
JAVA_VM.set(vm as usize).expect("JavaVM already set");
|
||||
}
|
||||
|
||||
pub fn java_vm() -> JavaVM {
|
||||
unsafe {
|
||||
JavaVM::from_raw(*JAVA_VM.get().expect("JavaVM not set") as *mut jni::sys::JavaVM).expect("Failed to get JavaVM")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach_jni_env(block: impl FnOnce(&mut jni::JNIEnv)) {
|
||||
let jvm = java_vm();
|
||||
let mut env: jni::AttachGuard = jvm.attach_current_thread().expect("Failed to attach to current thread");
|
||||
|
||||
block(&mut env);
|
||||
}
|
54
native/rust/src/config.rs
Normal file
54
native/rust/src/config.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use std::{error::Error, sync::Mutex};
|
||||
use jni::{objects::JObject, JNIEnv};
|
||||
use crate::util::get_jni_string;
|
||||
|
||||
static NATIVE_CONFIG: Mutex<Option<NativeConfig>> = Mutex::new(None);
|
||||
|
||||
pub fn native_config() -> NativeConfig {
|
||||
NATIVE_CONFIG.lock().unwrap().as_ref().expect("NativeConfig not loaded").clone()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct NativeConfig {
|
||||
pub disable_bitmoji: bool,
|
||||
pub disable_metrics: bool,
|
||||
pub composer_hooks: bool,
|
||||
pub custom_emoji_font_path: Option<String>,
|
||||
}
|
||||
|
||||
impl NativeConfig {
|
||||
fn new(env: &mut JNIEnv, obj: JObject) -> Result<Self, Box<dyn Error>> {
|
||||
macro_rules! get_boolean {
|
||||
($field:expr) => {
|
||||
env.get_field(&obj, $field, "Z")?.z()?
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! get_string {
|
||||
($field:expr) => {
|
||||
match env.get_field(&obj, $field, "Ljava/lang/String;")?.l()? {
|
||||
jstring => if !jstring.is_null() {
|
||||
Some(get_jni_string(env, jstring.into())?)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
disable_bitmoji: get_boolean!("disableBitmoji"),
|
||||
disable_metrics: get_boolean!("disableMetrics"),
|
||||
composer_hooks: get_boolean!("composerHooks"),
|
||||
custom_emoji_font_path: get_string!("customEmojiFontPath"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_config(mut env: JNIEnv, _class: JObject, obj: JObject) {
|
||||
NATIVE_CONFIG.lock().unwrap().replace(
|
||||
NativeConfig::new(&mut env, obj).expect("Failed to load NativeConfig")
|
||||
);
|
||||
|
||||
info!("Config loaded {:?}", native_config());
|
||||
}
|
50
native/rust/src/hook.rs
Normal file
50
native/rust/src/hook.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub static MUTEX: Mutex<()> = Mutex::new(());
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! def_hook {
|
||||
($func:ident, $ret:ty, | $($arg:ident : $arg_type:ty),* | $body:block) => {
|
||||
paste::item! {
|
||||
#[allow(non_upper_case_globals)]
|
||||
static mut [<$func _original>]: std::option::Option<extern "C" fn($($arg_type),*) -> $ret> = None;
|
||||
|
||||
fn $func($($arg: $arg_type),*) -> $ret {
|
||||
{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
$body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dobby_hook {
|
||||
($sym:expr, $hook:expr) => {
|
||||
paste::item! {
|
||||
unsafe {
|
||||
if let Ok(_) = crate::hook::MUTEX.lock() {
|
||||
if let Some(ptr) = dobby_rs::hook($sym, $hook as *mut std::ffi::c_void).ok().map(|x| x as *mut std::ffi::c_void) {
|
||||
[<$hook _original>] = std::mem::transmute(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dobby_hook_sym {
|
||||
($lib:expr, $sym:expr, $hook:expr) => {
|
||||
if let Some(hook_symbol) = dobby_rs::resolve_symbol($lib, $sym) {
|
||||
crate::dobby_hook!(hook_symbol, $hook);
|
||||
debug!("hooked symbol: {}", $sym);
|
||||
} else {
|
||||
panic!("Failed to resolve symbol: {}", $sym);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
146
native/rust/src/lib.rs
Normal file
146
native/rust/src/lib.rs
Normal file
@ -0,0 +1,146 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
mod common;
|
||||
|
||||
mod hook;
|
||||
mod util;
|
||||
mod mapped_lib;
|
||||
mod config;
|
||||
mod sig;
|
||||
|
||||
mod modules;
|
||||
|
||||
use android_logger::Config;
|
||||
use log::LevelFilter;
|
||||
use modules::{composer_hook, duplex_hook, fstat_hook, linker_hook, sqlite_hook, unary_call_hook};
|
||||
|
||||
use jni::objects::{JObject, JString};
|
||||
use jni::sys::{jint, jstring, JNI_VERSION_1_6};
|
||||
use jni::{JNIEnv, JavaVM, NativeMethod};
|
||||
use util::get_jni_string;
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
fn pre_init() {
|
||||
linker_hook::init();
|
||||
}
|
||||
|
||||
fn init(mut env: JNIEnv, _class: JObject, signature_cache: JString) -> jstring {
|
||||
debug!("Initializing native lib");
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
|
||||
// load signature cache
|
||||
|
||||
if !signature_cache.is_null() {
|
||||
let sig_cache_str = get_jni_string(&mut env, signature_cache).expect("Failed to convert mappings to string");
|
||||
|
||||
if let Ok(signature_cache) = serde_json::from_str(sig_cache_str.as_str()) {
|
||||
sig::add_signatures(signature_cache);
|
||||
} else {
|
||||
error!("Failed to load signature cache");
|
||||
}
|
||||
}
|
||||
|
||||
common::set_native_lib_instance(env.new_global_ref(_class).ok().expect("Failed to create global ref"));
|
||||
|
||||
let _ = common::CLIENT_MODULE;
|
||||
|
||||
// initialize modules asynchronously
|
||||
|
||||
let mut threads: Vec<JoinHandle<()>> = Vec::new();
|
||||
|
||||
macro_rules! async_init {
|
||||
($($f:expr),*) => {
|
||||
$(
|
||||
threads.push(std::thread::spawn(move || {
|
||||
$f;
|
||||
}));
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
async_init!(
|
||||
duplex_hook::init(),
|
||||
unary_call_hook::init(),
|
||||
composer_hook::init(),
|
||||
fstat_hook::init(),
|
||||
sqlite_hook::init()
|
||||
);
|
||||
|
||||
threads.into_iter().for_each(|t| t.join().unwrap());
|
||||
|
||||
info!("native init took {:?}", start_time.elapsed());
|
||||
|
||||
// send back the signature cache
|
||||
if let Ok(signature_cache) = serde_json::to_string(&sig::get_signatures()) {
|
||||
env.new_string(signature_cache).ok().expect("Failed to create new string").into_raw()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn JNI_OnLoad(_vm: JavaVM, _: *mut c_void) -> jint {
|
||||
android_logger::init_once(
|
||||
Config::default()
|
||||
.with_max_level(LevelFilter::Debug)
|
||||
.with_tag("SnapEnhanceNative")
|
||||
);
|
||||
|
||||
info!("JNI_OnLoad called");
|
||||
|
||||
std::panic::set_hook(Box::new(|panic_info| {
|
||||
error!("{:?}", panic_info);
|
||||
}));
|
||||
|
||||
common::set_java_vm(_vm.get_java_vm_pointer());
|
||||
|
||||
let mut env = _vm.get_env().expect("Failed to get JNIEnv");
|
||||
|
||||
let native_lib_class = env.find_class("me/rhunk/snapenhance/nativelib/NativeLib").expect("NativeLib class not found");
|
||||
|
||||
env.register_native_methods(
|
||||
native_lib_class,
|
||||
&[
|
||||
NativeMethod {
|
||||
name: "init".into(),
|
||||
sig: "(Ljava/lang/String;)Ljava/lang/String;".into(),
|
||||
fn_ptr: init as *mut c_void,
|
||||
},
|
||||
NativeMethod {
|
||||
name: "loadConfig".into(),
|
||||
sig: "(Lme/rhunk/snapenhance/nativelib/NativeConfig;)V".into(),
|
||||
fn_ptr: config::load_config as *mut c_void,
|
||||
},
|
||||
NativeMethod {
|
||||
name: "addLinkerSharedLibrary".into(),
|
||||
sig: "(Ljava/lang/String;[B)V".into(),
|
||||
fn_ptr: linker_hook::add_linker_shared_library as *mut c_void,
|
||||
},
|
||||
NativeMethod {
|
||||
name: "lockDatabase".into(),
|
||||
sig: "(Ljava/lang/String;Ljava/lang/Runnable;)V".into(),
|
||||
fn_ptr: sqlite_hook::lock_database as *mut c_void,
|
||||
},
|
||||
NativeMethod {
|
||||
name: "setComposerLoader".into(),
|
||||
sig: "(Ljava/lang/String;)V".into(),
|
||||
fn_ptr: composer_hook::set_composer_loader as *mut c_void,
|
||||
},
|
||||
NativeMethod {
|
||||
name: "composerEval".into(),
|
||||
sig: "(Ljava/lang/String;)Ljava/lang/String;".into(),
|
||||
fn_ptr: composer_hook::composer_eval as *mut c_void,
|
||||
}
|
||||
]
|
||||
).expect("Failed to register native methods");
|
||||
|
||||
pre_init();
|
||||
|
||||
JNI_VERSION_1_6
|
||||
}
|
49
native/rust/src/mapped_lib.rs
Normal file
49
native/rust/src/mapped_lib.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use std::error::Error;
|
||||
|
||||
use procfs::process::{MMPermissions, MMapPath};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MappedRegion {
|
||||
pub start: u64,
|
||||
pub end: u64,
|
||||
pub perms: MMPermissions,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MappedLib {
|
||||
name: String,
|
||||
pub regions: Vec<MappedRegion>,
|
||||
}
|
||||
|
||||
impl MappedLib {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
regions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search(&mut self) -> Result<&Self, Box<dyn Error>> {
|
||||
procfs::process::Process::myself()?.maps()?.iter().for_each(|map| {
|
||||
let pathname = &map.pathname;
|
||||
|
||||
if let MMapPath::Path(path_buffer) = pathname {
|
||||
let path = path_buffer.to_string_lossy();
|
||||
|
||||
if path.contains(&self.name) {
|
||||
self.regions.push(MappedRegion {
|
||||
start: map.address.0,
|
||||
end: map.address.1,
|
||||
perms: map.perms,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if self.regions.is_empty() {
|
||||
return Err(format!("No regions found for {}", self.name).into());
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
184
native/rust/src/modules/composer_hook.rs
Normal file
184
native/rust/src/modules/composer_hook.rs
Normal file
@ -0,0 +1,184 @@
|
||||
#![allow(dead_code, unused_mut)]
|
||||
|
||||
use std::{cell::Cell, ffi::{c_void, CStr}, sync::Mutex};
|
||||
use jni::{objects::JString, sys::jobject, JNIEnv};
|
||||
use crate::{common, config, def_hook, dobby_hook, sig, util::get_jni_string};
|
||||
|
||||
const JS_TAG_BIG_DECIMAL: i64 = -11;
|
||||
const JS_TAG_BIG_INT: i64 = -10;
|
||||
const JS_TAG_BIG_FLOAT: i64 = -9;
|
||||
const JS_TAG_SYMBOL: i64 = -8;
|
||||
const JS_TAG_STRING: i64 = -7;
|
||||
const JS_TAG_MODULE: i64 = -3;
|
||||
const JS_TAG_FUNCTION_BYTECODE: i64 = -2;
|
||||
const JS_TAG_OBJECT: i64 = -1;
|
||||
const JS_TAG_INT: i64 = 0;
|
||||
const JS_TAG_BOOL: i64 = 1;
|
||||
const JS_TAG_NULL: i64 = 2;
|
||||
const JS_TAG_UNDEFINED: i64 = 3;
|
||||
const JS_TAG_UNINITIALIZED: i64 = 4;
|
||||
const JS_TAG_CATCH_OFFSET: i64 = 5;
|
||||
const JS_TAG_EXCEPTION: i64 = 6;
|
||||
const JS_TAG_FLOAT64: i64 = 7;
|
||||
|
||||
#[repr(C)]
|
||||
struct JsString {
|
||||
/*
|
||||
original structure :
|
||||
struct JSString {
|
||||
struct JSRefCountHeader {
|
||||
int ref_count;
|
||||
};
|
||||
uint32_t len : 31;
|
||||
uint8_t is_wide_char : 1;
|
||||
uint32_t hash : 30;
|
||||
uint8_t atom_type : 2;
|
||||
uint32_t hash_next;
|
||||
|
||||
union {
|
||||
uint8_t str8[0];
|
||||
uint16_t str16[0];
|
||||
} u;
|
||||
};
|
||||
*/
|
||||
pad: [u32; 4],
|
||||
str8: [u8; 0],
|
||||
str16: [u16; 0],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
union JsValueUnion {
|
||||
int32: i32,
|
||||
float64: f64,
|
||||
ptr: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct JsValue {
|
||||
u: JsValueUnion,
|
||||
tag: i64,
|
||||
}
|
||||
|
||||
static mut GLOBAL_INSTANCE: Option<*mut c_void> = None;
|
||||
static mut GLOBAL_CTX: Option<*mut c_void> = None;
|
||||
static COMPOSER_LOADER_DATA: Mutex<Cell<Option<Box<String>>>> = Mutex::new(Cell::new(None));
|
||||
|
||||
static mut JS_EVAL_ORIGINAL2: Option<unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_void, *mut u8, usize, *const u8, u32) -> JsValue> = None;
|
||||
|
||||
def_hook!(
|
||||
js_eval,
|
||||
*mut c_void,
|
||||
|arg0: *mut c_void, arg1: *mut c_void, arg2: *mut c_void, arg3: *const u8, arg4: *const u8, arg5: *const u8, arg6: *mut c_void, arg7: u32| {
|
||||
let mut arg3 = arg3;
|
||||
let mut arg4 = arg4;
|
||||
let mut arg5 = arg5;
|
||||
|
||||
if GLOBAL_INSTANCE.is_none() || GLOBAL_CTX.is_none() {
|
||||
GLOBAL_INSTANCE = Some(arg0);
|
||||
GLOBAL_CTX = Some(arg1);
|
||||
|
||||
let mut loader_data = COMPOSER_LOADER_DATA.lock().unwrap();
|
||||
let mut loader_data = loader_data.get_mut();
|
||||
|
||||
let loader_data = loader_data.as_mut().unwrap();
|
||||
loader_data.push_str("\n");
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
loader_data.push_str(CStr::from_ptr(arg3).to_str().unwrap());
|
||||
arg3 = loader_data.as_mut_ptr();
|
||||
arg4 = loader_data.len() as *const u8;
|
||||
}
|
||||
|
||||
// On arm the original JS_Eval function is inlined so the arguments are shifted
|
||||
#[cfg(target_arch = "arm")]
|
||||
{
|
||||
loader_data.push_str(CStr::from_ptr(arg4).to_str().unwrap());
|
||||
arg4 = loader_data.as_mut_ptr();
|
||||
arg5 = loader_data.len() as *const u8;
|
||||
}
|
||||
|
||||
debug!("injected composer loader!");
|
||||
} else {
|
||||
COMPOSER_LOADER_DATA.lock().unwrap().take();
|
||||
}
|
||||
|
||||
js_eval_original.unwrap()(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
}
|
||||
);
|
||||
|
||||
pub fn set_composer_loader(mut env: JNIEnv, _: *mut c_void, code: JString) {
|
||||
let new_code = get_jni_string(&mut env, code).expect("Failed to get code");
|
||||
|
||||
COMPOSER_LOADER_DATA.lock().unwrap().replace(Some(Box::new(new_code)));
|
||||
}
|
||||
|
||||
#[allow(unreachable_code, unused_variables)]
|
||||
pub unsafe fn composer_eval(env: JNIEnv, _: *mut c_void, script: JString) -> jobject {
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
{
|
||||
return env.new_string("Architecture not supported").unwrap().into_raw();
|
||||
}
|
||||
|
||||
let mut env = env;
|
||||
|
||||
let script_str = get_jni_string(&mut env, script).expect("Failed to get script");
|
||||
let script_length = script_str.len();
|
||||
|
||||
let js_value = JS_EVAL_ORIGINAL2.expect("No js eval found")(
|
||||
GLOBAL_INSTANCE.expect("No global instance found"),
|
||||
GLOBAL_CTX.expect("No global context found"),
|
||||
std::ptr::null_mut(),
|
||||
(script_str + "\0").as_ptr() as *mut u8,
|
||||
script_length,
|
||||
"<eval>\0".as_ptr(),
|
||||
0
|
||||
);
|
||||
|
||||
let result: String = if js_value.tag == JS_TAG_STRING {
|
||||
let string = js_value.u.ptr as *mut JsString;
|
||||
CStr::from_ptr((*string).str8.as_ptr() as *const u8).to_str().unwrap().into()
|
||||
} else if js_value.tag == JS_TAG_INT {
|
||||
js_value.u.int32.to_string()
|
||||
} else if js_value.tag == JS_TAG_BOOL {
|
||||
if js_value.u.int32 == 1 { "true" } else { "false" }.into()
|
||||
} else if js_value.tag == JS_TAG_NULL {
|
||||
"null".into()
|
||||
} else if js_value.tag == JS_TAG_UNDEFINED {
|
||||
"undefined".into()
|
||||
} else if js_value.tag == JS_TAG_OBJECT {
|
||||
"[object]".into()
|
||||
} else if js_value.tag == JS_TAG_FLOAT64 {
|
||||
js_value.u.float64.to_string()
|
||||
} else if js_value.tag == JS_TAG_EXCEPTION {
|
||||
"Failed to evaluate script".into()
|
||||
} else {
|
||||
"[unknown tag ".to_owned() + &js_value.tag.to_string() + "]".into()
|
||||
};
|
||||
|
||||
env.new_string(result).unwrap().into_raw()
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
if !config::native_config().composer_hooks {
|
||||
return
|
||||
}
|
||||
|
||||
if let Some(signature) = sig::find_signature(
|
||||
&common::CLIENT_MODULE,
|
||||
"00 E4 00 6F 29 00 80 52 76 00 04 8B", -0x28,
|
||||
"A1 B0 07 92 81 46", -0x7
|
||||
) {
|
||||
dobby_hook!(signature as *mut c_void, js_eval);
|
||||
|
||||
unsafe {
|
||||
JS_EVAL_ORIGINAL2 = Some(std::mem::transmute(js_eval_original.unwrap()));
|
||||
}
|
||||
|
||||
debug!("js_eval {:#x}", signature);
|
||||
} else {
|
||||
warn!("Unable to find js_eval signature");
|
||||
}
|
||||
}
|
||||
|
0
native/rust/src/modules/custom_font_hook.rs
Normal file
0
native/rust/src/modules/custom_font_hook.rs
Normal file
41
native/rust/src/modules/duplex_hook.rs
Normal file
41
native/rust/src/modules/duplex_hook.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use std::ffi::c_void;
|
||||
|
||||
use jni::{objects::JObject, sys::jboolean, JNIEnv};
|
||||
|
||||
use crate::{common, def_hook, dobby_hook, util::get_jni_string};
|
||||
|
||||
|
||||
def_hook!(
|
||||
is_same_object,
|
||||
jboolean,
|
||||
|env: JNIEnv, obj1: JObject, obj2: JObject| {
|
||||
let mut env = env;
|
||||
|
||||
if obj1.is_null() || obj2.is_null() {
|
||||
return is_same_object_original.unwrap()(env, obj1, obj2);
|
||||
}
|
||||
|
||||
let class = env.find_class("java/lang/Class").unwrap();
|
||||
|
||||
if !env.is_instance_of(&obj1, class).unwrap() {
|
||||
return is_same_object_original.unwrap()(env, obj1, obj2);
|
||||
}
|
||||
|
||||
let obj1_class_name = env.call_method(&obj1, "getName", "()Ljava/lang/String;", &[]).unwrap().l().unwrap().into();
|
||||
let class_name = get_jni_string(&mut env, obj1_class_name).expect("Failed to get class name");
|
||||
|
||||
if class_name.contains("com.snapchat.client.duplex.MessageHandler") {
|
||||
debug!("is_same_object hook: MessageHandler");
|
||||
return 0;
|
||||
}
|
||||
|
||||
is_same_object_original.unwrap()(env, obj1, obj2)
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
pub fn init() {
|
||||
common::attach_jni_env(|env| {
|
||||
dobby_hook!((**env.get_native_interface()).IsSameObject.unwrap() as *mut c_void, is_same_object);
|
||||
});
|
||||
}
|
37
native/rust/src/modules/fstat_hook.rs
Normal file
37
native/rust/src/modules/fstat_hook.rs
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
use std::fs;
|
||||
|
||||
use nix::libc;
|
||||
|
||||
use crate::{config::{self, native_config}, def_hook, dobby_hook_sym};
|
||||
|
||||
def_hook!(
|
||||
fstat_hook,
|
||||
i32,
|
||||
|fd: i32, statbuf: *mut libc::stat| {
|
||||
if let Ok(link) = fs::read_link("/proc/self/fd/".to_owned() + &fd.to_string()) {
|
||||
if let Some(filename) = link.file_name().map(|t| t.to_string_lossy()) {
|
||||
let config = native_config();
|
||||
if config.disable_metrics && filename.contains("files/blizzardv2/queues") {
|
||||
if libc::unlink((filename.to_owned() + "\0").as_ptr()) == -1 {
|
||||
warn!("Failed to unlink {}", filename);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if config.disable_bitmoji && filename.contains("com.snap.file_manager_4_SCContent") {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fstat_hook_original.unwrap()(fd, statbuf)
|
||||
}
|
||||
);
|
||||
|
||||
pub fn init() {
|
||||
let config = config::native_config();
|
||||
if config.disable_metrics || config.disable_bitmoji {
|
||||
dobby_hook_sym!("libc.so", "fstat", fstat_hook);
|
||||
}
|
||||
}
|
60
native/rust/src/modules/linker_hook.rs
Normal file
60
native/rust/src/modules/linker_hook.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use std::{collections::HashMap, ffi::{c_void, CStr}, sync::Mutex};
|
||||
|
||||
use jni::{objects::{JByteArray, JString}, JNIEnv};
|
||||
use nix::libc;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{def_hook, dobby_hook_sym};
|
||||
|
||||
static SHARED_LIBRARIES: Lazy<Mutex<HashMap<String, Box<Vec<i8>>>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
def_hook!(
|
||||
linker_openat,
|
||||
i32,
|
||||
|dir_fd: i32, pathname: *mut u8, flags: i32, mode: i32| {
|
||||
let pathname_str = CStr::from_ptr(pathname).to_str().unwrap().to_string();
|
||||
|
||||
if let Some(content) = SHARED_LIBRARIES.lock().unwrap().remove(&pathname_str) {
|
||||
let memfd = libc::syscall(libc::SYS_memfd_create, "me.rhunk.snapenhance\0".as_ptr(), 0) as i32;
|
||||
let content = content.into_boxed_slice();
|
||||
|
||||
if libc::write(memfd, content.as_ptr() as *const c_void, content.len() as libc::size_t) == -1 {
|
||||
panic!("failed to write to memfd");
|
||||
}
|
||||
|
||||
if libc::lseek(memfd, 0, libc::SEEK_SET) == -1 {
|
||||
panic!("failed to seek memfd");
|
||||
}
|
||||
|
||||
std::mem::forget(content);
|
||||
|
||||
info!("opened shared library: {}", pathname_str);
|
||||
return memfd;
|
||||
}
|
||||
|
||||
linker_openat_original.unwrap()(dir_fd, pathname, flags, mode)
|
||||
}
|
||||
);
|
||||
|
||||
pub fn add_linker_shared_library(mut env: JNIEnv, _: *mut c_void, path: JString, content: JByteArray) {
|
||||
let path = env.get_string(&path).unwrap().to_str().unwrap().to_string();
|
||||
let content_length = env.get_array_length(&content).expect("Failed to get array length");
|
||||
let mut content_buffer = Box::new(vec![0i8; content_length as usize]);
|
||||
|
||||
env.get_byte_array_region(content, 0, content_buffer.as_mut_slice()).expect("Failed to get byte array region");
|
||||
|
||||
debug!("added shared library: {}", path);
|
||||
|
||||
SHARED_LIBRARIES.lock().unwrap().insert(path, content_buffer);
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
dobby_hook_sym!("linker64", "__dl___openat", linker_openat);
|
||||
}
|
||||
#[cfg(target_arch = "arm")]
|
||||
{
|
||||
dobby_hook_sym!("linker", "__dl___openat", linker_openat);
|
||||
}
|
||||
}
|
6
native/rust/src/modules/mod.rs
Normal file
6
native/rust/src/modules/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub mod linker_hook;
|
||||
pub mod duplex_hook;
|
||||
pub mod sqlite_hook;
|
||||
pub mod fstat_hook;
|
||||
pub mod unary_call_hook;
|
||||
pub mod composer_hook;
|
81
native/rust/src/modules/sqlite_hook.rs
Normal file
81
native/rust/src/modules/sqlite_hook.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use std::{collections::HashMap, ffi::{c_void, CStr}, mem::size_of, ptr::addr_of_mut, sync::Mutex};
|
||||
|
||||
use jni::{objects::{JObject, JString}, JNIEnv};
|
||||
use nix::libc::{self, pthread_mutex_t};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{common, def_hook, dobby_hook, sig, util::get_jni_string};
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Sqlite3Mutex {
|
||||
mutex: pthread_mutex_t
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Sqlite3 {
|
||||
pad: [u8; 3 * size_of::<usize>()],
|
||||
mutex: *mut Sqlite3Mutex
|
||||
}
|
||||
|
||||
static SQLITE3_MUTEX_MAP: Lazy<Mutex<HashMap<String, pthread_mutex_t>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
def_hook!(
|
||||
sqlite3_open,
|
||||
i32,
|
||||
|filename: *const u8, pp_db: *mut *mut Sqlite3, flags: u32, z_vfs: *const i8| {
|
||||
let result = sqlite3_open_original.unwrap()(filename, pp_db, flags, z_vfs);
|
||||
|
||||
if result == 0 {
|
||||
let sqlite3_mutex = (**pp_db).mutex;
|
||||
|
||||
if sqlite3_mutex != std::ptr::null_mut() {
|
||||
let filename = CStr::from_ptr(filename).to_string_lossy().to_string().split("/").last().expect("Failed to get filename").to_string();
|
||||
debug!("sqlite3_open hook {:?}", filename);
|
||||
|
||||
SQLITE3_MUTEX_MAP.lock().unwrap().insert(
|
||||
filename,
|
||||
(*sqlite3_mutex).mutex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
pub fn lock_database(mut env: JNIEnv, _: *mut c_void, filename: JString, runnable: JObject) {
|
||||
let database_filename = get_jni_string(&mut env, filename).expect("Failed to get database filename");
|
||||
let mutex = SQLITE3_MUTEX_MAP.lock().unwrap().get(&database_filename).map(|mutex| *mutex);
|
||||
|
||||
if let Some(mut mutex) = mutex {
|
||||
if unsafe { libc::pthread_mutex_lock(addr_of_mut!(mutex)) } != 0 {
|
||||
error!("pthread_mutex_lock failed");
|
||||
return;
|
||||
}
|
||||
|
||||
env.call_method(runnable, "run", "()V", &[]).expect("Failed to call run method");
|
||||
|
||||
if unsafe { libc::pthread_mutex_unlock(addr_of_mut!(mutex)) } != 0 {
|
||||
error!("pthread_mutex_unlock failed");
|
||||
}
|
||||
} else {
|
||||
warn!("No mutex found for database: {}", database_filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn init() {
|
||||
if let Some(signature) = sig::find_signature(
|
||||
&common::CLIENT_MODULE,
|
||||
"FF FF 00 A9 3F 00 00 F9", -0x3C,
|
||||
"9A 46 90 46 78 44 89 46 05 68",-0xd
|
||||
) {
|
||||
debug!("Found sqlite3_open signature: {:#x}", signature);
|
||||
dobby_hook!(signature as *mut c_void, sqlite3_open);
|
||||
} else {
|
||||
warn!("Failed to find sqlite3_open signature");
|
||||
}
|
||||
}
|
123
native/rust/src/modules/unary_call_hook.rs
Normal file
123
native/rust/src/modules/unary_call_hook.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use std::ffi::{c_void, CStr};
|
||||
|
||||
use jni::{objects::{JByteArray, JMethodID, JValue}, signature::ReturnType};
|
||||
use nix::libc;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use crate::{common::{self}, def_hook, dobby_hook, sig};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct RefCountedSliceByteBuffer {
|
||||
ref_counter: *mut c_void,
|
||||
length: usize,
|
||||
data: *mut u8
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct GrpcByteBuffer {
|
||||
reserved: *mut c_void,
|
||||
type_: *mut c_void,
|
||||
compression: *mut c_void,
|
||||
slice_buffer: *mut RefCountedSliceByteBuffer
|
||||
}
|
||||
|
||||
static NATIVE_LIB_ON_UNARY_CALL_METHOD: OnceCell<JMethodID> = OnceCell::new();
|
||||
|
||||
def_hook!(
|
||||
unary_call,
|
||||
*mut c_void,
|
||||
|unk1: *mut c_void, uri: *const u8, grpc_byte_buffer: *mut *mut GrpcByteBuffer, unk4: *mut c_void, unk5: *mut c_void, unk6: *mut c_void| {
|
||||
macro_rules! call_original {
|
||||
() => {
|
||||
unary_call_original.unwrap()(unk1, uri, grpc_byte_buffer, unk4, unk5, unk6)
|
||||
};
|
||||
}
|
||||
|
||||
// make a local copy of the slice buffer
|
||||
let mut slice_buffer = *(**grpc_byte_buffer).slice_buffer;
|
||||
|
||||
if slice_buffer.ref_counter.is_null() {
|
||||
return call_original!();
|
||||
}
|
||||
|
||||
let java_vm = common::java_vm();
|
||||
let mut env = java_vm.get_env().expect("Failed to get JNIEnv");
|
||||
|
||||
let slice_buffer_length = slice_buffer.length as usize;
|
||||
let jni_buffer = env.new_byte_array(slice_buffer_length as i32).expect("Failed to create new byte array");
|
||||
env.set_byte_array_region(&jni_buffer, 0, std::slice::from_raw_parts(slice_buffer.data as *const i8, slice_buffer_length)).expect("Failed to set byte array region");
|
||||
|
||||
let uri_str = CStr::from_ptr(uri).to_str().unwrap();
|
||||
|
||||
let native_request_data_object = env.call_method_unchecked(
|
||||
common::native_lib_instance(),
|
||||
NATIVE_LIB_ON_UNARY_CALL_METHOD.get().unwrap(),
|
||||
ReturnType::Object,
|
||||
&[
|
||||
JValue::from(&env.new_string(uri_str).unwrap()).as_jni(),
|
||||
JValue::from(&jni_buffer).as_jni()
|
||||
]
|
||||
).expect("Failed to call onNativeUnaryCall method").l().unwrap();
|
||||
|
||||
if native_request_data_object.is_null() {
|
||||
return call_original!();
|
||||
}
|
||||
|
||||
let is_canceled = env.get_field(&native_request_data_object, "canceled", "Z").expect("Failed to get canceled field").z().unwrap();
|
||||
|
||||
if is_canceled {
|
||||
info!("canceled request for {}", uri_str);
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let new_buffer: JByteArray = env.get_field(&native_request_data_object, "buffer", "[B").expect("Failed to get buffer field").l().unwrap().into();
|
||||
let new_buffer_length = env.get_array_length(&new_buffer).expect("Failed to get array length") as usize;
|
||||
|
||||
let mut new_buffer_data = Box::new(vec![0i8; new_buffer_length]);
|
||||
env.get_byte_array_region(&new_buffer, 0, new_buffer_data.as_mut_slice()).expect("Failed to get byte array region");
|
||||
|
||||
let ref_counter_struct_size = (slice_buffer.data as usize) - (slice_buffer.ref_counter as usize);
|
||||
|
||||
//we need to allocate a new ref_counter struct and copy the old ref_counter and the new_buffer to it
|
||||
let new_ref = {
|
||||
let new_ref = libc::malloc(ref_counter_struct_size + new_buffer_length) as *mut c_void;
|
||||
libc::memcpy(new_ref, slice_buffer.ref_counter, ref_counter_struct_size);
|
||||
libc::memcpy(new_ref.offset(ref_counter_struct_size as isize), new_buffer_data.as_ptr() as *const c_void, new_buffer_length);
|
||||
libc::free(slice_buffer.ref_counter);
|
||||
new_ref
|
||||
};
|
||||
|
||||
slice_buffer.ref_counter = new_ref;
|
||||
slice_buffer.length = new_buffer_length;
|
||||
slice_buffer.data = new_ref.offset(ref_counter_struct_size as isize) as *mut u8;
|
||||
|
||||
// update the grpc byte buffer
|
||||
*(**grpc_byte_buffer).slice_buffer = slice_buffer;
|
||||
|
||||
debug!("unary_call {}", uri_str);
|
||||
|
||||
call_original!()
|
||||
}
|
||||
);
|
||||
|
||||
pub fn init() {
|
||||
if let Some(signature) = sig::find_signature(
|
||||
&common::CLIENT_MODULE,
|
||||
"A8 03 1F F8 C2 00 00 94", -0x48,
|
||||
"0A 90 00 F0 3F F9", -0x37
|
||||
) {
|
||||
dobby_hook!(signature as *mut c_void, unary_call);
|
||||
common::attach_jni_env(|env| {
|
||||
NATIVE_LIB_ON_UNARY_CALL_METHOD.set(
|
||||
env.get_method_id(
|
||||
env.get_object_class(common::native_lib_instance()).unwrap(),
|
||||
"onNativeUnaryCall",
|
||||
"(Ljava/lang/String;[B)Lme/rhunk/snapenhance/nativelib/NativeRequestData;"
|
||||
).expect("Failed to get onNativeUnaryCall method id")
|
||||
).expect("unary call method already set");
|
||||
});
|
||||
} else {
|
||||
error!("Can't find unaryCall signature");
|
||||
}
|
||||
}
|
99
native/rust/src/sig.rs
Normal file
99
native/rust/src/sig.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use procfs::process::MMPermissions;
|
||||
|
||||
use crate::mapped_lib::MappedLib;
|
||||
|
||||
|
||||
static SIGNATURE_CACHE: Mutex<Vec<(String, Vec<usize>)>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn add_signatures(signatures: Vec<(String, Vec<usize>)>) {
|
||||
SIGNATURE_CACHE.lock().unwrap().extend(signatures);
|
||||
}
|
||||
|
||||
pub fn get_signatures() -> Vec<(String, Vec<usize>)> {
|
||||
SIGNATURE_CACHE.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn find_signatures(module_base: usize, size: usize, pattern: &str, once: bool) -> Vec<usize> {
|
||||
let mut results = Vec::new();
|
||||
let mut bytes = Vec::new();
|
||||
let mut mask = Vec::new();
|
||||
let mut i = 0;
|
||||
|
||||
if let Some(cache) = SIGNATURE_CACHE.lock().unwrap().iter().find(|(sig, _)| sig == pattern) {
|
||||
return cache.1.clone().into_iter().map(|offset| module_base + offset).collect();
|
||||
}
|
||||
|
||||
while i < pattern.len() {
|
||||
if pattern.chars().nth(i).unwrap() == '?' {
|
||||
bytes.push(0);
|
||||
mask.push('?');
|
||||
} else {
|
||||
bytes.push(u8::from_str_radix(&pattern[i..i+2], 16).unwrap());
|
||||
mask.push('x');
|
||||
}
|
||||
i += 3;
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < size {
|
||||
let mut found = true;
|
||||
let mut j = 0;
|
||||
|
||||
while j < bytes.len() {
|
||||
if mask[j] == '?' || bytes[j] == unsafe { *(module_base as *const u8).offset(i as isize + j as isize) } {
|
||||
j += 1;
|
||||
continue;
|
||||
}
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
if found {
|
||||
if once {
|
||||
SIGNATURE_CACHE.lock().unwrap().push((pattern.to_string(), vec![i]));
|
||||
return vec![module_base + i];
|
||||
}
|
||||
results.push(module_base + i);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
SIGNATURE_CACHE.lock().unwrap().push((pattern.to_string(), results.clone()));
|
||||
results
|
||||
}
|
||||
|
||||
pub fn find_signature_executable(mapped_lib: &MappedLib, pattern: &str) -> Option<usize> {
|
||||
let executable_regions = mapped_lib.regions.iter().filter(|region| {
|
||||
region.perms.contains(MMPermissions::EXECUTE) && region.perms.contains(MMPermissions::READ)
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
for region in executable_regions {
|
||||
let size = (region.end - region.start) as usize;
|
||||
let module_base = region.start as usize;
|
||||
|
||||
if size > 0 {
|
||||
let results = find_signatures(module_base, size, pattern, true);
|
||||
|
||||
if results.is_empty() {
|
||||
warn!("Signature not found in region: {:#x} - {:#x}", region.start, region.end);
|
||||
} else {
|
||||
debug!("Found {} results in region: {:#x} - {:#x}", results.len(), region.start, region.end);
|
||||
return Some(results[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_signature(mapped_lib: &MappedLib, _arm64_pattern: &str, _arm64_offset: i64, _arm32_pattern: &str, _arm32_offset: i64) -> Option<usize> {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
return find_signature_executable(mapped_lib, _arm64_pattern).map(|address| (address as i64 + _arm64_offset) as usize);
|
||||
}
|
||||
#[cfg(target_arch = "arm")]
|
||||
{
|
||||
return find_signature_executable(mapped_lib, _arm32_pattern).map(|address| (address as i64 + _arm32_offset) as usize);
|
||||
}
|
||||
}
|
8
native/rust/src/util.rs
Normal file
8
native/rust/src/util.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use std::error::Error;
|
||||
|
||||
use jni::{objects::JString, JNIEnv};
|
||||
|
||||
pub fn get_jni_string(env: &mut JNIEnv, obj: JString) -> Result<String, Box<dyn Error>> {
|
||||
let string = env.get_string(&obj)?;
|
||||
Ok(string.to_str()?.to_string())
|
||||
}
|
@ -5,9 +5,9 @@ import android.util.Log
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.random.Random
|
||||
|
||||
@Suppress("KotlinJniMissingFunction")
|
||||
class NativeLib {
|
||||
var nativeUnaryCallCallback: (NativeRequestData) -> Unit = {}
|
||||
var signatureCache: String? = null
|
||||
|
||||
companion object {
|
||||
var initialized = false
|
||||
@ -21,9 +21,7 @@ class NativeLib {
|
||||
initialized = true
|
||||
callback(this)
|
||||
return@runCatching {
|
||||
if (!init()) {
|
||||
throw IllegalStateException("NativeLib init failed. Check logcat for more info")
|
||||
}
|
||||
signatureCache = init(signatureCache) ?: throw IllegalStateException("NativeLib init failed. Check logcat for more info")
|
||||
}
|
||||
}.onFailure {
|
||||
initialized = false
|
||||
@ -67,7 +65,7 @@ class NativeLib {
|
||||
System.load(generatedPath)
|
||||
}
|
||||
|
||||
private external fun init(): Boolean
|
||||
private external fun init(signatureCache: String?): String?
|
||||
private external fun loadConfig(config: NativeConfig)
|
||||
private external fun lockDatabase(name: String, callback: Runnable)
|
||||
external fun setComposerLoader(code: String)
|
||||
|
Loading…
x
Reference in New Issue
Block a user