Magisk hide/Denylist 核心原理分析 ROOT隐藏的实现浅论

news2024/11/18 15:50:20

在这里插入图片描述

前言

当手机安装magisk后,全局的挂载空间会受到变更,magisk给我们挂载上了一个su二进制,这就是我们能够访问到su命令的原因
无论是Magisk hide还是Denylist,我们都可以将它们的工作分成两个部分,第一个部分是如何监控安卓进程的启动第二部分是在安卓进程启动(fork)之后,尽快移除已经“污染”的挂载空间

ROOT隐藏关乎到magisk的核心原理mount
我们可以从linux内核获取到mount的相关信息

全局挂载空间

/proc/mounts
/proc/mountinfo
/proc/mountstats

进程的挂载空间

/proc/pid/mounts

如果我们成功刷入magisk, 以下命令将会有大量的挂载信息输出

cat /proc/mounts |grep magisk

在linux系统中,mount有好多种
我们注意关注的是“mount --bind”
mount --bind命令来将两个目录连接起来,mount --bind命令是将前一个目录挂载到后一个目录上,所有对后一个目录的访问其实都是对前一个目录的访问
就是说,我们可以给文件目录戴上一个“面具”,应用程序访问文件,首先访问到的是“面具层”,而不是真正的文件。

程序由数据和代码组成,但是,我们也可以说,数据和代码都是基于“文件”, 因此对文件的改动,就能改变程序的运行~
所以我们可以面具实现许许多多的黑科技

在root下的程序可以切换到任意进程的命名空间,进行mount(umount)操作

如果我们使用momo检测,我们可以发现magisk这套检测并非运行得很完美

  1. magisk hide 的ptrace容易被检测到
  2. magisk hide 无法对isolated_zygote进程处理,因为isolated_zygote与zygote进程共享挂载空间,如果对isolated_zygote进程进行处理,那么后面所有打开的app无法访问到su,即无法获取到root权限。因此,出现一个名为riru_unshare的插件可以使用unshare函数指定独立进程不与zygote共享命名空间。
  3. 还有一些可以绕过早期magisk hide(18)的方法,就是自定义安卓的进程名后面加两个…, 如微信的“:hotpot…”进程会被magisk_hide忽略为无效的隐藏进程而被magisk hide忽视。

最常见检测安卓设备是否已ROOT的方法

public static boolean isDeviceRooted() {
    String[] paths = {
            "/system/app/Superuser.apk",
            "/sbin/su",
            "/system/bin/su",
            "/system/xbin/su",
            "/data/local/xbin/su",
            "/data/local/bin/su",
            "/system/sd/xbin/su",
            "/system/bin/failsafe/su",
            "/data/local/su",
            "/su/bin/su"
    };

    for (String path : paths) {
        if (new File(path).exists()) {
            return true;
        }
    }

    return false;
}

上述方法在magisk时代下,几乎毫无作用的,因为我们可以使用面具随机地让某个指定程序看到哪些文件,看不到哪些文件。

magisk hide/denylist 的源码分析

在magisk 23版本之前, 使用magisk hide进行隐藏, magisk hide使用ptrace监控进程启动,如果是zygote进程且非isolated_zygote进程,则暂停进程,进行umount操作,再继续运行进程

