重读Magisk内部实现细节3

警告
本文最后更新于 2023-06-18,文中内容可能已过时。

Magisk内部实现细节的第三篇,主要通过源码来了解下Magisk Hide的原理,这部分代码在native/jni/magiskhide当中

不管是在Magisk Manager中管理Magisk Hide

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
object MagiskHide : BaseSettingsItem.Toggle() {
    override val title = R.string.magiskhide.asText()
    override val description = R.string.settings_magiskhide_summary.asText()
    override var value = Config.magiskHide
        set(value) = setV(value, field, { field = it }) {
            val cmd = if (it) "enable" else "disable"
            Shell.su("magiskhide $cmd").submit { cb ->
                if (cb.isSuccess) Config.magiskHide = it
                else field = !it
            }
        }
}

还是通过adb shell来管理Magisk Hide

1
2
3
4
5
6
7
8
(base)  大慈大悲观世音菩萨  ~  adb shell
selene:/ $ su
selene:/ # magiskhide status
MagiskHide is not enabled
1|selene:/ # magiskhide enable
selene:/ # magiskhide status
MagiskHide is enabled
selene:/ #

其底层都是通过magiskhide这个二进制文件来触发的,而magiskhide的入口是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// native/jni/magiskhide/magiskhide.cpp
// 入口函数
int magiskhide_main(int argc, char *argv[]) {
    if (argc < 2)
        usage(argv[0]);

    // CLI backwards compatibility
    const char *opt = argv[1];
    if (opt[0] == '-' && opt[1] == '-')
        opt += 2;

    int req;
    // 选择触发的指令
    if (opt == "enable"sv)
        req = LAUNCH_MAGISKHIDE;
    else if (opt == "disable"sv)
        req = STOP_MAGISKHIDE;
    else if (opt == "add"sv)
        req = ADD_HIDELIST;
    else if (opt == "rm"sv)
        req = RM_HIDELIST;
    else if (opt == "ls"sv)
        req = LS_HIDELIST;
    else if (opt == "status"sv)
        req = HIDE_STATUS;
    else if (opt == "exec"sv && argc > 2) {
        xunshare(CLONE_NEWNS);
        xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr);
        hide_unmount();
        execvp(argv[2], argv + 2);
        exit(1);
    }
#if 0 && !ENABLE_INJECT
    else if (opt == "test"sv)
        test_proc_monitor();
#endif
    else
        usage(argv[0]);
    // 同样需要和daemon进行交互
    // Send request
    int fd = connect_daemon();
    write_int(fd, MAGISKHIDE);
    write_int(fd, req);
    if (req == ADD_HIDELIST || req == RM_HIDELIST) {
        write_string(fd, argv[2]);
        write_string(fd, argv[3] ? argv[3] : "");
    }

    // Get response
    int code = read_int(fd);
    switch (code) {
    case DAEMON_SUCCESS:
        break;
    case HIDE_NOT_ENABLED:
        fprintf(stderr, "MagiskHide is not enabled\n");
        goto return_code;
    case HIDE_IS_ENABLED:
        fprintf(stderr, "MagiskHide is enabled\n");
        goto return_code;
    case HIDE_ITEM_EXIST:
        fprintf(stderr, "Target already exists in hide list\n");
        goto return_code;
    case HIDE_ITEM_NOT_EXIST:
        fprintf(stderr, "Target does not exist in hide list\n");
        goto return_code;
    case HIDE_NO_NS:
        fprintf(stderr, "Your kernel doesn't support mount namespace\n");
        goto return_code;
    case HIDE_INVALID_PKG:
        fprintf(stderr, "Invalid package / process name\n");
        goto return_code;
    case ROOT_REQUIRED:
        fprintf(stderr, "Root is required for this operation\n");
        goto return_code;
    case DAEMON_ERROR:
    default:
        fprintf(stderr, "Daemon error\n");
        return DAEMON_ERROR;
    }

    if (req == LS_HIDELIST) {
        string res;
        for (;;) {
            read_string(fd, res);
            if (res.empty())
                break;
            printf("%s\n", res.data());
        }
    }

return_code:
    return req == HIDE_STATUS ? (code == HIDE_IS_ENABLED ? 0 : 1) : code != DAEMON_SUCCESS;

