From dc6cd4b0fdf7d87ccd37fe587dd8e47160a8bcd9 Mon Sep 17 00:00:00 2001 From: Nullptr <52071314+Dr-TSNG@users.noreply.github.com> Date: Thu, 21 Apr 2022 09:40:48 +0800 Subject: [PATCH] Fix incorrect mount point on Android 10 (#1870) * Detect conditions when dex2oat wrapper doesn't work * No enum * Copy * Update * Update * Fix dex2oat wrapper on Android 10 * Stupid typo * Update dex2oat.cpp Co-authored-by: LoveSy --- .../org/lsposed/manager/ConfigManager.java | 11 +- .../manager/ui/fragment/HomeFragment.java | 29 +++- app/src/main/res/values/strings.xml | 10 +- daemon/proguard-rules.pro | 7 + .../lsposed/lspd/service/Dex2OatService.java | 157 ++++++++++++++---- .../lspd/service/LSPManagerService.java | 6 +- daemon/src/main/jni/CMakeLists.txt | 2 +- daemon/src/main/jni/dex2oat.cpp | 141 ++++++++++++++++ daemon/src/main/jni/{dex2oat.c => utils.h} | 30 ++-- dex2oat/src/main/cpp/dex2oat.c | 4 +- magisk-loader/magisk_module/daemon | 10 -- magisk-loader/magisk_module/post-fs-data.sh | 7 - .../org/lsposed/lspd/ILSPManagerService.aidl | 8 +- 13 files changed, 336 insertions(+), 86 deletions(-) create mode 100644 daemon/src/main/jni/dex2oat.cpp rename daemon/src/main/jni/{dex2oat.c => utils.h} (52%) diff --git a/app/src/main/java/org/lsposed/manager/ConfigManager.java b/app/src/main/java/org/lsposed/manager/ConfigManager.java index 00bb6946..d0da0634 100644 --- a/app/src/main/java/org/lsposed/manager/ConfigManager.java +++ b/app/src/main/java/org/lsposed/manager/ConfigManager.java @@ -27,6 +27,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import org.lsposed.lspd.ILSPManagerService; import org.lsposed.lspd.models.Application; import org.lsposed.lspd.models.UserInfo; import org.lsposed.manager.adapters.ScopeAdapter; @@ -35,13 +36,9 @@ import org.lsposed.manager.receivers.LSPManagerServiceHolder; import java.io.File; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; -import io.github.xposed.xposedservice.utils.ParceledListSlice; - public class ConfigManager { public static boolean isBinderAlive() { @@ -360,12 +357,12 @@ public class ConfigManager { } } - public static boolean dex2oatWrapperAlive() { + public static int getDex2OatWrapperCompatibility() { try { - return LSPManagerServiceHolder.getService().dex2oatWrapperAlive(); + return LSPManagerServiceHolder.getService().getDex2OatWrapperCompatibility(); } catch (RemoteException e) { Log.e(App.TAG, Log.getStackTraceString(e)); - return false; + return ILSPManagerService.DEX2OAT_CRASHED; } } } diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java index 76933a6f..72b02759 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import androidx.core.text.HtmlCompat; import androidx.fragment.app.DialogFragment; +import org.lsposed.lspd.ILSPManagerService; import org.lsposed.manager.BuildConfig; import org.lsposed.manager.ConfigManager; import org.lsposed.manager.R; @@ -107,7 +108,7 @@ public class HomeFragment extends BaseFragment { } else { binding.updateCard.setVisibility(View.GONE); } - boolean dex2oatAbnormal = !ConfigManager.dex2oatWrapperAlive() && !ConfigManager.dex2oatFlagsLoaded(); + boolean dex2oatAbnormal = ConfigManager.getDex2OatWrapperCompatibility() != ILSPManagerService.DEX2OAT_OK && !ConfigManager.dex2oatFlagsLoaded(); if (!ConfigManager.isSepolicyLoaded() || !ConfigManager.systemServerRequested() || dex2oatAbnormal) { binding.statusTitle.setText(R.string.partial_activated); binding.statusIcon.setImageResource(R.drawable.ic_round_warning_24); @@ -158,11 +159,23 @@ public class HomeFragment extends BaseFragment { binding.api.setText(ConfigManager.getApi()); binding.frameworkVersion.setText(String.format(LocaleDelegate.getDefaultLocale(), "%1$s (%2$d)", ConfigManager.getXposedVersionName(), ConfigManager.getXposedVersionCode())); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - binding.dex2oatWrapper.setText(R.string.unsupported_android_version); - } else if (ConfigManager.dex2oatWrapperAlive()) { - binding.dex2oatWrapper.setText(R.string.supported); - } else { - binding.dex2oatWrapper.setText(R.string.unsupported_crashed); + binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.android_version_unsatisfied))); + } else switch (ConfigManager.getDex2OatWrapperCompatibility()) { + case ILSPManagerService.DEX2OAT_OK: + binding.dex2oatWrapper.setText(R.string.supported); + break; + case ILSPManagerService.DEX2OAT_CRASHED: + binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.crashed))); + break; + case ILSPManagerService.DEX2OAT_MOUNT_FAILED: + binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.mount_failed))); + break; + case ILSPManagerService.DEX2OAT_SELINUX_PERMISSIVE: + binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.selinux_permissive))); + break; + case ILSPManagerService.DEX2OAT_SEPOLICY_INCORRECT: + binding.dex2oatWrapper.setText(String.format(LocaleDelegate.getDefaultLocale(), "%s (%s)", getString(R.string.unsupported), getString(R.string.sepolicy_incorrect))); + break; } } else { binding.apiVersion.setText(R.string.not_installed); @@ -187,6 +200,10 @@ public class HomeFragment extends BaseFragment { "\n" + binding.api.getText() + "\n\n" + + activity.getString(R.string.info_dex2oat_wrapper) + + "\n" + + binding.dex2oatWrapper.getText() + + "\n\n" + activity.getString(R.string.info_framework_version) + "\n" + binding.frameworkVersion.getText() + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b125c77f..8f9e259b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -63,10 +63,14 @@ Device System ABI Injection Interface - Optimizer Wrapper + Dex Optimizer Wrapper Supported - Unsupported (Android version unsatisfied) - Unsupported (Crashed) + Unsupported + Android version unsatisfied + Crashed + Mount failed + SELinux is permissive + SELinux policy is incorrect Parasitic Manager Recommended LSPosed now supports system parasitization to avoid detection. You can safely uninstall the manager after successfully creating a shortcut of the parasitic manager. In any case you can install the manager back from /data/adb/lspd/manager.apk. Create shortcut diff --git a/daemon/proguard-rules.pro b/daemon/proguard-rules.pro index 39516ba3..74c73f81 100644 --- a/daemon/proguard-rules.pro +++ b/daemon/proguard-rules.pro @@ -4,6 +4,13 @@ -keepclasseswithmembers class org.lsposed.lspd.Main { public static void main(java.lang.String[]); } +-keepclasseswithmembers class org.lsposed.lspd.service.Dex2OatService { + private java.lang.String devTmpDir; + private java.lang.String magiskPath; + private java.lang.String fakeBin32; + private java.lang.String fakeBin64; + private java.lang.String[] dex2oatBinaries; +} -keepclasseswithmembers class org.lsposed.lspd.service.LogcatService { private int refreshFd(boolean); } diff --git a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java index e8e1b67a..8e67b9f7 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java @@ -19,12 +19,17 @@ package org.lsposed.lspd.service; +import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_CRASHED; +import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_MOUNT_FAILED; +import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_OK; +import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_SELINUX_PERMISSIVE; +import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_SEPOLICY_INCORRECT; + import android.net.LocalServerSocket; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.Build; -import android.os.Handler; -import android.os.Looper; +import android.os.FileObserver; import android.os.SELinux; import android.system.ErrnoException; import android.system.Os; @@ -32,88 +37,184 @@ import android.system.OsConstants; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Arrays; import java.util.Locale; +import java.util.Objects; public class Dex2OatService { public static final String PROP_NAME = "dalvik.vm.dex2oat-flags"; public static final String PROP_VALUE = "--inline-max-code-units=0"; private static final String TAG = "LSPosedDex2Oat"; - private static final String DEX2OAT_32 = "/apex/com.android.art/bin/dex2oat32"; - private static final String DEX2OAT_64 = "/apex/com.android.art/bin/dex2oat64"; - private final Thread thread; + private String devTmpDir, magiskPath, fakeBin32, fakeBin64; + private String[] dex2oatBinaries; + private FileDescriptor[] stockFds; private LocalSocket serverSocket = null; private LocalServerSocket server = null; - private FileDescriptor stockFd32 = null, stockFd64 = null; + private int compatibility = DEX2OAT_OK; + + private final FileObserver selinuxObserver = new FileObserver("/sys/fs/selinux/enforce", FileObserver.MODIFY) { + @Override + public void onEvent(int i, @Nullable String s) { + Log.d(TAG, "SELinux status changed"); + synchronized (this) { + if (compatibility == DEX2OAT_CRASHED) stopWatching(); + boolean enforcing = false; + try (var is = Files.newInputStream(Paths.get("/sys/fs/selinux/enforce"))) { + enforcing = is.read() == '1'; + } catch (IOException ignored) { + } + if (!enforcing) { + if (compatibility == DEX2OAT_OK) setEnabled(false); + compatibility = DEX2OAT_SELINUX_PERMISSIVE; + } else if (SELinux.checkSELinuxAccess("u:r:untrusted_app:s0", "u:object_r:dex2oat_exec:s0", "file", "execute") + || SELinux.checkSELinuxAccess("u:r:untrusted_app:s0", "u:object_r:dex2oat_exec:s0", "file", "execute_no_trans")) { + if (compatibility == DEX2OAT_OK) setEnabled(false); + compatibility = DEX2OAT_SEPOLICY_INCORRECT; + } else { + if (compatibility != DEX2OAT_OK) { + setEnabled(true); + if (checkMount()) compatibility = DEX2OAT_OK; + else { + setEnabled(false); + compatibility = DEX2OAT_MOUNT_FAILED; + stopWatching(); + } + } + } + } + } + + @Override + public void stopWatching() { + super.stopWatching(); + Log.w(TAG, "SELinux observer stopped"); + } + }; @RequiresApi(Build.VERSION_CODES.Q) public Dex2OatService() { - thread = new Thread(() -> { - var devPath = Paths.get(getDevPath()); + initNative(); + try { + Files.walk(Paths.get(magiskPath).resolve("dex2oat")).forEach(path -> { + SELinux.setFileContext(path.toString(), "u:object_r:magisk_file:s0"); + }); + } catch (IOException e) { + Log.e(TAG, "Error setting sepolicy", e); + } + if (Arrays.stream(dex2oatBinaries).noneMatch(Objects::nonNull)) { + Log.e(TAG, "Failed to find dex2oat binaries"); + compatibility = DEX2OAT_MOUNT_FAILED; + return; + } + + if (!checkMount()) { // Already mounted when restart daemon + setEnabled(true); + if (!checkMount()) { + setEnabled(false); + compatibility = DEX2OAT_MOUNT_FAILED; + return; + } + } + + Thread daemonThread = new Thread(() -> { + var devPath = Paths.get(devTmpDir); var sockPath = devPath.resolve("dex2oat.sock"); try { - Log.i(TAG, "dex2oat daemon start"); + Log.i(TAG, "Daemon start"); if (setSocketCreateContext("u:r:dex2oat:s0")) { - Log.d(TAG, "set socket context to u:r:dex2oat:s0"); + Log.d(TAG, "Set socket context to u:r:dex2oat:s0"); } else { - throw new IOException("failed to set socket context"); + throw new IOException("Failed to set socket context"); } Files.createDirectories(devPath); - Log.d(TAG, "dev path: " + devPath); + Log.d(TAG, "Dev path: " + devPath); serverSocket = new LocalSocket(LocalSocket.SOCKET_STREAM); serverSocket.bind(new LocalSocketAddress(sockPath.toString(), LocalSocketAddress.Namespace.FILESYSTEM)); server = new LocalServerSocket(serverSocket.getFileDescriptor()); SELinux.setFileContext(sockPath.toString(), "u:object_r:magisk_file:s0"); - if (new File(DEX2OAT_32).exists()) stockFd32 = Os.open(DEX2OAT_32, OsConstants.O_RDONLY, 0); - if (new File(DEX2OAT_64).exists()) stockFd64 = Os.open(DEX2OAT_64, OsConstants.O_RDONLY, 0); + stockFds = new FileDescriptor[dex2oatBinaries.length]; + for (int i = 0; i < dex2oatBinaries.length; i++) { + if (dex2oatBinaries[i] != null) { + stockFds[i] = Os.open(dex2oatBinaries[i], OsConstants.O_RDONLY, 0); + } + } while (true) { var client = server.accept(); try (var is = client.getInputStream(); var os = client.getOutputStream()) { - var lp = is.read(); - if (lp == 32) client.setFileDescriptorsForSend(new FileDescriptor[]{stockFd32}); - else client.setFileDescriptorsForSend(new FileDescriptor[]{stockFd64}); + var id = is.read(); + client.setFileDescriptorsForSend(new FileDescriptor[]{stockFds[id]}); os.write(1); - Log.d(TAG, "sent fd" + lp); + Log.d(TAG, String.format("Sent stock fd: is64 = %b, isDebug = %b", (id & 0b10) != 0, (id & 0b01) != 0)); } } } catch (Throwable e) { - Log.e(TAG, "dex2oat daemon crashed", e); + Log.e(TAG, "Daemon crashed", e); try { server.close(); Files.delete(sockPath); } catch (IOException ignored) { } try { - if (stockFd32 != null && stockFd32.valid()) Os.close(stockFd32); - if (stockFd64 != null && stockFd64.valid()) Os.close(stockFd64); + for (var fd : stockFds) { + if (fd != null && fd.valid()) Os.close(fd); + } } catch (ErrnoException ignored) { } - new Handler(Looper.getMainLooper()).post(Dex2OatService::fallback); + synchronized (this) { + selinuxObserver.stopWatching(); + if (compatibility == DEX2OAT_OK) { + setEnabled(false); + compatibility = DEX2OAT_CRASHED; + } + } } }); - thread.start(); + + daemonThread.start(); + selinuxObserver.startWatching(); } @RequiresApi(Build.VERSION_CODES.Q) - public boolean isAlive() { - return thread.isAlive(); + public int getCompatibility() { + return compatibility; } - private static native String getDevPath(); + private native void initNative(); - private static native void fallback(); + private native void setEnabled(boolean enabled); + + private boolean checkMount() { + for (int i = 0; i < dex2oatBinaries.length; i++) { + var bin = dex2oatBinaries[i]; + if (bin == null) continue; + try { + var apex = Os.stat("/proc/1/root" + bin); + var fake = Os.stat(i < 2 ? fakeBin32 : fakeBin64); + if (apex.st_ino != fake.st_ino) { + Log.w(TAG, "Check mount failed for " + bin); + return false; + } + } catch (ErrnoException e) { + Log.e(TAG, "Check mount failed for " + bin, e); + return false; + } + } + Log.d(TAG, "Check mount succeeded"); + return true; + } private boolean setSocketCreateContext(String context) { FileDescriptor fd = null; 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 e2762e3e..9c22d3aa 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -777,11 +777,11 @@ public class LSPManagerService extends ILSPManagerService.Stub { } @Override - public boolean dex2oatWrapperAlive() { + public int getDex2OatWrapperCompatibility() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - return ServiceManager.getDex2OatService().isAlive(); + return ServiceManager.getDex2OatService().getCompatibility(); } else { - return false; + return 0; } } } diff --git a/daemon/src/main/jni/CMakeLists.txt b/daemon/src/main/jni/CMakeLists.txt index 8ff0318a..6f63d431 100644 --- a/daemon/src/main/jni/CMakeLists.txt +++ b/daemon/src/main/jni/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.4.1) add_subdirectory(${EXTERNAL_ROOT} external) set(SOURCES - dex2oat.c + dex2oat.cpp logcat.cpp obfuscation.cpp ) diff --git a/daemon/src/main/jni/dex2oat.cpp b/daemon/src/main/jni/dex2oat.cpp new file mode 100644 index 00000000..11e693ea --- /dev/null +++ b/daemon/src/main/jni/dex2oat.cpp @@ -0,0 +1,141 @@ +/* + * 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 + */ + +// +// Created by Nullptr on 2022/4/2. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" +#include "utils.h" + +using namespace std::string_literals; + +const char *kDex2oat32Path, *kDex2oatDebug32Path; +const char *kDex2oat64Path, *kDex2oatDebug64Path; + +char kFakeBin32[PATH_MAX], kFakeBin64[PATH_MAX]; +char kTmpDir[] = "placeholder_/dev/0123456789abcdef\0"; + +extern "C" +JNIEXPORT void JNICALL +Java_org_lsposed_lspd_service_Dex2OatService_initNative(JNIEnv *env, jobject thiz) { + char magisk_path[PATH_MAX], cwd[PATH_MAX], *module_name; + FILE *fp = popen("magisk --path", "r"); + fscanf(fp, "%s", magisk_path); + fclose(fp); + getcwd(cwd, PATH_MAX); + module_name = cwd + std::string_view(cwd).find_last_of('/') + 1; + sprintf(kFakeBin32, "%s/.magisk/modules/%s/bin/dex2oat32", magisk_path, module_name); + sprintf(kFakeBin64, "%s/.magisk/modules/%s/bin/dex2oat64", magisk_path, module_name); + + if (GetAndroidApiLevel() == 29) { + kDex2oat32Path = "/apex/com.android.runtime/bin/dex2oat"; + kDex2oatDebug32Path = "/apex/com.android.runtime/bin/dex2oatd"; + } else { + kDex2oat32Path = "/apex/com.android.art/bin/dex2oat32"; + kDex2oatDebug32Path = "/apex/com.android.art/bin/dex2oatd32"; + kDex2oat64Path = "/apex/com.android.art/bin/dex2oat64"; + kDex2oatDebug64Path = "/apex/com.android.art/bin/dex2oatd64"; + } + + std::string copy_dir = magisk_path + "/dex2oat"s; + mkdir(copy_dir.c_str(), 0755); + auto CopyAndMount = [&](const char *src, const std::string &target) -> bool { + int stock = open(src, O_RDONLY); + if (stock == -1) return false; + struct stat st{}; + fstat(stock, &st); + int copy = open(target.c_str(), O_WRONLY | O_CREAT, st.st_mode); + sendfile(copy, stock, nullptr, st.st_size); + close(stock); + close(copy); + mount(target.c_str(), src, nullptr, MS_BIND, nullptr); + return true; + }; + bool done[4] = {false}; + done[0] = CopyAndMount(kDex2oat32Path, copy_dir + "/dex2oat32"); + done[1] = CopyAndMount(kDex2oatDebug32Path, copy_dir + "/dex2oatd32"); + if (GetAndroidApiLevel() >= 30) { + done[2] = CopyAndMount(kDex2oat64Path, copy_dir + "/dex2oat64"); + done[3] = CopyAndMount(kDex2oatDebug64Path, copy_dir + "/dex2oatd64"); + } + + auto clazz = env->GetObjectClass(thiz); + auto dev_path_field = env->GetFieldID(clazz, "devTmpDir", "Ljava/lang/String;"); + auto magisk_path_field = env->GetFieldID(clazz, "magiskPath", "Ljava/lang/String;"); + auto fake_bin32_field = env->GetFieldID(clazz, "fakeBin32", "Ljava/lang/String;"); + auto fake_bin64_field = env->GetFieldID(clazz, "fakeBin64", "Ljava/lang/String;"); + auto binaries_field = env->GetFieldID(clazz, "dex2oatBinaries", "[Ljava/lang/String;"); + env->SetObjectField(thiz, dev_path_field, env->NewStringUTF(kTmpDir + 12)); + env->SetObjectField(thiz, magisk_path_field, env->NewStringUTF(magisk_path)); + env->SetObjectField(thiz, fake_bin32_field, env->NewStringUTF(kFakeBin32)); + env->SetObjectField(thiz, fake_bin64_field, env->NewStringUTF(kFakeBin64)); + auto arr = env->NewObjectArray(4, env->FindClass("java/lang/String"), nullptr); + if (done[0]) env->SetObjectArrayElement(arr, 0, env->NewStringUTF(kDex2oat32Path)); + if (done[1]) env->SetObjectArrayElement(arr, 1, env->NewStringUTF(kDex2oatDebug32Path)); + if (GetAndroidApiLevel() >= 30) { + if (done[2]) env->SetObjectArrayElement(arr, 2, env->NewStringUTF(kDex2oat64Path)); + if (done[3]) env->SetObjectArrayElement(arr, 3, env->NewStringUTF(kDex2oatDebug64Path)); + } + env->SetObjectField(thiz, binaries_field, arr); +} + +extern "C" +JNIEXPORT void JNICALL +Java_org_lsposed_lspd_service_Dex2OatService_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { + if (vfork() == 0) { + int ns = open("/proc/1/ns/mnt", O_RDONLY); + setns(ns, CLONE_NEWNS); + close(ns); + if (enabled) { + LOGI("Enable dex2oat wrapper"); + mount(kFakeBin32, kDex2oat32Path, nullptr, MS_BIND, nullptr); + mount(kFakeBin32, kDex2oatDebug32Path, nullptr, MS_BIND, nullptr); + if (GetAndroidApiLevel() >= 30) { + mount(kFakeBin64, kDex2oat64Path, nullptr, MS_BIND, nullptr); + mount(kFakeBin64, kDex2oatDebug64Path, nullptr, MS_BIND, nullptr); + } + execlp("resetprop", "resetprop", "--delete", "dalvik.vm.dex2oat-flags", nullptr); + } else { + LOGI("Disable dex2oat wrapper"); + umount(kDex2oat32Path); + umount(kDex2oatDebug32Path); + if (GetAndroidApiLevel() >= 30) { + umount(kDex2oat64Path); + umount(kDex2oatDebug64Path); + } + execlp("resetprop", "resetprop", "dalvik.vm.dex2oat-flags", "--inline-max-code-units=0", nullptr); + } + PLOGE("Failed to resetprop"); + exit(0); + } +} diff --git a/daemon/src/main/jni/dex2oat.c b/daemon/src/main/jni/utils.h similarity index 52% rename from daemon/src/main/jni/dex2oat.c rename to daemon/src/main/jni/utils.h index d3d5e0d1..fb0eaf12 100644 --- a/daemon/src/main/jni/dex2oat.c +++ b/daemon/src/main/jni/utils.h @@ -18,26 +18,18 @@ */ // -// Created by Nullptr on 2022/4/2. +// Created by Nullptr on 2022/4/20. // -#include -#include -#include -#include +#pragma once -#include "logging.h" - -char kTmpDir[] = "placeholder_/dev/0123456789abcdef"; - -JNIEXPORT jstring JNICALL -Java_org_lsposed_lspd_service_Dex2OatService_getDevPath(JNIEnv *env, jclass clazz) { - return (*env)->NewStringUTF(env, kTmpDir + 12); -} - -JNIEXPORT void JNICALL -Java_org_lsposed_lspd_service_Dex2OatService_fallback(JNIEnv *env, jclass clazz) { - LOGI("do fallback"); - system("nsenter -m -t 1 umount /apex/com.android.art/bin/dex2oat*"); - __system_property_set("dalvik.vm.dex2oat-flags", "--inline-max-code-units=0"); +inline int32_t GetAndroidApiLevel() { + static int32_t api_level = []() { + char prop_value[PROP_VALUE_MAX]; + __system_property_get("ro.build.version.sdk", prop_value); + int base = atoi(prop_value); + __system_property_get("ro.build.version.preview_sdk", prop_value); + return base + atoi(prop_value); + }(); + return api_level; } diff --git a/dex2oat/src/main/cpp/dex2oat.c b/dex2oat/src/main/cpp/dex2oat.c index 67c97d9e..aa622853 100644 --- a/dex2oat/src/main/cpp/dex2oat.c +++ b/dex2oat/src/main/cpp/dex2oat.c @@ -36,6 +36,8 @@ # define LP_SELECT(lp32, lp64) lp32 #endif +#define ID_VEC(is64, is_debug) (((is64) << 1) | (is_debug)) + char kTmpDir[] = "placeholder_/dev/0123456789abcdef"; static ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) { @@ -106,7 +108,7 @@ int main(int argc, char **argv) { PLOGE("failed to connect to %s", sock.sun_path); return 1; } - write_int(sock_fd, LP_SELECT(32, 64)); + write_int(sock_fd, ID_VEC(LP_SELECT(0, 1), strstr(argv[0], "dex2oatd") != NULL)); int stock_fd = recv_fd(sock_fd); read_int(sock_fd); close(sock_fd); diff --git a/magisk-loader/magisk_module/daemon b/magisk-loader/magisk_module/daemon index f7cf399e..8187d2aa 100644 --- a/magisk-loader/magisk_module/daemon +++ b/magisk-loader/magisk_module/daemon @@ -40,15 +40,5 @@ if [ ! -S "/dev/socket/zygote" ]; then fi $debug && log -p d -t "LSPosed" "start $flavor daemon $*" -if [ "$(getprop ro.build.version.sdk)" -ge 29 ]; then - umount /apex/com.android.art/bin/dex2oat* - mkdir "$magiskPath/dex2oat" - cp -p /apex/com.android.art/bin/dex2oat32 "$magiskPath/dex2oat/dex2oat32" - cp -p /apex/com.android.art/bin/dex2oat64 "$magiskPath/dex2oat/dex2oat64" - chcon -R u:object_r:magisk_file:s0 "$magiskPath/dex2oat" - mount --bind "$magiskPath/dex2oat/dex2oat32" /apex/com.android.art/bin/dex2oat32 - mount --bind "$magiskPath/dex2oat/dex2oat64" /apex/com.android.art/bin/dex2oat64 -fi - # shellcheck disable=SC2086 exec /system/bin/app_process $java_options /system/bin --nice-name=lspd org.lsposed.lspd.Main "$@" >/dev/null 2>&1 diff --git a/magisk-loader/magisk_module/post-fs-data.sh b/magisk-loader/magisk_module/post-fs-data.sh index c26cb98f..6a205643 100644 --- a/magisk-loader/magisk_module/post-fs-data.sh +++ b/magisk-loader/magisk_module/post-fs-data.sh @@ -18,15 +18,8 @@ # MODDIR=${0%/*} -MODNAME=${MODDIR##*/} -MAGISK_PATH=$(magisk --path) rm -f "/data/local/tmp/daemon.apk" cd "$MODDIR" -if [ "$(getprop ro.build.version.sdk)" -ge 29 ]; then - mount --bind "$MAGISK_PATH/.magisk/modules/$MODNAME/bin/dex2oat32" "/apex/com.android.art/bin/dex2oat32" - mount --bind "$MAGISK_PATH/.magisk/modules/$MODNAME/bin/dex2oat64" "/apex/com.android.art/bin/dex2oat64" -fi - unshare -m sh -c "$MODDIR/daemon &" diff --git a/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl b/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl index dc899994..1b0b17a1 100644 --- a/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl +++ b/services/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl @@ -6,6 +6,12 @@ import org.lsposed.lspd.models.Application; interface ILSPManagerService { + const int DEX2OAT_OK = 0; + const int DEX2OAT_CRASHED = 1; + const int DEX2OAT_MOUNT_FAILED = 2; + const int DEX2OAT_SELINUX_PERMISSIVE = 3; + const int DEX2OAT_SEPOLICY_INCORRECT = 4; + String getApi() = 1; ParceledListSlice getInstalledPackagesFromAllUsers(int flags, boolean filterNoProcess) = 2; @@ -80,5 +86,5 @@ interface ILSPManagerService { void setDexObfuscate(boolean enable) = 43; - boolean dex2oatWrapperAlive() = 44; + int getDex2OatWrapperCompatibility() = 44; }