Magisk源码地址:https://github.com/topjohnwu/Magisk,隐藏root功能包含了隐藏某些系统属性,隐藏magisk包名等。本菜抛砖引玉,希望大家讨论其中隐藏su文件的逻辑,以及检测这种情况的方法。代码位于工程的 Magisk/native/jni/magiskhide目录下,这里直接把功能实现核心代码抽离出来,以下是主要部分:
int main(int argc, char** argv) {
DEBUG_PRINT("hideroot start\n");
if (argc
DEBUG_PRINT("usage: ./hideroot {pid}\n");
return 1;
}
pid_t target_pid = atoi(argv[1]);
DEBUG_PRINT("target pid is %d\n", target_pid);
// 1. 暂停目标进程
kill(target_pid, SIGSTOP);
// 2. 关联到目标进程的mnt-namespace
DEBUG_PRINT("hideroot switch_mnt_ns\n");
if (switch_mnt_ns(target_pid)) {
DEBUG_PRINT("");
return 1;
}
// 3. 卸载相关文件系统
DEBUG_PRINT("hideroot read mounts\n");
char buffer[256];
char* line = NULL;
struct vector mount_list;
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", target_pid);
DEBUG_PRINT("hideroot init mount_list\n");
vec_init(&mount_list);
DEBUG_PRINT("hideroot read file\n");
file_to_vector(buffer, &mount_list);
DEBUG_PRINT("hideroot unmount dummy skeletons and /sbin links\n");
// Unmount dummy skeletons and /sbin links
vec_for_each(&mount_list, line) {
if (strstr(line, "tmpfs /system/") || strstr(line, "tmpfs /vendor/") || strstr(line, "tmpfs /sbin")) {
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
free(line);
}
vec_destroy(&mount_list);
// Re-read mount infos
snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", target_pid);
vec_init(&mount_list);
file_to_vector(buffer, &mount_list);
DEBUG_PRINT("hideroot unmount everything under /system, /vendor\n");
// Unmount everything under /system, /vendor, and loop mounts
vec_for_each(&mount_list, line) {
if (strstr(line, "/dev/block/loop") || strstr(line, " /system/") || strstr(line, " /vendor/")) {
sscanf(line, "%*s %4096s", buffer);
lazy_unmount(buffer);
}
free(line);
}
vec_destroy(&mount_list);
exit:
// 4. 目标进程继续运行
kill(target_pid, SIGCONT);
sleep(100);
return 0;
}
switch_mnt_ns的实现如下:
int switch_mnt_ns(int pid) {
char mnt[32];
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid);
if(access(mnt, R_OK) == -1) {
DEBUG_PRINT("[switch_mnt_ns] %s can't access!", mnt);
return 1;
}
int fd, ret;
fd = open(mnt, O_RDONLY);
if (fd
DEBUG_PRINT("[switch_mnt_ns] open %s failed!", mnt);
return 1;
}
// setns的详细解释见 http://man7.org/linux/man-pages/man2/setns.2.html
ret = setns(fd, 0);
close(fd);
return ret;
}
原理很简单,通过setns关联到目标进程,unmount掉一些文件系统。附件demo的测试输出:
unmount前后,目标进程/proc/self/mounts文件内容的变化:
然后测试了下namespace相关的输出,shell 和root的mnt-namespace 是同一个:
所以在shell或者root中某一个终端隐藏su后,在其他终端也不能直接访问su了。
另外,查看过的所有进程的net-namespace都是同一个:
关于哪些进程使用同一个namespace的逻辑,本菜也不清楚从内核源码的哪部分看,有大佬了解这块的欢迎拍砖。
上传的附件:
hideroot.zip
(7.06kb,202次下载)