而对于daemon进程来说,处理magiskhide传来的指令,具体的处理逻辑还是在magiskhide.cpp中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// native/jni/core/daemon.cpp
static void request_handler(int client, int req_code, ucred cred) {
    switch (req_code) {
        case MAGISKHIDE:
            magiskhide_handler(client, &cred);
            break;
        ......

// native/jni/magiskhide/magiskhide.cpp
void magiskhide_handler(int client, ucred *cred) {
    int req = read_int(client);
    int res = DAEMON_ERROR;

    ......

    switch (req) {
    // magiskhide启动
    case LAUNCH_MAGISKHIDE:
        res = launch_magiskhide(true);
        break;
    // magiskhide关闭
    case STOP_MAGISKHIDE:
        res = stop_magiskhide();
        break;
    // 新增需要隐藏的app
    case ADD_HIDELIST:
        res = add_list(client);
        break;
    // 移除
    case RM_HIDELIST:
        res = rm_list(client);
        break;
    case LS_HIDELIST:
        ls_list(client);
        return;
    case HIDE_STATUS:
        res = hide_enabled() ? HIDE_IS_ENABLED : HIDE_NOT_ENABLED;
        break;
#if ENABLE_INJECT
    case REMOTE_CHECK_HIDE:
        res = check_uid_map(client);
        break;
    case REMOTE_DO_HIDE:
        kill(cred->pid, SIGSTOP);
        write_int(client, 0);
        hide_daemon(cred->pid);
        close(client);
        return;
#endif
    }

    write_int(client, res);
    close(client);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// native/jni/magiskhide/hide_utils.cpp
// 开启magiskhide
int launch_magiskhide(bool late_props) {
    // 锁申请
    mutex_guard lock(hide_state_lock);

    // 判断全局变量hide_state的值,如果已经启动直接返回
    if (hide_state)
        return HIDE_IS_ENABLED;

    // 检测是否有访问namespace的权限
    if (access("/proc/self/ns/mnt", F_OK) != 0)
        return HIDE_NO_NS;

    // 复制procfp
    if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
        return DAEMON_ERROR;

    LOGI("* Enable MagiskHide\n");
    // 初始化hide_set并杀死相关进程
    // Initialize the hide list
    if (!init_list())
        return DAEMON_ERROR;
    // 替换prop属性
    hide_sensitive_props();
    if (late_props)
        // 针对vendor.boot.verifiedbootstate进行替换
        hide_late_sensitive_props();

#if !ENABLE_INJECT
    // Start monitoring
    // 创建监控线程monitor_thread
    if (new_daemon_thread(&proc_monitor))
        return DAEMON_ERROR;
#endif
    // 更新当前magiskhide状态
    hide_state = true;
    // 更新settings里的magiskhide配置
    update_hide_config();
    // 释放锁
    // Unlock here or else we'll be stuck in deadlock
    lock.unlock();
    // 更新uid_proc_map,需要隐藏的app的uid对应进程名
    update_uid_map();
    return DAEMON_SUCCESS;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// native/jni/magiskhide/hide_utils.cpp
int stop_magiskhide() {
    mutex_guard g(hide_state_lock);

    if (hide_state) {
        LOGI("* Disable MagiskHide\n");
        // 清理工作
        uid_proc_map.clear();
        hide_set.clear();
#if !ENABLE_INJECT
        // 向monitor_thread发送自定义信号SIGTERMTHRD
        pthread_kill(monitor_thread, SIGTERMTHRD);
#endif
    }
    // 更新当前magiskhide状态
    hide_state = false;
    // 更新settings里的magiskhide配置
    update_hide_config();
    return DAEMON_SUCCESS;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// native/jni/magiskhide/hide_utils.cpp
int add_list(int client) {
    string pkg = read_string(client);
    string proc = read_string(client);
    int ret = add_list(pkg.data(), proc.data());
    if (ret == DAEMON_SUCCESS)
        // 更新uid_proc_map
        update_uid_map();
    return ret;
}

static int add_list(const char *pkg, const char *proc) {
    if (proc[0] == '\0')
        proc = pkg;

    if (!validate(pkg, proc))
        return HIDE_INVALID_PKG;

    for (auto &hide : hide_set)
        if (hide.first == pkg && hide.second == proc)
            return HIDE_ITEM_EXIST;

    // Add to database
    char sql[4096];
    // 写入hidelist数据表
    snprintf(sql, sizeof(sql),
            "INSERT INTO hidelist (package_name, process) VALUES('%s', '%s')", pkg, proc);
    char *err = db_exec(sql);
    db_err_cmd(err, return DAEMON_ERROR);

    {
        // Critical region
        mutex_guard lock(hide_state_lock);
        // 更新hide_set
        add_hide_set(pkg, proc);
    }

    return DAEMON_SUCCESS;
}

可以看出,首先在Magisk Hide中有三个存储结构用来做Magisk Hide的管理工作

  • hide_set: 存储需要隐藏功能的包名-进程名
  • uid_proc_map: 根据hide_set集合来存储对应App的uid以及进程名映射
  • hidelist: 数据表,供展示时使用

其次,可以看到在Magisk Hide启动时会额外启动monitor_thread这个线程,而这个就是Magisk Hide隐藏功能的核心

跟进monitor_thread

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// native/jni/magiskhide/proc_monitor.cpp

// 设置该线程为monitor_thread,并于后续清理
monitor_thread = pthread_self();

// Backup original mask
// 获取当前线程的信号掩码保存在orin_mask
sigset_t orig_mask;
pthread_sigmask(SIG_SETMASK, nullptr, &orig_mask);

// 清空信号集并初始化
sigset_t unblock_set;
sigemptyset(&unblock_set);
sigaddset(&unblock_set, SIGTERMTHRD);
sigaddset(&unblock_set, SIGIO);
sigaddset(&unblock_set, SIGALRM);

// 设置信号处理函数集合
struct sigaction act{};
sigfillset(&act.sa_mask);
act.sa_handler = SIG_IGN;
sigaction(SIGTERMTHRD, &act, nullptr);
sigaction(SIGIO, &act, nullptr);
sigaction(SIGALRM, &act, nullptr);

// 防止信号积压处理
// Temporary unblock to clear pending signals
pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr);
pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr);

// 使用term_thread来处理SIGTERMTHRD信号
act.sa_handler = term_thread;
sigaction(SIGTERMTHRD, &act, nullptr);
// 使用inotify_event处理SIGIO信号
act.sa_handler = inotify_event;
sigaction(SIGIO, &act, nullptr);
// 使用check_zygote处理SIGALRM信号
act.sa_handler = [](int){ check_zygote(); };
sigaction(SIGALRM, &act, nullptr);

setup_inotify();

static void setup_inotify() {
    // 创建inotify实例时指定了IN_CLOEXEC标志位,表示将inotify实例设置为 close-on-exec 模式。
    // 在close-on-exec模式下,当进程调用exec函数时,inotify实例会自动关闭
    inotify_fd = xinotify_init1(IN_CLOEXEC);
    if (inotify_fd < 0)
        return;

    // Setup inotify asynchronous I/O
    // 设置inotify文件描述符的异步通知和所有权
    fcntl(inotify_fd, F_SETFL, O_ASYNC);
    struct f_owner_ex ex = {
        .type = F_OWNER_TID,
        .pid = gettid()
    };
    fcntl(inotify_fd, F_SETOWN_EX, &ex);

    // 监控/data/system的写入并关闭事件
    // Monitor packages.xml
    inotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE);

    // 监控app_process的被访问的事件,也就是监控App
    // Monitor app_process
    if (access(APP_PROC "32", F_OK) == 0) {
        inotify_add_watch(inotify_fd, APP_PROC "32", IN_ACCESS);
        if (access(APP_PROC "64", F_OK) == 0)
            inotify_add_watch(inotify_fd, APP_PROC "64", IN_ACCESS);
    } else {
        inotify_add_watch(inotify_fd, APP_PROC, IN_ACCESS);
    }
}

这个部分主要做的事是

  • 设置信号处理函数,信号分别是SIGTERMTHRD、SIGIO、SIGALRM
  • 启动inotify,fd写入inotify_fd,监控/system/bin/app_process的access事件,重点在于packages.xml文件的写入
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
check_zygote();
if (!is_zygote_done()) {
    // 如果获取到zygote,则每250ms发送SIGALRM信号触发check_zygote
    // 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);
}

static void check_zygote() {
    crawl_procfs([](int pid) -> bool {
        char buf[512];
        snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
        if (FILE *f = fopen(buf, "re")) {
            fgets(buf, sizeof(buf), f);
            if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1)
                new_zygote(pid);
            fclose(f);
        }
        return true;
    });
    if (is_zygote_done()) {
        // Stop periodic scanning
        timeval val { .tv_sec = 0, .tv_usec = 0 };
        itimerval interval { .it_interval = val, .it_value = val };
        setitimer(ITIMER_REAL, &interval, nullptr);
    }
}

static DIR *procfp;
// procfp在之前已经被赋值成/proc目录
void crawl_procfs(const function<bool(int)> &fn) {
    // 指针重置到目录起始位置
    rewinddir(procfp);
    crawl_procfs(procfp, fn);
}

// 遍历proc目录,获取zygote的pid
void crawl_procfs(DIR *dir, const function<bool(int)> &fn) {
    struct dirent *dp;
    int pid;
    while ((dp = readdir(dir))) {
        pid = parse_int(dp->d_name);
        if (pid > 0 && !fn(pid))
            break;
    }
}

static void new_zygote(int pid) {
    struct stat st;
    // 读取zygote挂载的namespace信息
    if (read_ns(pid, &st))
        return;

    // 更新或者存储st到zygote_map
    auto it = zygote_map.find(pid);
    if (it != zygote_map.end()) {
        // Update namespace info
        it->second = st;
        return;
    }

    LOGD("proc_monitor: ptrace zygote PID=[%d]\n", pid);
    zygote_map[pid] = st;
    // ptrace attach到zygote进程
    xptrace(PTRACE_ATTACH, pid);
    // 等待zygote进程状态变化 
    waitpid(pid, nullptr, __WALL | __WNOTHREAD);
    监控zygote fork/vfork/exit事件
    xptrace(PTRACE_SETOPTIONS, pid, nullptr,
            PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXIT);
    // 恢复zygote进程执行
    xptrace(PTRACE_CONT, pid);
}

这一部分的作用是轮询判断zygote进程是否启动以及ptrace attach到zygote以便于监控到zygote的fork操作(引导启动App进程)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
for (int status;;) {
    // 解除信号阻塞,获取信号
    pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr);
    // 获取待处理的pid
    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;
    // 获取pid的信号和事件类型
    int event = WEVENT(status);
    int signal = WSTOPSIG(status);

    if (signal == SIGTRAP && event) {
        unsigned long msg;
        xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg);
        // 处理zygote消息
        if (zygote_map.count(pid)) {
            // Zygote event
            switch (event) {
                case PTRACE_EVENT_FORK:
                case PTRACE_EVENT_VFORK:
                    PTRACE_LOG("zygote forked: [%lu]\n", msg);
                    // 表示收到的是zygote消息,监控到zygote fork子进程
                    // 此时设置attaches map中app pid的值为true
                    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 {
            // 处理用户App消息
            switch (event) {
                // 表示收到的是子进程的信号,有新的App启动,开始执行隐藏操作
                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);
    }
}

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;
    }
    // 获取进程pid
    int uid = st.st_uid;

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

    // 读取/proc/pid/cmdline的内容到cmdline
    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);
    return true;

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

