From 5ab4c98fc561ca2adde017a8c9afeb1ce0b93f8d Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 13 Feb 2022 02:47:59 +0800 Subject: [PATCH] Refine obfuscate (#1679) --- daemon/proguard-rules.pro | 3 - daemon/src/main/cpp/obfuscation.cpp | 101 ++++-------------- daemon/src/main/cpp/obfuscation.h | 4 +- .../lspd/service/ConfigFileManager.java | 48 ++++++--- .../lsposed/lspd/service/ConfigManager.java | 29 ++--- .../lspd/service/LSPApplicationService.java | 17 +-- .../lspd/service/LSPManagerService.java | 4 +- .../lspd/service/ObfuscationManager.java | 12 --- .../lsposed/lspd/service/ServiceManager.java | 3 - 9 files changed, 79 insertions(+), 142 deletions(-) diff --git a/daemon/proguard-rules.pro b/daemon/proguard-rules.pro index da5c9d22..39516ba3 100644 --- a/daemon/proguard-rules.pro +++ b/daemon/proguard-rules.pro @@ -11,9 +11,6 @@ public int getUserId(); public android.os.UserHandle getUser(); } --keepclasseswithmembers class org.lsposed.lspd.service.ObfuscationManager { - static boolean enabled(); -} -assumenosideeffects class android.util.Log { public static *** v(...); public static *** d(...); diff --git a/daemon/src/main/cpp/obfuscation.cpp b/daemon/src/main/cpp/obfuscation.cpp index 18052405..aee8b695 100644 --- a/daemon/src/main/cpp/obfuscation.cpp +++ b/daemon/src/main/cpp/obfuscation.cpp @@ -35,16 +35,8 @@ #include "slicer/writer.h" #include "obfuscation.h" -bool obfuscate_enabled(JNIEnv* env, jclass obfuscation_manager) { - auto method_enabled = JNI_GetStaticMethodID(env, obfuscation_manager, "enabled", "()Z"); - auto result = JNI_CallStaticBooleanMethod(env, obfuscation_manager, method_enabled); - return result; -} - -extern "C" -JNIEXPORT void JNICALL -Java_org_lsposed_lspd_service_ObfuscationManager_init(JNIEnv *env, jclass obfuscation_manager) { - if (!obfuscate_enabled(env, obfuscation_manager)) return; +void maybeInit(JNIEnv *env) { + if (inited.test_and_set(std::memory_order_acq_rel)) [[likely]] return; LOGD("ObfuscationManager.init"); if (auto file_descriptor = JNI_FindClass(env, "java/io/FileDescriptor")) { class_file_descriptor = JNI_NewGlobalRef(env, file_descriptor); @@ -57,14 +49,6 @@ Java_org_lsposed_lspd_service_ObfuscationManager_init(JNIEnv *env, jclass obfusc } 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 -Java_org_lsposed_lspd_service_ObfuscationManager_getObfuscatedSignature(JNIEnv *env, jclass obfuscation_manager) { - if (!obfuscate_enabled(env, obfuscation_manager)) return env->NewStringUTF(old_signature.c_str()); - if (!obfuscated_signature.empty()) return env->NewStringUTF(obfuscated_signature.c_str()); auto regen = []() { static auto& chrs = "abcdefghijklmnopqrstuvwxyz" @@ -94,27 +78,34 @@ Java_org_lsposed_lspd_service_ObfuscationManager_getObfuscatedSignature(JNIEnv * auto contains_keyword = [](std::string_view s) -> bool { for (const auto &i: { - "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", - "continue", "const", "default", "do", "double", "else", "enum", "exports", "extends", - "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", - "int", "interface", "long", "module", "native", "new", "package", "private", "protected", - "public", "requires", "return", "short", "static", "strictfp", "super", "switch", - "synchronized", "this", "throw", "throws", "transient", "try", "var", "void", "volatile", - "while"}) { + "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", + "continue", "const", "default", "do", "double", "else", "enum", "exports", "extends", + "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", + "int", "interface", "long", "module", "native", "new", "package", "private", "protected", + "public", "requires", "return", "short", "static", "strictfp", "super", "switch", + "synchronized", "this", "throw", "throws", "transient", "try", "var", "void", "volatile", + "while"}) { if (s.find(i) != std::string::npos) return true; } return false; }; - do { + [[unlikely]] do { obfuscated_signature = regen(); } while (contains_keyword(obfuscated_signature)); LOGD("ObfuscationManager.getObfuscatedSignature: %s", obfuscated_signature.c_str()); + LOGD("ObfuscationManager init successfully"); +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_org_lsposed_lspd_service_ObfuscationManager_getObfuscatedSignature(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager) { + maybeInit(env); return env->NewStringUTF(obfuscated_signature.c_str()); } -int obfuscateDex(const void *dex, size_t size) { +static int obfuscateDex(const void *dex, size_t size) { const char* new_sig = obfuscated_signature.c_str(); dex::Reader reader{reinterpret_cast(dex), size}; @@ -136,63 +127,11 @@ int obfuscateDex(const void *dex, size_t size) { return allocator.GetFd(p_dex); } -extern "C" -JNIEXPORT jint JNICALL -Java_org_lsposed_lspd_service_ObfuscationManager_preloadDex(JNIEnv *env, jclass obfuscation_manager) { - using namespace std::string_literals; - std::lock_guard lg(dex_lock); - if (lspdDex != -1) return lspdDex; - const std::string dex_path = "framework/lspd.dex"; - - std::unique_ptr f{fopen(dex_path.data(), "rb"), &fclose}; - - if (!f) { - LOGE("Fail to open dex from %s", dex_path.data()); - return -1; - } - fseek(f.get(), 0, SEEK_END); - size_t size = ftell(f.get()); - rewind(f.get()); - - LOGD("Loaded %s with size %zu", dex_path.data(), size); - - auto *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(f.get()), 0); - - if (addr == MAP_FAILED) { - PLOGE("Map dex"); - return -1; - } - - int new_dex; - if (!obfuscate_enabled(env, obfuscation_manager)) { - new_dex = ASharedMemory_create("", size); - auto new_addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, new_dex, 0); - memcpy(new_addr, addr, size); - munmap(new_addr, size); - } else { - new_dex = obfuscateDex(addr, size); - } - - munmap(addr, size); - 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 *, jclass ) { - if (lspdDex != -1) { - return ASharedMemory_getSize(lspdDex); - } - return 0; -} - extern "C" JNIEXPORT jobject -Java_org_lsposed_lspd_service_ObfuscationManager_obfuscateDex(JNIEnv *env, jclass obfuscation_manager, +Java_org_lsposed_lspd_service_ObfuscationManager_obfuscateDex(JNIEnv *env, [[maybe_unused]] jclass obfuscation_manager, jobject memory) { - if (!obfuscate_enabled(env, obfuscation_manager)) { return memory; } + maybeInit(env); int fd = ASharedMemory_dupFromJava(env, memory); auto size = ASharedMemory_getSize(fd); LOGD("fd=%d, size=%zu", fd, size); diff --git a/daemon/src/main/cpp/obfuscation.h b/daemon/src/main/cpp/obfuscation.h index 83019e16..cdd57015 100644 --- a/daemon/src/main/cpp/obfuscation.h +++ b/daemon/src/main/cpp/obfuscation.h @@ -23,8 +23,8 @@ 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 std::atomic_flag inited; static jclass class_file_descriptor; static jmethodID method_file_descriptor_ctor; 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 edf4dc83..a756eded 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -44,6 +44,7 @@ import org.lsposed.lspd.util.Utils; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -92,6 +93,7 @@ public class ConfigFileManager { private static FileLocker locker = null; private static Resources res = null; private static ParcelFileDescriptor fd = null; + private static SharedMemory preloadDex = null; static { try { @@ -306,21 +308,28 @@ public class ConfigFileManager { }); } - private static void readDexes(ZipFile apkFile, List preLoadedDexes) { + private static SharedMemory readDex(InputStream in, boolean obfuscate) throws IOException, ErrnoException { + var memory = SharedMemory.create(null, in.available()); + var byteBuffer = memory.mapReadWrite(); + Channels.newChannel(in).read(byteBuffer); + SharedMemory.unmap(byteBuffer); + if (obfuscate) { + var newMemory = ObfuscationManager.obfuscateDex(memory); + if (memory != newMemory) { + memory.close(); + memory = newMemory; + } + } + memory.setProtect(OsConstants.PROT_READ); + return memory; + } + + private static void readDexes(ZipFile apkFile, List preLoadedDexes, boolean obfuscate) { int secondary = 2; for (var dexFile = apkFile.getEntry("classes.dex"); dexFile != null; dexFile = apkFile.getEntry("classes" + secondary + ".dex"), secondary++) { - try (var in = apkFile.getInputStream(dexFile)) { - var memory = SharedMemory.create(null, in.available()); - var byteBuffer = memory.mapReadWrite(); - Channels.newChannel(in).read(byteBuffer); - SharedMemory.unmap(byteBuffer); - var new_memory = ObfuscationManager.obfuscateDex(memory); - if (memory != new_memory) { - memory.close(); - } - new_memory.setProtect(OsConstants.PROT_READ); - preLoadedDexes.add(new_memory); + try (var is = apkFile.getInputStream(dexFile)) { + preLoadedDexes.add(readDex(is, obfuscate)); } catch (IOException | ErrnoException e) { Log.w(TAG, "Can not load " + dexFile + " in " + apkFile, e); } @@ -344,14 +353,14 @@ public class ConfigFileManager { } @Nullable - static PreLoadedApk loadModule(String path) { + static PreLoadedApk loadModule(String path, boolean obfuscate) { if (path == null) return null; var file = new PreLoadedApk(); var preLoadedDexes = new ArrayList(); var moduleClassNames = new ArrayList(1); var moduleLibraryNames = new ArrayList(1); try (var apkFile = new ZipFile(toGlobalNamespace(path))) { - readDexes(apkFile, preLoadedDexes); + readDexes(apkFile, preLoadedDexes, obfuscate); readName(apkFile, "assets/xposed_init", moduleClassNames); readName(apkFile, "assets/native_init", moduleLibraryNames); } catch (IOException e) { @@ -382,6 +391,17 @@ public class ConfigFileManager { } } + synchronized static SharedMemory getPreloadDex(boolean obfuscate) { + if (preloadDex == null) { + try (var is = new FileInputStream("framework/lspd.dex")) { + preloadDex = readDex(is, obfuscate); + } catch (Throwable e) { + Log.e(TAG, "preload dex", e); + } + } + return preloadDex; + } + private static class FileLocker { private final FileChannel lockChannel; private final FileLock locker; diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java index f3518dd7..921d4290 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -200,7 +200,7 @@ public class ConfigManager { var packageName = cursor.getString(pkgNameIdx); var m = cachedModule.computeIfAbsent(packageName, p -> { var module = new Module(); - var file = ConfigFileManager.loadModule(path); + var file = ConfigFileManager.loadModule(path, dexObfuscate); if (file == null) { Log.w(TAG, "Can not load " + path + ", skip!"); return null; @@ -250,6 +250,8 @@ public class ConfigManager { } updateManager(false); + + cacheHandler.post(this::getPreloadDex); } public synchronized void updateManager(boolean uninstalled) { @@ -271,16 +273,11 @@ public class ConfigManager { } static ConfigManager getInstance() { - return getInstance(true); - } - - static ConfigManager getInstance(boolean needCached) { if (instance == null) instance = new ConfigManager(); - if (needCached) { - synchronized (instance.cacheHandler) { - needCached = instance.lastModuleCacheTime == 0 || instance.lastScopeCacheTime == 0; - } + boolean needCached; + synchronized (instance.cacheHandler) { + needCached = instance.lastModuleCacheTime == 0 || instance.lastScopeCacheTime == 0; } if (needCached) { if (PackageService.isAlive()) { @@ -501,7 +498,7 @@ public class ConfigManager { apkPath = getModuleApkPath(pkgInfo.applicationInfo); if (apkPath == null) obsoleteModules.add(packageName); else obsoletePaths.put(packageName, apkPath); - var file = ConfigFileManager.loadModule(apkPath); + var file = ConfigFileManager.loadModule(apkPath, dexObfuscate); if (file == null) { Log.w(TAG, "failed to load module " + packageName); obsoleteModules.add(packageName); @@ -882,11 +879,13 @@ public class ConfigManager { public void setDexObfuscate(boolean on) { updateModulePrefs("lspd", 0, "config", "enable_dex_obfuscate", on); - dexObfuscate = on; } - public boolean dexObfuscate() { - return dexObfuscate; + // this is for manager and should not use the cache result + boolean dexObfuscate() { + Map config = getModulePrefs("lspd", 0, "config"); + Object bool = config.get("enable_dex_obfuscate"); + return bool != null && (boolean) bool; } public boolean isAddShortcut() { @@ -1057,4 +1056,8 @@ public class ConfigManager { }); os.closeEntry(); } + + synchronized SharedMemory getPreloadDex() { + return ConfigFileManager.getPreloadDex(dexObfuscate); + } } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java index 472fa7ef..913b8c35 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java @@ -26,13 +26,11 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.os.SharedMemory; import android.util.Log; import android.util.Pair; import org.lsposed.lspd.models.Module; -import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -47,18 +45,13 @@ public class LSPApplicationService extends ILSPApplicationService.Stub { private final static Set recipients = ConcurrentHashMap.newKeySet(); @Override - public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException { + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { Log.d(TAG, "LSPApplicationService.onTransact: code=" + code); if (code == DEX_TRANSACTION_CODE) { - try { - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(ObfuscationManager.preloadDex()); - reply.writeFileDescriptor(pfd.getFileDescriptor()); - reply.writeLong(ObfuscationManager.getPreloadedDexSize()); - pfd.close(); - } catch (IOException ignored) { - Log.e(TAG, "LSPApplicationService.onTransact: ParcelFileDescriptor.fromFd failed"); - return false; - } + var shm = ConfigManager.getInstance().getPreloadDex(); + // assume that write only a fd + shm.writeToParcel(reply, 0); + reply.writeLong(shm.getSize()); return true; } return super.onTransact(code, data, reply, flags); diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java index f0341df9..2f603710 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -771,12 +771,12 @@ public class LSPManagerService extends ILSPManagerService.Stub { } @Override - public boolean getDexObfuscate() throws RemoteException { + public boolean getDexObfuscate() { return ConfigManager.getInstance().dexObfuscate(); } @Override - public void setDexObfuscate(boolean enabled) throws RemoteException { + public void setDexObfuscate(boolean enabled) { ConfigManager.getInstance().setDexObfuscate(enabled); } } 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 fde3226f..10934c65 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ObfuscationManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ObfuscationManager.java @@ -3,21 +3,9 @@ package org.lsposed.lspd.service; import android.os.SharedMemory; public class ObfuscationManager { - static boolean enabled() { - return ConfigManager.getInstance(false).dexObfuscate(); - } - - static native void init(); - // For module dexes static native SharedMemory obfuscateDex(SharedMemory memory); - // preload lspd dex only, on daemon startup. - // it will cache the result, so we could obtain it back on startup. - static native int preloadDex(); - - static native long getPreloadedDexSize(); - // generates signature static native String getObfuscatedSignature(); } 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 dac96b9c..ab78b0c1 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java @@ -98,9 +98,6 @@ public class ServiceManager { logcatService.start(); Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); - ObfuscationManager.init(); - ObfuscationManager.getObfuscatedSignature(); - ObfuscationManager.preloadDex(); Looper.prepareMainLooper(); mainService = new LSPosedService(); applicationService = new LSPApplicationService();