Part1 监控安卓进程的启动
void proc_monitor() {
    monitor_thread = pthread_self();

    // *******
    setup_inotify();

    // First try find existing zygotes
    check_zygote();
    if (!is_zygote_done()) {
        // Periodic scan every 250ms
        timeval val { .tv_sec = 0, .tv_usec = 250000 };
        itimerval interval { .it_interval = val, .it_value = val };
        setitimer(ITIMER_REAL, &interval, nullptr);
    }

    for (int status;;) {
        pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr);

        const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD);
        if (pid < 0) {
            if (errno == ECHILD) {
                // Nothing to wait yet, sleep and wait till signal interruption
                LOGD("proc_monitor: nothing to monitor, wait for signal\n");
                struct timespec ts = {
                    .tv_sec = INT_MAX,
                    .tv_nsec = 0
                };
                nanosleep(&ts, nullptr);
            }
            continue;
        }

        pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr);

        if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */)
            DETACH_AND_CONT;

        int event = WEVENT(status);
        int signal = WSTOPSIG(status);

        if (signal == SIGTRAP && event) {
            unsigned long msg;
            xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg);
            if (zygote_map.count(pid)) {
                // Zygote event
                switch (event) {
                    case PTRACE_EVENT_FORK:
                    case PTRACE_EVENT_VFORK:
                        PTRACE_LOG("zygote forked: [%lu]\n", msg);
                        attaches[msg] = true;
                        break;
                    case PTRACE_EVENT_EXIT:
                        PTRACE_LOG("zygote exited with status: [%lu]\n", msg);
                        [[fallthrough]];
                    default:
                        zygote_map.erase(pid);
                        DETACH_AND_CONT;
                }
            } else {
                switch (event) {
                    case PTRACE_EVENT_CLONE:
                        PTRACE_LOG("create new threads: [%lu]\n", msg);
                        if (attaches[pid] && check_pid(pid))
                            continue;
                        break;
                    case PTRACE_EVENT_EXEC:
                    case PTRACE_EVENT_EXIT:
                        PTRACE_LOG("exit or execve\n");
                        [[fallthrough]];
                    default:
                        DETACH_AND_CONT;
                }
            }
            xptrace(PTRACE_CONT, pid);
        } else if (signal == SIGSTOP) {
            if (!attaches[pid]) {
                // Double check if this is actually a process
                attaches[pid] = is_process(pid);
            }
            if (attaches[pid]) {
                // This is a process, continue monitoring
                PTRACE_LOG("SIGSTOP from child\n");
                xptrace(PTRACE_SETOPTIONS, pid, nullptr,
                        PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT);
                xptrace(PTRACE_CONT, pid);
            } else {
                // This is a thread, do NOT monitor
                PTRACE_LOG("SIGSTOP from thread\n");
                DETACH_AND_CONT;
            }
        } else {
            // Not caused by us, resend signal
            xptrace(PTRACE_CONT, pid, nullptr, signal);
            PTRACE_LOG("signal [%d]\n", signal);
        }
    }
