From 3d11c2f0f7754201c6b5ec4213b77ab26b711d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E5=AE=AB=E9=9B=AA=E7=8F=8A?= Date: Sun, 19 Feb 2023 21:24:54 +0800 Subject: [PATCH] Make dex2oat wrapper more compatible (#2372) --- app/build.gradle.kts | 2 +- .../manager/ui/fragment/HomeFragment.java | 8 +- app/src/main/res/values/strings.xml | 2 +- build.gradle.kts | 4 +- .../lsposed/lspd/service/ConfigManager.java | 7 - .../lsposed/lspd/service/Dex2OatService.java | 333 ++++++++---------- .../lspd/service/LSPManagerService.java | 3 +- .../lsposed/lspd/service/ServiceManager.java | 1 + daemon/src/main/jni/dex2oat.cpp | 170 ++++----- daemon/src/main/jni/utils.h | 35 -- dex2oat/src/main/cpp/dex2oat.c | 15 +- gradle.properties | 2 +- magisk-loader/build.gradle.kts | 9 +- magisk-loader/magisk_module/customize.sh | 15 +- magisk-loader/magisk_module/daemon | 1 - magisk-loader/magisk_module/sepolicy.rule | 2 +- .../lsposed/lspd/service/BridgeService.java | 2 +- 17 files changed, 245 insertions(+), 366 deletions(-) delete mode 100644 daemon/src/main/jni/utils.h diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7272675a..fe8d70fd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -151,7 +151,7 @@ dependencies { implementation("androidx.recyclerview:recyclerview:1.2.1") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01") implementation("com.github.bumptech.glide:glide:$glideVersion") - implementation("com.google.android.material:material:1.7.0") + implementation("com.google.android.material:material:1.8.0") implementation("com.google.code.gson:gson:2.10.1") implementation(platform("com.squareup.okhttp3:okhttp-bom:4.10.0")) implementation("com.squareup.okhttp3:okhttp") 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 920248bf..8b1fc5cc 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 @@ -109,15 +109,17 @@ public class HomeFragment extends BaseFragment { binding.updateCard.setVisibility(View.GONE); } boolean dex2oatAbnormal = ConfigManager.getDex2OatWrapperCompatibility() != ILSPManagerService.DEX2OAT_OK && !ConfigManager.dex2oatFlagsLoaded(); - if (!ConfigManager.isSepolicyLoaded() || !ConfigManager.systemServerRequested() || dex2oatAbnormal) { + var sepolicyAbnormal = !ConfigManager.isSepolicyLoaded(); + var systemServerAbnormal = !ConfigManager.systemServerRequested(); + if (sepolicyAbnormal || systemServerAbnormal || dex2oatAbnormal) { binding.statusTitle.setText(R.string.partial_activated); binding.statusIcon.setImageResource(R.drawable.ic_round_warning_24); binding.warningCard.setVisibility(View.VISIBLE); - if (!ConfigManager.isSepolicyLoaded()) { + if (sepolicyAbnormal) { binding.warningTitle.setText(R.string.selinux_policy_not_loaded_summary); binding.warningSummary.setText(HtmlCompat.fromHtml(getString(R.string.selinux_policy_not_loaded), HtmlCompat.FROM_HTML_MODE_LEGACY)); } - if (!ConfigManager.systemServerRequested()) { + if (systemServerAbnormal) { binding.warningTitle.setText(R.string.system_inject_fail_summary); binding.warningSummary.setText(HtmlCompat.fromHtml(getString(R.string.system_inject_fail), HtmlCompat.FROM_HTML_MODE_LEGACY)); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c311a883..95404860 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -50,7 +50,7 @@ Activated Partially activated SEPolicy is not loaded properly - Please report this to Magisk developer.]]> + Please report this to Magisk developer.]]> System Framework injection failed Magisk or some low-quality Magisk modules.
Please try to disable Magisk modules other than Riru and LSPosed or submit full log to developers.]]>
System prop incorrect diff --git a/build.gradle.kts b/build.gradle.kts index 0ff07233..bb583088 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,8 +22,8 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.gradle.BaseExtension import org.eclipse.jgit.api.Git import org.eclipse.jgit.storage.file.FileRepositoryBuilder -import java.nio.file.Paths import org.gradle.internal.os.OperatingSystem +import java.nio.file.Paths plugins { id("com.android.application") apply false @@ -122,7 +122,7 @@ fun Project.configureBaseExtension() { "-DINJECTED_AID=$injectedPackageUid", ) cppFlags("-std=c++20", *flags) - cFlags("-std=c18", *flags) + cFlags("-std=c2x", *flags) arguments( "-DANDROID_STL=none", ) 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 0be41025..8268d0b4 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -104,8 +104,6 @@ public class ConfigManager { private long lastScopeCacheTime = 0; private long requestScopeCacheTime = 0; - private boolean sepolicyLoaded = true; - private String api = "(???)"; static class ProcessScope { @@ -184,7 +182,6 @@ public class ConfigManager { // for system server, cache is not yet ready, we need to query database for it public boolean shouldSkipSystemServer() { if (!SELinux.checkSELinuxAccess("u:r:system_server:s0", "u:r:system_server:s0", "process", "execmem")) { - sepolicyLoaded = false; Log.e(TAG, "skip injecting into android because sepolicy was not loaded properly"); return true; // skip } @@ -1021,10 +1018,6 @@ public class ConfigManager { ConfigFileManager.deleteFolderIfExists(path); } - public boolean isSepolicyLoaded() { - return sepolicyLoaded; - } - public List getDenyListPackages() { List result = new ArrayList<>(); if (!getApi().equals("Zygisk")) return result; 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 208863c1..a4761bac 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java @@ -26,247 +26,194 @@ 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.FileObserver; import android.os.SELinux; import android.system.ErrnoException; import android.system.Os; 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 { +import java.util.ArrayList; +@RequiresApi(Build.VERSION_CODES.Q) +public class Dex2OatService implements Runnable { 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 WRAPPER32 = "bin/dex2oat32"; + private static final String WRAPPER64 = "bin/dex2oat64"; - private String devTmpDir, magiskPath, fakeBin32, fakeBin64; - private String[] dex2oatBinaries; - private FileDescriptor[] stockFds; - private LocalSocket serverSocket = null; - private LocalServerSocket server = null; + private final String[] dex2oatArray = new String[4]; + private final FileDescriptor[] fdArray = new FileDescriptor[4]; + private final FileObserver selinuxObserver; 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(); + private void openDex2oat(int id, String path) { + try { + var fd = Os.open(path, OsConstants.O_RDONLY, 0); + dex2oatArray[id] = path; + fdArray[id] = fd; + } catch (ErrnoException ignored) { + } + } + + public Dex2OatService() { + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { + openDex2oat(0, "/apex/com.android.runtime/bin/dex2oat"); + openDex2oat(1, "/apex/com.android.runtime/bin/dex2oatd"); + } else { + openDex2oat(0, "/apex/com.android.art/bin/dex2oat32"); + openDex2oat(1, "/apex/com.android.art/bin/dex2oatd32"); + openDex2oat(2, "/apex/com.android.art/bin/dex2oat64"); + openDex2oat(3, "/apex/com.android.art/bin/dex2oatd64"); + } + + var enforce = Paths.get("/sys/fs/selinux/enforce"); + var policy = Paths.get("/sys/fs/selinux/policy"); + var list = new ArrayList(); + list.add(enforce.toFile()); + list.add(policy.toFile()); + selinuxObserver = new FileObserver(list, FileObserver.CLOSE_WRITE) { + @Override + public synchronized void onEvent(int i, @Nullable String s) { + Log.d(TAG, "SELinux status changed"); + if (compatibility == DEX2OAT_CRASHED) { + stopWatching(); + return; + } + boolean enforcing = false; - try (var is = Files.newInputStream(Paths.get("/sys/fs/selinux/enforce"))) { + try (var is = Files.newInputStream(enforce)) { enforcing = is.read() == '1'; } catch (IOException ignored) { } + if (!enforcing) { - if (compatibility == DEX2OAT_OK) setEnabled(false); + if (compatibility == DEX2OAT_OK) doMount(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); + } 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) doMount(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(); - } + } else if (compatibility != DEX2OAT_OK) { + doMount(true); + if (notMounted()) { + doMount(false); + compatibility = DEX2OAT_MOUNT_FAILED; + stopWatching(); + } else { + compatibility = DEX2OAT_OK; } } } - } - @Override - public void stopWatching() { - super.stopWatching(); - Log.w(TAG, "SELinux observer stopped"); - } - }; + @Override + public void stopWatching() { + super.stopWatching(); + Log.w(TAG, "SELinux observer stopped"); + } + }; + } - @RequiresApi(Build.VERSION_CODES.Q) - public Dex2OatService() { - 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; + private boolean notMounted() { + for (int i = 0; i < dex2oatArray.length; i++) { + var bin = dex2oatArray[i]; + if (bin == null) continue; + try { + var apex = Os.stat("/proc/1/root" + bin); + var wrapper = Os.stat(i < 2 ? WRAPPER32 : WRAPPER64); + if (apex.st_dev != wrapper.st_dev || apex.st_ino != wrapper.st_ino) { + Log.w(TAG, "Check mount failed for " + bin); + return true; + } + } catch (ErrnoException e) { + Log.e(TAG, "Check mount failed for " + bin, e); + return true; + } } + Log.d(TAG, "Check mount succeeded"); + return false; + } - if (!checkMount()) { // Already mounted when restart daemon - setEnabled(true); - if (!checkMount()) { - setEnabled(false); + private void doMount(boolean enabled) { + doMountNative(enabled, dex2oatArray[0], dex2oatArray[1], dex2oatArray[2], dex2oatArray[3]); + } + + public void start() { + if (notMounted()) { // Already mounted when restart daemon + doMount(true); + if (notMounted()) { + doMount(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 wrapper daemon start"); - if (setSocketCreateContext("u:r:dex2oat:s0")) { - Log.d(TAG, "Set socket context to u:r:dex2oat:s0"); - } else { - throw new IOException("Failed to set socket context"); - } - Files.createDirectories(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"); - 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 id = is.read(); - client.setFileDescriptorsForSend(new FileDescriptor[]{stockFds[id]}); - os.write(1); - 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 wrapper daemon crashed", e); - try { - server.close(); - Files.delete(sockPath); - } catch (IOException ignored) { - } - try { - for (var fd : stockFds) { - if (fd != null && fd.valid()) Os.close(fd); - } - } catch (ErrnoException ignored) { - } - synchronized (this) { - selinuxObserver.stopWatching(); - if (compatibility == DEX2OAT_OK) { - setEnabled(false); - compatibility = DEX2OAT_CRASHED; - } - } - } - }); - - daemonThread.start(); + var thread = new Thread(this); + thread.setName("dex2oat"); + thread.start(); selinuxObserver.startWatching(); } - @RequiresApi(Build.VERSION_CODES.Q) + @Override + public void run() { + Log.i(TAG, "Dex2oat wrapper daemon start"); + var sockPath = getSockPath(); + Log.d(TAG, "wrapper path: " + sockPath); + var magisk_file = "u:object_r:magisk_file:s0"; + var dex2oat_exec = "u:object_r:dex2oat_exec:s0"; + if (SELinux.checkSELinuxAccess("u:r:dex2oat:s0", dex2oat_exec, + "file", "execute_no_trans")) { + SELinux.setFileContext(WRAPPER32, dex2oat_exec); + SELinux.setFileContext(WRAPPER64, dex2oat_exec); + setSockCreateContext("u:r:dex2oat:s0"); + } else { + SELinux.setFileContext(WRAPPER32, magisk_file); + SELinux.setFileContext(WRAPPER64, magisk_file); + setSockCreateContext("u:r:installd:s0"); + } + try (var server = new LocalServerSocket(sockPath)) { + setSockCreateContext(null); + while (true) { + try (var client = server.accept(); + var is = client.getInputStream(); + var os = client.getOutputStream()) { + var id = is.read(); + var fd = new FileDescriptor[]{fdArray[id]}; + client.setFileDescriptorsForSend(fd); + os.write(1); + Log.d(TAG, "Sent stock fd: is64 = " + ((id & 0b10) != 0) + + ", isDebug = " + ((id & 0b01) != 0)); + } + } + } catch (IOException e) { + Log.e(TAG, "Dex2oat wrapper daemon crashed", e); + if (compatibility == DEX2OAT_OK) { + doMount(false); + compatibility = DEX2OAT_CRASHED; + } + } + } + public int getCompatibility() { return compatibility; } - private native void initNative(); + private native void doMountNative(boolean enabled, + String r32, String d32, String r64, String d64); - private native void setEnabled(boolean enabled); + private static native boolean setSockCreateContext(String context); - 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; - try { - fd = Os.open("/proc/thread-self/attr/sockcreate", OsConstants.O_RDWR, 0); - } catch (ErrnoException e) { - if (e.errno == OsConstants.ENOENT) { - int tid = Os.gettid(); - try { - fd = Os.open(String.format(Locale.ENGLISH, "/proc/self/task/%d/attr/sockcreate", tid), OsConstants.O_RDWR, 0); - } catch (ErrnoException ignored) { - } - } - } - - if (fd == null) { - return false; - } - - byte[] bytes; - int length; - int remaining; - if (!TextUtils.isEmpty(context)) { - byte[] stringBytes = context.getBytes(); - bytes = new byte[stringBytes.length + 1]; - System.arraycopy(stringBytes, 0, bytes, 0, stringBytes.length); - bytes[stringBytes.length] = '\0'; - - length = bytes.length; - remaining = bytes.length; - } else { - bytes = null; - length = 0; - remaining = 0; - } - - do { - try { - remaining -= Os.write(fd, bytes, length - remaining, remaining); - if (remaining <= 0) { - break; - } - } catch (ErrnoException e) { - break; - } catch (InterruptedIOException e) { - remaining -= e.bytesTransferred; - } - } while (true); - - try { - Os.close(fd); - } catch (ErrnoException e) { - Log.w(TAG, Log.getStackTraceString(e)); - } - return true; - } + private native String getSockPath(); } 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 d09b600e..8b32cf52 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -469,7 +469,8 @@ public class LSPManagerService extends ILSPManagerService.Stub { @Override public boolean isSepolicyLoaded() { - return ConfigManager.getInstance().isSepolicyLoaded(); + return SELinux.checkSELinuxAccess("u:r:dex2oat:s0", "u:object_r:dex2oat_exec:s0", + "file", "execute_no_trans"); } @Override 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 8d118172..580be54b 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ServiceManager.java @@ -117,6 +117,7 @@ public class ServiceManager { systemServerService = new LSPSystemServerService(systemServerMaxRetry); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { dex2OatService = new Dex2OatService(); + dex2OatService.start(); } systemServerService.putBinderForSystemServer(); diff --git a/daemon/src/main/jni/dex2oat.cpp b/daemon/src/main/jni/dex2oat.cpp index 6e2c1984..202c99ff 100644 --- a/daemon/src/main/jni/dex2oat.cpp +++ b/daemon/src/main/jni/dex2oat.cpp @@ -14,131 +14,105 @@ * You should have received a copy of the GNU General Public License * along with LSPosed. If not, see . * - * Copyright (C) 2022 LSPosed Contributors + * Copyright (C) 2023 LSPosed Contributors */ -// -// Created by Nullptr on 2022/4/2. -// - -#include #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); +Java_org_lsposed_lspd_service_Dex2OatService_doMountNative(JNIEnv *env, jobject, + jboolean enabled, + jstring r32, jstring d32, + jstring r64, jstring d64) { + char dex2oat32[PATH_MAX], dex2oat64[PATH_MAX]; + realpath("bin/dex2oat32", dex2oat32); + realpath("bin/dex2oat64", dex2oat64); - 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 (pid_t pid = fork(); pid > 0) { // parent waitpid(pid, nullptr, 0); } else { // child int ns = open("/proc/1/ns/mnt", O_RDONLY); setns(ns, CLONE_NEWNS); close(ns); + + const char *r32p, *d32p, *r64p, *d64p; + if (r32) r32p = env->GetStringUTFChars(r32, nullptr); + if (d32) d32p = env->GetStringUTFChars(d32, nullptr); + if (r64) r64p = env->GetStringUTFChars(r64, nullptr); + if (d64) d64p = env->GetStringUTFChars(d64, nullptr); + 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); + if (r32) { + mount(dex2oat32, r32p, nullptr, MS_BIND, nullptr); + mount(nullptr, r32p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr); + } + if (d32) { + mount(dex2oat32, d32p, nullptr, MS_BIND, nullptr); + mount(nullptr, d32p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr); + } + if (r64) { + mount(dex2oat64, r64p, nullptr, MS_BIND, nullptr); + mount(nullptr, r64p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, nullptr); + } + if (d64) { + mount(dex2oat64, d64p, nullptr, MS_BIND, nullptr); + mount(nullptr, d64p, nullptr, MS_BIND | MS_REMOUNT | MS_RDONLY, 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); + if (r32) umount(r32p); + if (d32) umount(d32p); + if (r64) umount(r64p); + if (d64) umount(d64p); + execlp("resetprop", "resetprop", "dalvik.vm.dex2oat-flags", "--inline-max-code-units=0", + nullptr); } + PLOGE("Failed to resetprop"); - exit(0); + exit(1); } } + +static int setsockcreatecon_raw(const char *context) { + std::string path = "/proc/self/task/" + std::to_string(gettid()) + "/attr/sockcreate"; + int fd = open(path.c_str(), O_RDWR | O_CLOEXEC); + if (fd < 0) return -1; + int ret; + if (context) { + do { + ret = write(fd, context, strlen(context) + 1); + } while (ret < 0 && errno == EINTR); + } else { + do { + ret = write(fd, nullptr, 0); // clear + } while (ret < 0 && errno == EINTR); + } + close(fd); + return ret < 0 ? -1 : 0; +} + +extern "C" +JNIEXPORT jboolean JNICALL +Java_org_lsposed_lspd_service_Dex2OatService_setSockCreateContext(JNIEnv *env, jclass, + jstring contextStr) { + const char *context = env->GetStringUTFChars(contextStr, nullptr); + int ret = setsockcreatecon_raw(context); + env->ReleaseStringUTFChars(contextStr, context); + return ret == 0; +} + +extern "C" +JNIEXPORT jstring JNICALL +Java_org_lsposed_lspd_service_Dex2OatService_getSockPath(JNIEnv *env, jobject) { + return env->NewStringUTF("5291374ceda0aef7c5d86cd2a4f6a3ac\0"); +} diff --git a/daemon/src/main/jni/utils.h b/daemon/src/main/jni/utils.h deleted file mode 100644 index fb0eaf12..00000000 --- a/daemon/src/main/jni/utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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/20. -// - -#pragma once - -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 0a8b6d87..1e120623 100644 --- a/dex2oat/src/main/cpp/dex2oat.c +++ b/dex2oat/src/main/cpp/dex2oat.c @@ -38,7 +38,7 @@ #define ID_VEC(is64, is_debug) (((is64) << 1) | (is_debug)) -char kTmpDir[] = "placeholder_/dev/0123456789abcdef"; +const char kSockName[] = "5291374ceda0aef7c5d86cd2a4f6a3ac\0"; static ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) { int rec = recvmsg(sockfd, msg, flags); @@ -99,20 +99,21 @@ static void write_int(int fd, int val) { } int main(int argc, char **argv) { - LOGD("dex2oat wrapper"); - struct sockaddr_un sock; + LOGD("dex2oat wrapper ppid=%d", getppid()); + struct sockaddr_un sock = {}; sock.sun_family = AF_UNIX; - snprintf(sock.sun_path, sizeof(sock.sun_path), "%s/dex2oat.sock", kTmpDir + 12); + strlcpy(sock.sun_path + 1, kSockName, sizeof(sock.sun_path) - 1); int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (-1 == connect(sock_fd, (struct sockaddr *) &sock, sizeof(sock))) { - PLOGE("failed to connect to %s", sock.sun_path); + size_t len = sizeof(sa_family_t) + strlen(sock.sun_path + 1) + 1; + if (connect(sock_fd, (struct sockaddr *) &sock, len)) { + PLOGE("failed to connect to %s", sock.sun_path + 1); return 1; } 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); - LOGD("sock: %s %d", sock.sun_path, stock_fd); + LOGD("sock: %s %d", sock.sun_path + 1, stock_fd); const char *new_argv[argc + 2]; for (int i = 0; i < argc; i++) new_argv[i] = argv[i]; diff --git a/gradle.properties b/gradle.properties index 0dfa0785..080ba356 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,4 @@ android.useAndroidX=true agpVersion=7.4.1 navVersion=2.5.3 -kotlinVersion=1.8.0 +kotlinVersion=1.8.10 diff --git a/magisk-loader/build.gradle.kts b/magisk-loader/build.gradle.kts index 4a7b0137..f05a7a78 100644 --- a/magisk-loader/build.gradle.kts +++ b/magisk-loader/build.gradle.kts @@ -22,7 +22,7 @@ import org.apache.tools.ant.filters.FixCrLfFilter import org.apache.tools.ant.filters.ReplaceTokens import java.io.ByteArrayOutputStream import java.security.MessageDigest -import java.util.* +import java.util.Locale plugins { id("com.android.application") @@ -87,7 +87,6 @@ android { arguments += "-DAPI=${name.toLowerCase()}" } } - buildConfigField("String", "API", """"$name"""") } create("Riru") { @@ -148,7 +147,7 @@ fun afterEval() = android.applicationVariants.forEach { variant -> into(magiskDir) from("${rootProject.projectDir}/README.md") from("$projectDir/magisk_module") { - exclude("riru.sh", "module.prop", "customize.sh", "sepolicy.rule", "daemon") + exclude("riru.sh", "module.prop", "customize.sh", "daemon") } from("$projectDir/magisk_module") { include("module.prop") @@ -178,7 +177,7 @@ fun afterEval() = android.applicationVariants.forEach { variant -> } if (flavorLowered == "riru") { from("${projectDir}/magisk_module") { - include("riru.sh", "sepolicy.rule") + include("riru.sh") val tokens = mapOf( "RIRU_MODULE_LIB_NAME" to "lspd", "RIRU_MODULE_API_VERSION" to moduleMaxRiruApiVersion.toString(), @@ -294,7 +293,7 @@ val reRunDaemon = task("reRunDaemon") { dependsOn(pushDaemon, pushDaemonNative, killLspd) // tricky to pass a minus number to avoid the injection warning commandLine( - adb, "shell", "ASH_STANDALONE=1", "su", "-pc", + adb, "shell", "ASH_STANDALONE=1", "su", "-mm", "-pc", "/data/adb/magisk/busybox sh /data/adb/modules/*_lsposed/service.sh --system-server-max-retry=-1&" ) isIgnoreExitValue = true diff --git a/magisk-loader/magisk_module/customize.sh b/magisk-loader/magisk_module/customize.sh index 733d684c..c920331b 100644 --- a/magisk-loader/magisk_module/customize.sh +++ b/magisk-loader/magisk_module/customize.sh @@ -82,6 +82,7 @@ extract "$ZIPFILE" 'module.prop' "$MODPATH" extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH" extract "$ZIPFILE" 'service.sh' "$MODPATH" extract "$ZIPFILE" 'uninstall.sh' "$MODPATH" +extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH" extract "$ZIPFILE" 'framework/lspd.dex' "$MODPATH" extract "$ZIPFILE" 'daemon.apk' "$MODPATH" extract "$ZIPFILE" 'daemon' "$MODPATH" @@ -110,7 +111,6 @@ if [ "$FLAVOR" == "zygisk" ]; then fi fi elif [ "$FLAVOR" == "riru" ]; then - extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH" mkdir "$MODPATH/riru" mkdir "$MODPATH/riru/lib" mkdir "$MODPATH/riru/lib64" @@ -172,19 +172,16 @@ if [ "$API" -ge 29 ]; then fi ui_print "- Patching binaries" - DEV_PATH=$(tr -dc 'a-f0-9' < /dev/urandom | head -c 16) - while [ -d "/dev/$DEV_PATH" ]; do - DEV_PATH=$(tr -dc 'a-f0-9' < /dev/urandom | head -c 16) - done - sed -i "s/placeholder_\/dev\/................/placeholder_\/dev\/$DEV_PATH/g" "$MODPATH/daemon.apk" - sed -i "s/placeholder_\/dev\/................/placeholder_\/dev\/$DEV_PATH/" "$MODPATH/bin/dex2oat32" - sed -i "s/placeholder_\/dev\/................/placeholder_\/dev\/$DEV_PATH/" "$MODPATH/bin/dex2oat64" + DEV_PATH=$(tr -dc 'a-z0-9' < /dev/urandom | head -c 32) + sed -i "s/5291374ceda0aef7c5d86cd2a4f6a3ac/$DEV_PATH/g" "$MODPATH/daemon.apk" + sed -i "s/5291374ceda0aef7c5d86cd2a4f6a3ac/$DEV_PATH/" "$MODPATH/bin/dex2oat32" + sed -i "s/5291374ceda0aef7c5d86cd2a4f6a3ac/$DEV_PATH/" "$MODPATH/bin/dex2oat64" else extract "$ZIPFILE" 'system.prop' "$MODPATH" fi set_perm_recursive "$MODPATH" 0 0 0755 0644 -set_perm_recursive "$MODPATH/bin" 0 2000 0755 0755 u:object_r:dex2oat_exec:s0 +set_perm_recursive "$MODPATH/bin" 0 2000 0755 0755 u:object_r:magisk_file:s0 chmod 0744 "$MODPATH/daemon" if [ "$(grep_prop ro.maple.enable)" == "1" ] && [ "$FLAVOR" == "zygisk" ]; then diff --git a/magisk-loader/magisk_module/daemon b/magisk-loader/magisk_module/daemon index 8187d2aa..fa00052b 100644 --- a/magisk-loader/magisk_module/daemon +++ b/magisk-loader/magisk_module/daemon @@ -1,7 +1,6 @@ #!/system/bin/sh dir=${0%/*} -magiskPath=$(magisk --path) tmpLspdApk="/data/local/tmp/daemon.apk" debug=@DEBUG@ flavor=@FLAVOR@ diff --git a/magisk-loader/magisk_module/sepolicy.rule b/magisk-loader/magisk_module/sepolicy.rule index c7efd94a..e0b8c560 100644 --- a/magisk-loader/magisk_module/sepolicy.rule +++ b/magisk-loader/magisk_module/sepolicy.rule @@ -1 +1 @@ -allow system_server system_server process execmem +allow dex2oat dex2oat_exec file execute_no_trans diff --git a/magisk-loader/src/main/java/org/lsposed/lspd/service/BridgeService.java b/magisk-loader/src/main/java/org/lsposed/lspd/service/BridgeService.java index 71fcd4e1..5e292450 100644 --- a/magisk-loader/src/main/java/org/lsposed/lspd/service/BridgeService.java +++ b/magisk-loader/src/main/java/org/lsposed/lspd/service/BridgeService.java @@ -85,7 +85,7 @@ public class BridgeService { try { IApplicationThread at = ActivityThread.currentActivityThread().getApplicationThread(); Context ctx = ActivityThread.currentActivityThread().getSystemContext(); - service.dispatchSystemServerContext(at.asBinder(), Context_getActivityToken(ctx), BuildConfig.API); + service.dispatchSystemServerContext(at.asBinder(), Context_getActivityToken(ctx), BuildConfig.FLAVOR); } catch (Throwable e) { Log.e(TAG, "dispatch context: ", e); }