void hide_daemon(int pid) {
    if (fork_dont_care() == 0) {
        // 关键隐藏动作
        hide_unmount(pid);
        // Send resume signal
        kill(pid, SIGCONT);
        _exit(0);
    }
}

void hide_unmount(int pid) {
    // 切换目标pid的namespace
    if (pid > 0 && switch_mnt_ns(pid))
        return;

    LOGD("hide: handling PID=[%d]\n", pid);

    char val;
    // 读取selinux的模式
    int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
    xxread(fd, &val, sizeof(val));
    close(fd);
    // Permissive
    // 如果是宽容模式,则限制访问
    if (val == '0') {
        chmod(SELINUX_ENFORCE, 0640);
        chmod(SELINUX_POLICY, 0440);
    }

    vector<string> targets;

    // Unmount dummy skeletons and /sbin links
    // android11中为/dev/xxxx
    targets.push_back(MAGISKTMP);
    parse_mnt("/proc/self/mounts", [&](mntent *mentry) {
        if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(product) || TMPFS_MNT(system_ext))
            targets.emplace_back(mentry->mnt_dir);
        return true;
    });

    for (auto &s : reversed(targets))
        lazy_unmount(s.data());
    targets.clear();

    // Unmount all Magisk created mounts
    parse_mnt("/proc/self/mounts", [&](mntent *mentry) {
        if (strstr(mentry->mnt_fsname, BLOCKDIR))
            targets.emplace_back(mentry->mnt_dir);
        return true;
    });

    for (auto &s : reversed(targets))
        lazy_unmount(s.data());
}

核心步骤,上一步已经ptrace attach到zygote,当监听到App启动了,切换到目标进程的namespace并执行umount操作

相关内容