Part2 移除已经污染对挂载空间
static bool check_pid(int pid) {
    char path[128];
    char cmdline[1024];
    struct stat st;

    sprintf(path, "/proc/%d", pid);
    if (stat(path, &st)) {
        // Process died unexpectedly, ignore
        detach_pid(pid);
        return true;
    }

    int uid = st.st_uid;

    // UID hasn't changed
    if (uid == 0)
        return false;

    sprintf(path, "/proc/%d/cmdline", pid);
    if (auto f = open_file(path, "re")) {
        fgets(cmdline, sizeof(cmdline), f.get());
    } else {
        // Process died unexpectedly, ignore
        detach_pid(pid);
        return true;
    }

    if (cmdline == "zygote"sv || cmdline == "zygote32"sv || cmdline == "zygote64"sv ||
        cmdline == "usap32"sv || cmdline == "usap64"sv)
        return false;

    if (!is_hide_target(uid, cmdline, 95))
        goto not_target;

    // Ensure ns is separated
    read_ns(pid, &st);
    for (auto &zit : zygote_map) {
        if (zit.second.st_ino == st.st_ino &&
            zit.second.st_dev == st.st_dev) {
            // ns not separated, abort
            LOGW("proc_monitor: skip [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
            goto not_target;
        }
    }

    // Detach but the process should still remain stopped
    // The hide daemon will resume the process after hiding it
    LOGI("proc_monitor: [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
    detach_pid(pid, SIGSTOP);
    hide_daemon(pid); // 对指定pid进程进行隐藏
    return true;

not_target:
    PTRACE_LOG("[%s] is not our target\n", cmdline);
    detach_pid(pid);
    return true;
}

在magisk 25版本之后,使用denylist,如果你要使用denylist,那需要先需要开启zygote,因为denylist通过hook zygote的fork函数获知进程的启动。
denylist与magisk hide也是一样,都是对污染的命名空间进行卸载,但有一点不同,denylist是在自身的进程执行卸载代码,而magisk_hide在magiskd进程进行操作,需要先使用__switch_to_ns__切换到目标进程的命名空间。

// Unmount stuffs in the process's private mount namespace
DCL_HOOK_FUNC(int, unshare, int flags) {
    int res = old_unshare(flags);
    if (g_ctx && (flags & CLONE_NEWNS) != 0 && res == 0 &&
        // For some unknown reason, unmounting app_process in SysUI can break.
        // This is reproducible on the official AVD running API 26 and 27.
        // Simply avoid doing any unmounts for SysUI to avoid potential issues.
        g_ctx->process && g_ctx->process != "com.android.systemui"sv) {
        if (g_ctx->flags[DO_REVERT_UNMOUNT]) {
            revert_unmount();
        } else {
            umount2("/system/bin/app_process64", MNT_DETACH);
            umount2("/system/bin/app_process32", MNT_DETACH);
        }
        // Restore errno back to 0
        errno = 0;
    }
    return res;
}

Yyds.Msu 的设计

Yyds.Msu与magisk hide反其道而行之。大道至简,尽可能让方案更加简单才能更稳定。

  1. Yyds.Msu 是先卸载,默认应用是没有污染命名空间的,也就是说,直接对zygote以及zygote64进程进行命名空间卸载。
  2. Yyds.Msu 在不使用zygote hook与ptrace的情况下,是无法监听其它app进程的启动的,但可以想办法监听到yyds.msu管理apk的启动,那就是inotify,我们只需要让yyds.msu管理apk在Application时候创建一个文件,即可让我们魔改过的magiskd知晓到并进行挂载。
  3. 缺点是我们无法让其它app打开即进行挂载获得到root权限,在不进行任何hook的情况下除非我们死循环地地扫描/proc目录。
  4. 在Yyds.Msu中我们手动使用超级运行对指定进程直接挂载root权限以及相关magisk bin,但是似乎对某些app来说获取root权限有些慢,我们直接对zygote进程挂载root权限并在app启动成功后恢复zygote的正常挂载,这个就是超级运行的兼容模式。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/670233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue2中引入天地图及相关配置

前言 项目中需要引入特殊用途的地图&#xff0c;发现天地图比高德地图、百度地图要更符合需求&#xff0c;于是看了看天地图。 正文 vue2项目中如何引入天地图并对相关的配置进行修改使用呢&#xff1f;官方给的4.0版本的使用说明。 引入&#xff1a; 进入到public/index.html中…

使用逻辑回归LogisticRegression来对我们自己的数据excel或者csv数据进行分类--------python程序代码,可直接运行

文章目录 一、逻辑回归LogisticRegression是什么&#xff1f;二、逻辑回归LogisticRegression进行分类的具体步骤二、逻辑回归LogisticRegression进行二分类的详细代码三、逻辑回归LogisticRegression的广泛用途总结 一、逻辑回归LogisticRegression是什么&#xff1f; 逻辑回…

小白白也能学会的 PyQt 教程 —— QRadioButton 介绍以及基本使用

文章目录 一、QRadioButton快速入门1. QRadioButton简介2. QRadioButton快速上手 二、响应单选按钮点击事件1、信号和槽机制&#xff1a;2、创建槽函数来响应单选按钮点击&#xff1a;3、示例&#xff1a;执行特定操作或显示相关内容&#xff1a; 三、单选按钮的常用功能和属性…

三维形体投影面积

&#x1f388; 算法并不一定都是很难的题目&#xff0c;也有很多只是一些代码技巧&#xff0c;多进行一些算法题目的练习&#xff0c;可以帮助我们开阔解题思路&#xff0c;提升我们的逻辑思维能力&#xff0c;也可以将一些算法思维结合到业务代码的编写思考中。简而言之&#…

petalinux 生成SDK报错排除

AAA: 在项目文件下新建Qt5文件夹文件夹内新建文件并且设置对应参数 文件夹路径&#xff1a; project-spec/meta-user/recipes-qt/qt5 新建文件 vim ./qt5/qt3d_%.bbappend vim ./qt5/qtquickcontrols2_%.bbappend vim ./qt5/qtserialbus_%.bbappend 文件内容 qt3d_%.bbap…

完美解决Non-terminating decimal expansion; no exact representable decimal result.异常

我们在使用BigDecimal进行精确计算时常常会出现Non-terminating decimal expansion; no exact representable decimal result.异常。 出现这个异常的原因在于 BigDecimal 是不可变的、任意精度的有符号十进制数&#xff0c;所以可以做精确计算。但在除法中&#xff0c;准确的商…

ernie-layout笔记

1: 识别文档中文字以及准确的对这些文字排序是必须的一步骤 采用 OCR技术识别文字以及对应的图像坐标信息&#xff0c;光栅扫描以生成输入序列按照从左到右&#xff0c;从上到下的顺序&#xff1b;但是以上方法针对复杂的结构就会出现问题&#xff1b;因此文章使用了Document-P…

Spring源码核心剖析 | 京东云技术团队

前言 SpringAOP作为Spring最核心的能力之一&#xff0c;其重要性不言而喻。然后需要知道的是AOP并不只是Spring特有的功能&#xff0c;而是一种思想&#xff0c;一种通用的功能。而SpringAOP只是在AOP的基础上将能力集成到SpringIOC中&#xff0c;使其作为bean的一种&#xff…

算法程序设计 之 循环赛日程表(2/8)

一、实验目的&#xff1a; 理解并掌握分治算法的基本思想和设计步骤。 二、实验内容 设有n个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表&#xff1a; &#xff08;1&#xff09;每个选手必须与其他n-1个选手各赛一次&#xff1b; &#xff08;2&#xff0…

HOOPS Web SDK 2023 Crack

在 HOOPS WEB 平台上释放 3D 的力量 HOOPS Web 平台加速 Web 应用程序开发&#xff0c;提供先进的 3D Web 可视化、准确快速的 CAD 数据访问和 3D 数据发布软件开发工具包 &#xff08;SDK&#xff09;。 构建 3D WEB 应用程序 借助 HOOPS Web 平台&#xff0c;快速构建适用于…

值得收藏的 10个 Android 手机恢复丢失文件的工具榜单

尽管我们尽可能避免这种情况&#xff0c;但有时我们还是会不小心删除 Android 设备上的重要文件。无论是照片、视频、文档还是任何其他形式的数据&#xff0c;数据丢失都会带来巨大的痛苦。不幸的是&#xff0c;Android 设备没有内置恢复工具。但是&#xff0c;有一些第三方恢复…

里程碑式突破!关键的薛定谔猫编码能带来更好的量子比特

​ 薛定谔的猫编码插图&#xff08;图片来源&#xff1a;网络&#xff09; 来自瑞士洛桑联邦理工学院&#xff08;EPFL&#xff09;的科学家提出了一种突破性的量子计算容错方案&#xff0c;称为“关键的薛定谔猫编码”。这种新颖的系统在混合状态下运行&#xff0c;具有强大的…

容灾与备份区别、灾备技术、容灾体系规划

1.容灾备份的区别 容灾 &#xff08;Disaster Tolerance&#xff09;&#xff1a;就是在上述的灾难发生时&#xff0c;在保证生产系统的数据尽量少丢失的情况下&#xff0c;保持生存系统的业务不间断地运行。 容错 &#xff08;Fault Tolerance&#xff09;&#xff1a;指在计…

激光显示技术路线之争:超级全色激光技术ALPD5.0更先进

5月以来,智能投影市场爆发的激光显示技术路线之争愈演愈烈,各厂家带领自有的技术路线你方唱罢我登场,犹如一出愈演愈烈的大戏,吸引了业内外各界的目光。 从极米在5月10日2023春季新品发布会上率先向三色激光技术发难,再到坚果投影首席产品官在朋友圈发文炮轰极米的技术路线,随…

MarkDown常用功能

快捷键 撤销&#xff1a;Ctrl/Command Z 重做&#xff1a;Ctrl/Command Y 加粗&#xff1a;Ctrl/Command B 斜体&#xff1a;Ctrl/Command I 标题&#xff1a;Ctrl/Command Shift H 无序列表&#xff1a;Ctrl/Command Shift U 有序列表&#xff1a;Ctrl/Command Shif…

【二叉树part01】| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代遍历

目录 ✿二叉树的递归遍历❀ ☞LeetCode144.前序遍历 ☞LeetCode145.二叉树的后序遍历 ☞LeetCode94.二叉树的中序遍历 ✿二叉树的迭代遍历❀ ☞LeetCode144.前序遍历 ☞LeetCode145.二叉树的后序遍历 ☞LeetCode94.二叉树的中序遍历 ✿二叉树的统一迭代遍历❀ ☞Lee…

CTFshow-pwn入门-前置基础pwn32-pwn34

FORTIFY_SOURCE FORTIFY_SOURCE(源码增强)&#xff0c;这个其实有点类似与Windows中用新版Visual Studio进行开发的时候&#xff0c;当你用一些危险函数比如strcpy、sprintf、strcat&#xff0c;编译器会提示你用xx_s加强版函数。 FORTIFY_SOURCE本质上一种检查和替换机制&am…

算法程序设计 之 矩阵连乘(3/8)

一、实验目的&#xff1a; 理解动态规划算法的基本思想和设计步骤&#xff1b; 掌握动态规划算法的典型应用范例——矩阵连乘。 二、实验内容 矩阵连乘 给定n个可乘的数字矩阵A1,…,An&#xff0c;以及矩阵的阶p0* p1, p1* p2,…, pn-1* pn,求给定矩阵链的最优计算次序使得所需…

JavaWeb之文件的上传和下载

文章目录 文件的上传基本介绍文件上传的HTTP协议的说明commons-fileupload.jar 常用API介绍说明fileupload类库的使用 文件的下载基本介绍和使用说明中文名乱码问题解决方案 文件的上传和下载&#xff0c;是非常常见的功能。很多的系统中&#xff0c;或者软件中都经常使用文件的…

使用spacy做分词的示例

下载数据&#xff1a; aws s3 cp s3://applied-nlp-book/data/ data --recursive --no-sign-request aws s3 cp s3://applied-nlp-book/models/ag_dataset/ models/ag_dataset --recursive --no-sign-request 上面第一份数据接近1GB&#xff0c;第二份接近3GB&#xff1b; 示…