From 9760e8d7332aa23c2b3671be184a8d7ed3dd3742 Mon Sep 17 00:00:00 2001 From: kotori0 Date: Wed, 2 Feb 2022 18:18:42 +0800 Subject: [PATCH] reduce memory allocation && properly close fds --- core/src/main/cpp/main/src/context.cpp | 12 +- core/src/main/cpp/main/src/context.h | 2 - core/src/main/cpp/main/src/service.cpp | 6 +- core/src/main/cpp/main/src/service.h | 2 +- daemon/src/main/cpp/obfuscation.cpp | 108 +++++++----------- daemon/src/main/cpp/obfuscation.h | 55 +++++++++ .../lspd/service/ConfigFileManager.java | 26 ++++- .../lspd/service/ObfuscationManager.java | 2 + .../lsposed/lspd/service/ServiceManager.java | 1 + 9 files changed, 133 insertions(+), 81 deletions(-) create mode 100644 daemon/src/main/cpp/obfuscation.h diff --git a/core/src/main/cpp/main/src/context.cpp b/core/src/main/cpp/main/src/context.cpp index e95c31c8..f90bab6e 100644 --- a/core/src/main/cpp/main/src/context.cpp +++ b/core/src/main/cpp/main/src/context.cpp @@ -82,7 +82,7 @@ namespace lspd { void Context::LoadDex(JNIEnv *env, int fd, size_t size) { LOGD("Context::LoadDex: %d", fd); // map fd to memory. fd should be created with ASharedMemory_create. - dex_ = PreloadedDex(fd, size); // for RAII... + auto dex = PreloadedDex(fd, size); // for RAII... auto classloader = JNI_FindClass(env, "java/lang/ClassLoader"); auto getsyscl_mid = JNI_GetStaticMethodID( @@ -96,7 +96,7 @@ namespace lspd { auto initMid = JNI_GetMethodID(env, in_memory_classloader, "", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); auto byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer"); - auto dex_buffer = env->NewDirectByteBuffer(dex_.data(), dex_.size()); + auto dex_buffer = env->NewDirectByteBuffer(dex.data(), dex.size()); if (auto my_cl = JNI_NewObject(env, in_memory_classloader, initMid, dex_buffer, sys_classloader)) { inject_class_loader_ = JNI_NewGlobalRef(env, my_cl); @@ -199,7 +199,9 @@ namespace lspd { // Call application_binder directly if application binder is available, // or we proxy the request from system server binder auto dex = instance->RequestLSPDex(env, application_binder ? application_binder : system_server_binder); - LoadDex(env, std::get<0>(dex), std::get<1>(dex)); + auto dex_fd = std::get<0>(dex); + LoadDex(env, dex_fd, std::get<1>(dex)); + close(dex_fd); instance->HookBridge(*this, env); if (application_binder) { @@ -264,7 +266,9 @@ namespace lspd { if (binder) { InstallInlineHooks(); auto dex = instance->RequestLSPDex(env, binder); - LoadDex(env, std::get<0>(dex), std::get<1>(dex)); + auto dex_fd = std::get<0>(dex); + LoadDex(env, dex_fd, std::get<1>(dex)); + close(dex_fd); obfuscated_signature_ = std::move(std::get<2>(dex)); Init(env); LOGD("Done prepare"); diff --git a/core/src/main/cpp/main/src/context.h b/core/src/main/cpp/main/src/context.h index 98ee066b..ef36f957 100644 --- a/core/src/main/cpp/main/src/context.h +++ b/core/src/main/cpp/main/src/context.h @@ -110,8 +110,6 @@ namespace lspd { std::size_t size_; }; - PreloadedDex dex_{}; - Context() {} void LoadDex(JNIEnv *env, int fd, size_t size); diff --git a/core/src/main/cpp/main/src/service.cpp b/core/src/main/cpp/main/src/service.cpp index ba80aac5..15783626 100644 --- a/core/src/main/cpp/main/src/service.cpp +++ b/core/src/main/cpp/main/src/service.cpp @@ -133,7 +133,7 @@ namespace lspd { LOGE("ParcelFileDescriptor not found"); return; } - get_fd_method = JNI_GetMethodID(env, parcel_file_descriptor_class_, "getFd", "()I"); + detach_fd_method_ = JNI_GetMethodID(env, parcel_file_descriptor_class_, "detachFd", "()I"); if (auto dead_object_exception_class = JNI_FindClass(env, "android/os/DeadObjectException")) { @@ -299,8 +299,6 @@ namespace lspd { if (res) { JNI_CallVoidMethod(env, reply, read_exception_method_); app_binder = JNI_CallObjectMethod(env, reply, read_strong_binder_method_); - } else { - LOGE("Service::RequestSystemServerBinder binder.transact failed?"); } JNI_CallVoidMethod(env, data, recycleMethod_); JNI_CallVoidMethod(env, reply, recycleMethod_); @@ -323,7 +321,7 @@ namespace lspd { return {-1, 0, ""}; } auto parcel_fd = JNI_CallObjectMethod(env, reply, read_file_descriptor_method_); - int fd = JNI_CallIntMethod(env, parcel_fd, get_fd_method); + int fd = JNI_CallIntMethod(env, parcel_fd, detach_fd_method_); auto size = JNI_CallLongMethod(env, reply, read_long_method_); auto signature = JNI_CallObjectMethod(env, reply, read_string_method_); JNI_CallVoidMethod(env, data, recycleMethod_); diff --git a/core/src/main/cpp/main/src/service.h b/core/src/main/cpp/main/src/service.h index 8fe40886..1bd89fac 100644 --- a/core/src/main/cpp/main/src/service.h +++ b/core/src/main/cpp/main/src/service.h @@ -103,7 +103,7 @@ namespace lspd { jmethodID read_string_method_ = nullptr; jclass parcel_file_descriptor_class_ = nullptr; - jmethodID get_fd_method = nullptr; + jmethodID detach_fd_method_ = nullptr; jclass deadObjectExceptionClass_ = nullptr; diff --git a/daemon/src/main/cpp/obfuscation.cpp b/daemon/src/main/cpp/obfuscation.cpp index 4dc88eb9..51924cf1 100644 --- a/daemon/src/main/cpp/obfuscation.cpp +++ b/daemon/src/main/cpp/obfuscation.cpp @@ -33,24 +33,25 @@ #include "slicer/reader.h" #include "slicer/writer.h" #include "config.h" -#include "jni_helper.h" +#include "obfuscation.h" -class WA: public dex::Writer::Allocator { - std::unordered_map allocated_; -public: - void* Allocate(size_t size) override { - auto *mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - allocated_[mem] = size; - return mem; - } - void Free(void* ptr) override { - munmap(ptr, allocated_[ptr]); - allocated_.erase(ptr); - } -}; +extern "C" +JNIEXPORT void JNICALL +Java_org_lsposed_lspd_service_ObfuscationManager_init(JNIEnv *env, jclass ) { + LOGD("ObfuscationManager.init"); + if (auto file_descriptor = JNI_FindClass(env, "java/io/FileDescriptor")) { + class_file_descriptor = JNI_NewGlobalRef(env, file_descriptor); + } else return; -static std::string obfuscated_signature; -static const std::string old_signature = "Lde/robv/android/xposed"; + method_file_descriptor_ctor = JNI_GetMethodID(env, class_file_descriptor, "", "(I)V"); + + if (auto shared_memory = JNI_FindClass(env, "android/os/SharedMemory")) { + class_shared_memory = JNI_NewGlobalRef(env, shared_memory); + } else return; + + method_shared_memory_ctor = JNI_GetMethodID(env, class_shared_memory, "", "(Ljava/io/FileDescriptor;)V"); + LOGD("ObfuscationManager init successfully"); +} extern "C" JNIEXPORT jstring JNICALL @@ -84,10 +85,9 @@ Java_org_lsposed_lspd_service_ObfuscationManager_getObfuscatedSignature(JNIEnv * return env->NewStringUTF(obfuscated_signature.c_str()); } -using ustring = std::basic_string; -ustring obfuscateDex(void *dex, size_t size) { +int obfuscateDex(const void *dex, size_t size) { const char* new_sig = obfuscated_signature.c_str(); - dex::Reader reader{reinterpret_cast(dex), size}; + dex::Reader reader{reinterpret_cast(dex), size}; reader.CreateFullIr(); auto ir = reader.GetIr(); @@ -102,29 +102,16 @@ ustring obfuscateDex(void *dex, size_t size) { size_t new_size; WA allocator; - auto *p_dex = writer.CreateImage(&allocator, &new_size); - ustring new_dex(p_dex, new_size); - allocator.Free(p_dex); - return new_dex; + auto *p_dex = writer.CreateImage(&allocator, &new_size); // allocates memory only once + return allocator.GetFd(p_dex); } -ScopedLocalRef new_sharedmem(JNIEnv* env, jint size) { - auto clazz = JNI_FindClass(env, "android/os/SharedMemory"); - auto mid = JNI_GetStaticMethodID(env, clazz, "create", "(Ljava/lang/String;I)Landroid/os/SharedMemory;"); - auto empty_str = JNI_NewStringUTF(env, ""); - auto new_mem = JNI_CallStaticObjectMethod(env, clazz, mid, empty_str, static_cast(size)); - return new_mem; -} - -static jobject lspdDex = nullptr; -static std::mutex dex_lock; - extern "C" JNIEXPORT jint JNICALL -Java_org_lsposed_lspd_service_ObfuscationManager_preloadDex(JNIEnv *env, jclass ) { +Java_org_lsposed_lspd_service_ObfuscationManager_preloadDex(JNIEnv *, jclass ) { using namespace std::string_literals; std::lock_guard lg(dex_lock); - if (lspdDex) return ASharedMemory_dupFromJava(env, lspdDex); + if (lspdDex != -1) return lspdDex; std::string dex_path = "/data/adb/modules/"s + lspd::moduleName + "/" + lspd::kDexPath; std::unique_ptr f{fopen(dex_path.data(), "rb"), &fclose}; @@ -147,25 +134,16 @@ Java_org_lsposed_lspd_service_ObfuscationManager_preloadDex(JNIEnv *env, jclass } auto new_dex = obfuscateDex(addr, size); - LOGD("LSPApplicationService::preloadDex: %p, size=%zu", new_dex.data(), new_dex.size()); - auto new_mem = new_sharedmem(env, new_dex.size()); - lspdDex = JNI_NewGlobalRef(env, new_mem); - auto new_fd = ASharedMemory_dupFromJava(env, lspdDex); - auto new_addr = mmap(nullptr, new_dex.size(), PROT_READ | PROT_WRITE, MAP_SHARED, new_fd, 0); - if (new_addr == MAP_FAILED) { - LOGE("Failed to map new dex to memory?"); - } - memmove(new_addr, new_dex.data(), new_dex.size()); - - return new_fd; + LOGD("LSPApplicationService::preloadDex: %d, size=%zu", new_dex, ASharedMemory_getSize(new_dex)); + lspdDex = new_dex; + return new_dex; } extern "C" JNIEXPORT jlong JNICALL -Java_org_lsposed_lspd_service_ObfuscationManager_getPreloadedDexSize(JNIEnv *env, jclass ) { - if (lspdDex) { - auto fd = ASharedMemory_dupFromJava(env, lspdDex); - return ASharedMemory_getSize(fd); +Java_org_lsposed_lspd_service_ObfuscationManager_getPreloadedDexSize(JNIEnv *, jclass ) { + if (lspdDex != -1) { + return ASharedMemory_getSize(lspdDex); } return 0; } @@ -176,23 +154,19 @@ Java_org_lsposed_lspd_service_ObfuscationManager_obfuscateDex(JNIEnv *env, jclas jobject memory) { int fd = ASharedMemory_dupFromJava(env, memory); auto size = ASharedMemory_getSize(fd); - ustring mem_wrapper; - mem_wrapper.resize(size); - read(fd, mem_wrapper.data(), size); + LOGD("fd=%d, size=%zu", fd, size); - void *mem = mem_wrapper.data(); - - auto new_dex = obfuscateDex(mem, size); - - // create new SharedMem since it cannot be resized - auto new_mem = new_sharedmem(env, new_dex.size()); - int new_fd = ASharedMemory_dupFromJava(env, new_mem.get()); - - mem = mmap(nullptr, new_dex.size(), PROT_READ | PROT_WRITE, MAP_SHARED, new_fd, 0); + const void* mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mem == MAP_FAILED) { - LOGE("Failed to map new dex to memory?"); + LOGE("old dex map failed?"); + return nullptr; } - memcpy(mem, new_dex.data(), new_dex.size()); - ASharedMemory_setProt(fd, PROT_READ); - return new_mem.release(); + + auto new_fd = obfuscateDex(mem, size); + + // construct new shared mem with fd + auto java_fd = JNI_NewObject(env, class_file_descriptor, method_file_descriptor_ctor, new_fd); + auto java_sm = JNI_NewObject(env, class_shared_memory, method_shared_memory_ctor, java_fd); + + return java_sm.release(); } \ No newline at end of file diff --git a/daemon/src/main/cpp/obfuscation.h b/daemon/src/main/cpp/obfuscation.h new file mode 100644 index 00000000..bf283e74 --- /dev/null +++ b/daemon/src/main/cpp/obfuscation.h @@ -0,0 +1,55 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2022 LSPosed Contributors + */ + +#pragma once +#include +#include "jni_helper.h" + +static std::string obfuscated_signature; +static const std::string old_signature = "Lde/robv/android/xposed"; +static int lspdDex = -1; +static std::mutex dex_lock; + +static jclass class_file_descriptor; +static jmethodID method_file_descriptor_ctor; + +static jclass class_shared_memory; +static jmethodID method_shared_memory_ctor; + +class WA: public dex::Writer::Allocator { + // addr: {size, fd} + std::unordered_map> allocated_; +public: + inline void* Allocate(size_t size) override { + auto fd = ASharedMemory_create("", size); + auto *mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + allocated_[mem] = {size, fd}; + return mem; + } + inline void Free(void* ptr) override { + auto alloc_data = allocated_.at(ptr); + close(alloc_data.second); + allocated_.erase(ptr); + } + inline int GetFd(void* ptr) { + auto alloc_data = allocated_.find(ptr); + if (alloc_data == allocated_.end()) return -1; + return (*alloc_data).second.second; + } +}; \ No newline at end of file diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index 8cf4d9e5..d7610f10 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -1,3 +1,22 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2021 - 2022 LSPosed Contributors + */ + package org.lsposed.lspd.service; import static org.lsposed.lspd.service.ServiceManager.TAG; @@ -296,9 +315,10 @@ public class ConfigFileManager { var byteBuffer = memory.mapReadWrite(); Channels.newChannel(in).read(byteBuffer); SharedMemory.unmap(byteBuffer); - memory = ObfuscationManager.obfuscateDex(memory); - memory.setProtect(OsConstants.PROT_READ); - preLoadedDexes.add(memory); + var new_memory = ObfuscationManager.obfuscateDex(memory); + memory.close(); + new_memory.setProtect(OsConstants.PROT_READ); + preLoadedDexes.add(new_memory); } catch (IOException | ErrnoException e) { Log.w(TAG, "Can not load " + dexFile + " in " + apkFile, e); } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ObfuscationManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ObfuscationManager.java index eed0b671..c198c60a 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ObfuscationManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ObfuscationManager.java @@ -3,6 +3,8 @@ package org.lsposed.lspd.service; import android.os.SharedMemory; public class ObfuscationManager { + static native void init(); + // For module dexes static native SharedMemory obfuscateDex(SharedMemory memory); diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java index 90928b0a..dac96b9c 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java @@ -98,6 +98,7 @@ public class ServiceManager { logcatService.start(); Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); + ObfuscationManager.init(); ObfuscationManager.getObfuscatedSignature(); ObfuscationManager.preloadDex(); Looper.prepareMainLooper();