diff --git a/app/src/main/cpp/detector/root_detector.cpp b/app/src/main/cpp/detector/root_detector.cpp index 4de4f0c..5553f41 100644 --- a/app/src/main/cpp/detector/root_detector.cpp +++ b/app/src/main/cpp/detector/root_detector.cpp @@ -30,6 +30,11 @@ const std::vector& RootDetector::getSuPaths() { const std::vector& RootDetector::getMagiskPaths() { static std::vector paths = { "/sbin/.magisk", + "/sbin/.magisk/mirror", + "/sbin/.magisk/block", + "/sbin/.core", // Legacy Magisk (v15-v18) core directory + "/sbin/.core/mirror", // Legacy Magisk mirror mount + "/sbin/.core/img", // Legacy Magisk module image "/data/adb/magisk", "/data/adb/magisk.img", "/data/adb/magisk.db", @@ -190,6 +195,8 @@ bool RootDetector::checkSuspiciousMountsNative() { // Note: overlay and tmpfs are normal system mounts, don't check them const std::vector suspiciousPatterns = { "magisk", + "/sbin/.magisk/", // Magisk working directory + "/sbin/.core/", // Legacy Magisk (v15-v18) core directory "zygisk", "zygisksu", "kernelsu", @@ -321,6 +328,8 @@ bool RootDetector::checkSuspiciousMountsSyscall() { // Note: overlay and tmpfs are normal system mounts, don't check them const std::vector suspiciousPatterns = { "magisk", + "/sbin/.magisk/", // Magisk working directory + "/sbin/.core/", // Legacy Magisk (v15-v18) core directory "zygisk", "zygisksu", "zygisk_su", @@ -388,6 +397,81 @@ bool RootDetector::checkBuildTags() { return content.find("ro.build.tags=test-keys") != std::string::npos; } +// ===================== /proc/mounts Magisk Detection ===================== +// Read /proc//mounts via direct syscall, search for Magisk signatures + +bool RootDetector::checkMountsForMagiskNative() { + std::ifstream file("/proc/self/mounts"); + if (!file.is_open()) return false; + + std::string line; + while (std::getline(file, line)) { + if (line.find("/sbin/.magisk/") != std::string::npos || + line.find("magisk") != std::string::npos || + line.find("/sbin/.core/") != std::string::npos) { + LOGD("Magisk mount signature found (native): %s", line.c_str()); + return true; + } + } + return false; +} + +bool RootDetector::checkMountsForMagiskSyscall() { + std::string content = syscall_read_file("/proc/self/mounts", 32768); + if (content.empty()) return false; + + if (content.find("/sbin/.magisk/") != std::string::npos || + content.find("magisk") != std::string::npos || + content.find("/sbin/.core/") != std::string::npos) { + LOGD("Magisk mount signature found (syscall)"); + return true; + } + return false; +} + +// ===================== Zygote Context Detection ===================== +// Read /proc//attr/prev, search for "zygote" in SELinux context +// Normal Android app processes are forked from zygote, so attr/prev +// should contain the zygote's SELinux context (e.g. "u:r:zygote:s0") + +bool RootDetector::checkZygoteContextNative() { + std::ifstream file("/proc/self/attr/prev"); + if (!file.is_open()) return false; + + std::string content; + std::getline(file, content); + + // Normal: should contain "zygote" (e.g. "u:r:zygote:s0") + if (content.find("zygote") != std::string::npos) { + return false; // Normal - process was forked from zygote + } + + // Abnormal: attr/prev does not contain zygote context + // This may indicate the process was spawned abnormally + LOGD("Abnormal zygote context (native): %s", content.c_str()); + return true; +} + +bool RootDetector::checkZygoteContextSyscall() { + std::string content = syscall_read_file("/proc/self/attr/prev", 256); + + // If we can't read attr/prev, try attr/current as fallback reference + if (content.empty()) { + content = syscall_read_file("/proc/self/attr/current", 256); + } + + if (content.empty()) return false; + + // Normal: should contain "zygote" (e.g. "u:r:zygote:s0") + if (content.find("zygote") != std::string::npos) { + return false; // Normal - process was forked from zygote + } + + // Abnormal: context does not contain zygote + LOGD("Abnormal zygote context (syscall): %s", content.c_str()); + return true; +} + bool RootDetector::checkSelinuxStatus() { std::string content = syscall_read_file("/sys/fs/selinux/enforce"); return !content.empty() && content[0] == '0'; diff --git a/app/src/main/cpp/detector/root_detector.h b/app/src/main/cpp/detector/root_detector.h index d0206e0..785bbc4 100644 --- a/app/src/main/cpp/detector/root_detector.h +++ b/app/src/main/cpp/detector/root_detector.h @@ -76,6 +76,14 @@ class RootDetector { static bool checkRootHidingSyscall(); static bool checkSuspiciousMountsSyscall(); + // /proc/mounts Magisk signature detection + static bool checkMountsForMagiskNative(); + static bool checkMountsForMagiskSyscall(); + + // Zygote SELinux context detection via /proc/self/attr/prev + static bool checkZygoteContextNative(); + static bool checkZygoteContextSyscall(); + // System property checks static bool checkBuildTags(); static bool checkSelinuxStatus(); diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 7e227b2..f123d66 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -143,6 +143,28 @@ Java_com_xff_launch_detector_NativeDetector_checkMountInfoSyscall(JNIEnv *env, j return RootDetector::checkMountInfoSyscall(); } +// /proc/mounts Magisk signature detection +JNIEXPORT jboolean JNICALL +Java_com_xff_launch_detector_NativeDetector_checkMountsForMagiskNative(JNIEnv *env, jobject thiz) { + return RootDetector::checkMountsForMagiskNative(); +} + +JNIEXPORT jboolean JNICALL +Java_com_xff_launch_detector_NativeDetector_checkMountsForMagiskSyscall(JNIEnv *env, jobject thiz) { + return RootDetector::checkMountsForMagiskSyscall(); +} + +// Zygote SELinux context detection via /proc/self/attr/prev +JNIEXPORT jboolean JNICALL +Java_com_xff_launch_detector_NativeDetector_checkZygoteContextNative(JNIEnv *env, jobject thiz) { + return RootDetector::checkZygoteContextNative(); +} + +JNIEXPORT jboolean JNICALL +Java_com_xff_launch_detector_NativeDetector_checkZygoteContextSyscall(JNIEnv *env, jobject thiz) { + return RootDetector::checkZygoteContextSyscall(); +} + // ===================== Hook Detection ===================== JNIEXPORT jboolean JNICALL diff --git a/app/src/main/java/com/xff/launch/detector/NativeDetector.java b/app/src/main/java/com/xff/launch/detector/NativeDetector.java index 922c12c..b292941 100644 --- a/app/src/main/java/com/xff/launch/detector/NativeDetector.java +++ b/app/src/main/java/com/xff/launch/detector/NativeDetector.java @@ -36,6 +36,14 @@ public class NativeDetector { public native boolean checkMountInfoNative(); public native boolean checkMountInfoSyscall(); + /** Check /proc/self/mounts for Magisk mount signatures via direct syscall */ + public native boolean checkMountsForMagiskNative(); + public native boolean checkMountsForMagiskSyscall(); + + /** Check /proc/self/attr/prev for zygote SELinux context anomaly */ + public native boolean checkZygoteContextNative(); + public native boolean checkZygoteContextSyscall(); + // ===================== Hook Detection ===================== public native boolean checkXposedNative(); diff --git a/app/src/main/java/com/xff/launch/detector/RootDetector.java b/app/src/main/java/com/xff/launch/detector/RootDetector.java index 62d70ed..5b50f2c 100644 --- a/app/src/main/java/com/xff/launch/detector/RootDetector.java +++ b/app/src/main/java/com/xff/launch/detector/RootDetector.java @@ -334,6 +334,83 @@ public DetectionItem detectSuspiciousMounts() { return item; } + /** + * Detect Magisk mount signatures in /proc/self/mounts + * Uses direct syscall to bypass libc hooks + */ + public DetectionItem detectMountsForMagisk() { + DetectionItem item = new DetectionItem("Mounts Magisk 签名", + "通过 /proc/mounts 搜索 Magisk 挂载特征"); + + // Native layer + boolean nativeResult = nativeDetector.checkMountsForMagiskNative(); + item.setLayerResult(DetectionLayer.NATIVE, nativeResult); + + // Syscall layer + boolean syscallResult = nativeDetector.checkMountsForMagiskSyscall(); + item.setLayerResult(DetectionLayer.SYSCALL, syscallResult); + + if (item.getMostTrustworthyResult()) { + item.setStatus(DetectionStatus.RISK); + item.setDetail("检测到 Magisk 挂载签名"); + item.addDetectionDetail("💾 /proc/mounts", "Magisk 挂载特征", + "搜索词: /sbin/.magisk/, magisk, /sbin/.core/", + DetectionLayer.SYSCALL, "🔍"); + } else { + item.setStatus(DetectionStatus.SAFE); + item.setDetail("未检测到"); + } + + if (item.hasInconsistentResults()) { + item.setDetail(item.getDetail() + " (检测层不一致)"); + } + + return item; + } + + /** + * Detect zygote SELinux context anomaly via /proc/self/attr/prev + * Uses direct syscall to bypass libc hooks + * Normal app processes should have "zygote" in their prev SELinux context + */ + public DetectionItem detectZygoteContext() { + DetectionItem item = new DetectionItem("Zygote 上下文检测", + "通过 /proc/self/attr/prev 检测 Zygote SELinux 上下文"); + + // Native layer + boolean nativeResult = nativeDetector.checkZygoteContextNative(); + item.setLayerResult(DetectionLayer.NATIVE, nativeResult); + + // Syscall layer + boolean syscallResult = nativeDetector.checkZygoteContextSyscall(); + item.setLayerResult(DetectionLayer.SYSCALL, syscallResult); + + if (item.getMostTrustworthyResult()) { + item.setStatus(DetectionStatus.WARNING); + item.setDetail("Zygote 上下文异常"); + item.addDetectionDetail("🔑 SELinux 上下文", "/proc/self/attr/prev", + "正常应包含 \"zygote\" (如 u:r:zygote:s0)\n" + + "上下文中未找到 zygote 标识", + DetectionLayer.SYSCALL, "⚠️"); + } else { + item.setStatus(DetectionStatus.SAFE); + item.setDetail("Zygote 上下文正常"); + + // 显示当前 SELinux context 作为参考 + String context = nativeDetector.getSELinuxContextNative(); + if (context != null && !context.isEmpty()) { + item.addDetectionDetail("🔑 SELinux 上下文", "attr/prev", + "当前上下文: " + context, DetectionLayer.NATIVE, "✅"); + } + } + + if (item.hasInconsistentResults()) { + item.setDetail(item.getDetail() + " (检测层不一致)"); + } + + return item; + } + /** * Get all root detection items */ @@ -347,6 +424,8 @@ public List getAllDetections() { items.add(detectRootManagers()); items.add(detectRootHiding()); items.add(detectSuspiciousMounts()); + items.add(detectMountsForMagisk()); + items.add(detectZygoteContext()); return items; }