reduce memory allocation && properly close fds

This commit is contained in:
kotori0 2022-02-02 18:18:42 +08:00 committed by LoveSy
parent d3b0140230
commit 9760e8d733
9 changed files with 133 additions and 81 deletions

View File

@ -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, "<init>",
"(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");

View File

@ -110,8 +110,6 @@ namespace lspd {
std::size_t size_;
};
PreloadedDex dex_{};
Context() {}
void LoadDex(JNIEnv *env, int fd, size_t size);

View File

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

View File

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

View File

@ -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<void*, size_t> 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, "<init>", "(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, "<init>", "(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<uint8_t>;
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::u1*>(dex), size};
dex::Reader reader{reinterpret_cast<const dex::u1*>(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<jobject> 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<jint>(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<FILE, decltype(&fclose)> 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();
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
#pragma once
#include <string>
#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<void*, std::pair<size_t, int>> 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;
}
};

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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);
}

View File

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

View File

@ -98,6 +98,7 @@ public class ServiceManager {
logcatService.start();
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
ObfuscationManager.init();
ObfuscationManager.getObfuscatedSignature();
ObfuscationManager.preloadDex();
Looper.prepareMainLooper();