├── jni ├── Android.mk ├── Application.mk ├── demo.c ├── hideroot.c ├── log.h └── utils │ ├── vector.c │ └── vector.h └── readme.md /jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := hideroot 6 | LOCAL_SRC_FILES := hideroot.c utils/vector.c 7 | 8 | LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 9 | 10 | include $(BUILD_EXECUTABLE) 11 | 12 | 13 | include $(CLEAR_VARS) 14 | 15 | LOCAL_MODULE := demo 16 | LOCAL_SRC_FILES := demo.c 17 | 18 | LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 19 | 20 | include $(BUILD_EXECUTABLE) -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a 2 | APP_PLATFORM := android-21 3 | APP_STL := stlport_static -------------------------------------------------------------------------------- /jni/demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "log.h" 5 | 6 | int main(int angc, char** argv) { 7 | while(1) { 8 | if (access("/sbin/su", F_OK) == 0) { 9 | DEBUG_PRINT("Can access /sbin/su!\n"); 10 | } 11 | else { 12 | DEBUG_PRINT("Can not access /sbin/su!\nS"); 13 | } 14 | sleep(3); 15 | } 16 | } -------------------------------------------------------------------------------- /jni/hideroot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "log.h" 23 | #include "utils/vector.h" 24 | 25 | int switch_mnt_ns(int pid) { 26 | char mnt[32]; 27 | snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid); 28 | if(access(mnt, R_OK) == -1) { 29 | DEBUG_PRINT("[switch_mnt_ns] %s can't access!", mnt); 30 | return 1; 31 | } 32 | 33 | int fd, ret; 34 | fd = open(mnt, O_RDONLY); 35 | if (fd < 0) { 36 | DEBUG_PRINT("[switch_mnt_ns] open %s failed!", mnt); 37 | return 1; 38 | } 39 | ret = setns(fd, 0); 40 | close(fd); 41 | return ret; 42 | } 43 | 44 | ssize_t readline(char **lineptr, size_t *n, FILE *stream) 45 | { 46 | int ch; 47 | char *line = *lineptr; 48 | size_t alloced = *n; 49 | size_t len = 0; 50 | 51 | do { 52 | ch = fgetc(stream); 53 | if (ch == EOF) 54 | break; 55 | if (len + 1 >= alloced) { 56 | alloced += alloced/4 + 64; 57 | line = realloc(line, alloced); 58 | } 59 | line[len++] = ch; 60 | } while (ch != '\n'); 61 | 62 | if (len == 0) 63 | return -1; 64 | 65 | line[len] = '\0'; 66 | *lineptr = line; 67 | *n = alloced; 68 | return len; 69 | } 70 | 71 | int file_to_vector(const char* filename, struct vector *v) { 72 | if (access(filename, R_OK) != 0) 73 | return 1; 74 | char *line = NULL; 75 | size_t len = 0; 76 | ssize_t read; 77 | 78 | FILE *fp = fopen(filename, "r"); 79 | if (fp == NULL) 80 | return 1; 81 | while ((read = readline(&line, &len, fp)) != -1) { 82 | // Remove end newline 83 | if (line[read - 1] == '\n') 84 | line[read - 1] = '\0'; 85 | vec_push_back(v, line); 86 | line = NULL; 87 | len = 0; 88 | } 89 | fclose(fp); 90 | return 0; 91 | } 92 | 93 | static void lazy_unmount(const char* mountpoint) { 94 | if (umount2(mountpoint, MNT_DETACH) != -1) { 95 | LOGD("[lazy_unmount]: Unmounted (%s)\n", mountpoint); 96 | } 97 | else { 98 | LOGD("[lazy_unmount]: Unmount %s failed\n", mountpoint); 99 | } 100 | } 101 | 102 | int main(int argc, char** argv) { 103 | DEBUG_PRINT("hideroot start\n"); 104 | if (argc < 2) { 105 | DEBUG_PRINT("usage: ./hideroot {pid}\n"); 106 | return 1; 107 | } 108 | 109 | pid_t target_pid = atoi(argv[1]); 110 | DEBUG_PRINT("target pid is %d\n", target_pid); 111 | 112 | kill(target_pid, SIGSTOP); 113 | 114 | DEBUG_PRINT("hideroot switch_mnt_ns\n"); 115 | if (switch_mnt_ns(target_pid)) { 116 | DEBUG_PRINT(""); 117 | return 1; 118 | } 119 | 120 | DEBUG_PRINT("hideroot read mounts\n"); 121 | char buffer[256]; 122 | char* line = NULL; 123 | struct vector mount_list; 124 | snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", target_pid); 125 | DEBUG_PRINT("hideroot init mount_list\n"); 126 | vec_init(&mount_list); 127 | DEBUG_PRINT("hideroot read file\n"); 128 | file_to_vector(buffer, &mount_list); 129 | 130 | DEBUG_PRINT("hideroot unmount dummy skeletons and /sbin links\n"); 131 | // Unmount dummy skeletons and /sbin links 132 | vec_for_each(&mount_list, line) { 133 | if (strstr(line, "tmpfs /system/") || strstr(line, "tmpfs /vendor/") || strstr(line, "tmpfs /sbin")) { 134 | sscanf(line, "%*s %4096s", buffer); 135 | lazy_unmount(buffer); 136 | } 137 | free(line); 138 | } 139 | vec_destroy(&mount_list); 140 | 141 | // Re-read mount infos 142 | snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", target_pid); 143 | vec_init(&mount_list); 144 | file_to_vector(buffer, &mount_list); 145 | 146 | DEBUG_PRINT("hideroot unmount everything under /system, /vendor\n"); 147 | // Unmount everything under /system, /vendor, and loop mounts 148 | vec_for_each(&mount_list, line) { 149 | if (strstr(line, "/dev/block/loop") || strstr(line, " /system/") || strstr(line, " /vendor/")) { 150 | sscanf(line, "%*s %4096s", buffer); 151 | lazy_unmount(buffer); 152 | } 153 | free(line); 154 | } 155 | vec_destroy(&mount_list); 156 | exit: 157 | // Send resume signal 158 | kill(target_pid, SIGCONT); 159 | sleep(100); 160 | return 0; 161 | } -------------------------------------------------------------------------------- /jni/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __MY_LOG_H__ 2 | #define __MY_LOG_H__ 3 | 4 | #include 5 | 6 | #define ENABLE_DEBUG 1 7 | 8 | #if ENABLE_DEBUG 9 | #define LOG_TAG "hideroot" 10 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 11 | #define DEBUG_PRINT(format,args...) LOGD(format, ##args) 12 | #else 13 | #define DEBUG_PRINT(format,args...) 14 | #endif 15 | 16 | #endif // __MY_LOG_H__ 17 | -------------------------------------------------------------------------------- /jni/utils/vector.c: -------------------------------------------------------------------------------- 1 | /* vector.c - A simple vector implementation in c 2 | */ 3 | 4 | #include 5 | #include 6 | 7 | #include "vector.h" 8 | 9 | void vec_init(struct vector *v) { 10 | if (v == NULL) return; 11 | vec_size(v) = 0; 12 | vec_cap(v) = 1; 13 | vec_entry(v) = malloc(sizeof(void*)); 14 | } 15 | 16 | void vec_push_back(struct vector *v, void *p) { 17 | if (v == NULL) return; 18 | if (vec_size(v) == vec_cap(v)) { 19 | vec_cap(v) *= 2; 20 | vec_entry(v) = realloc(vec_entry(v), sizeof(void*) * vec_cap(v)); 21 | } 22 | vec_entry(v)[vec_size(v)] = p; 23 | ++vec_size(v); 24 | } 25 | 26 | void *vec_pop_back(struct vector *v) { 27 | void *ret = vec_entry(v)[vec_size(v) - 1]; 28 | --vec_size(v); 29 | return ret; 30 | } 31 | 32 | static int (*cmp)(const void *, const void *); 33 | 34 | static int vec_comp(const void *a, const void *b) { 35 | void *aa = *((void **)a), *bb = *((void **)b); 36 | if (aa == NULL && bb == NULL) return 0; 37 | else if (aa == NULL) return 1; 38 | else if (bb == NULL) return -1; 39 | else return cmp ? cmp(aa, bb) : 0; 40 | } 41 | 42 | void vec_sort(struct vector *v, int (*compar)(const void *, const void *)) { 43 | if (v == NULL) return; 44 | cmp = compar; 45 | qsort(vec_entry(v), vec_size(v), sizeof(void*), vec_comp); 46 | void *e; 47 | vec_for_each_r(v, e) { 48 | if (e) break; 49 | --vec_size(v); 50 | } 51 | } 52 | 53 | /* Will cleanup only the vector itself 54 | * use in cases when each element requires special cleanup 55 | */ 56 | void vec_destroy(struct vector *v) { 57 | if (v == NULL) return; 58 | vec_size(v) = 0; 59 | vec_cap(v) = 0; 60 | free(vec_entry(v)); 61 | vec_entry(v) = NULL; // Prevent double destroy segfault 62 | } 63 | 64 | /* Will cleanup each element AND the vector itself 65 | * Shall be the general case 66 | */ 67 | void vec_deep_destroy(struct vector *v) { 68 | if (v == NULL) return; 69 | void *e; 70 | vec_for_each(v, e) { 71 | free(e); 72 | } 73 | vec_destroy(v); 74 | } 75 | 76 | struct vector *vec_dup(struct vector *v) { 77 | struct vector *ret = malloc(sizeof(*ret)); 78 | vec_size(ret) = vec_size(v); 79 | vec_cap(ret) = vec_cap(v); 80 | vec_entry(v) = malloc(sizeof(void*) * vec_cap(ret)); 81 | memcpy(vec_entry(ret), vec_entry(v), sizeof(void*) * vec_cap(ret)); 82 | return ret; 83 | } 84 | -------------------------------------------------------------------------------- /jni/utils/vector.h: -------------------------------------------------------------------------------- 1 | /* vector.h - A simple vector implementation in c 2 | */ 3 | 4 | #ifndef _VECTOR_H_ 5 | #define _VECTOR_H_ 6 | 7 | #include 8 | 9 | struct vector { 10 | unsigned size; 11 | unsigned cap; 12 | void **data; 13 | }; 14 | 15 | void vec_init(struct vector *v); 16 | void vec_push_back(struct vector *v, void *p); 17 | void *vec_pop_back(struct vector *v); 18 | void vec_sort(struct vector *v, int (*compar)(const void *, const void *)); 19 | void vec_destroy(struct vector *v); 20 | void vec_deep_destroy(struct vector *v); 21 | struct vector *vec_dup(struct vector *v); 22 | 23 | #define vec_size(v) (v)->size 24 | #define vec_cap(v) (v)->cap 25 | #define vec_entry(v) (v)->data 26 | /* Usage: vec_for_each(vector *v, void *e) */ 27 | #define vec_for_each(v, e) \ 28 | e = v ? (v)->data[0] : NULL; \ 29 | for (int _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_]) 30 | 31 | #define vec_for_each_r(v, e) \ 32 | e = (v && (v)->size > 0) ? (v)->data[(v)->size - 1] : NULL; \ 33 | for (int _ = ((int) (v)->size) - 1; v && _ >= 0; --_, e = (v)->data[_]) 34 | 35 | #define vec_cur(v) vec_entry(v)[_] 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Magisk源码[地址](https://github.com/topjohnwu/Magisk) 2 | 隐藏root功能的代码位于工程的 Magisk/native/jni/magiskhide目录下 3 | 这里直接把功能实现核心代码抽离出来。完整代码见附件,以下是主要部分: 4 | 5 | ```c 6 | int main(int argc, char** argv) { 7 | DEBUG_PRINT("hideroot start\n"); 8 | if (argc < 2) { 9 | DEBUG_PRINT("usage: ./hideroot {pid}\n"); 10 | return 1; 11 | } 12 | 13 | pid_t target_pid = atoi(argv[1]); 14 | DEBUG_PRINT("target pid is %d\n", target_pid); 15 | 16 | // 1. 暂停目标进程 17 | kill(target_pid, SIGSTOP); 18 | 19 | // 2. 关联到目标进程的mnt-namespace 20 | DEBUG_PRINT("hideroot switch_mnt_ns\n"); 21 | if (switch_mnt_ns(target_pid)) { 22 | DEBUG_PRINT(""); 23 | return 1; 24 | } 25 | 26 | // 3. 卸载相关文件系统 27 | DEBUG_PRINT("hideroot read mounts\n"); 28 | char buffer[256]; 29 | char* line = NULL; 30 | struct vector mount_list; 31 | snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", target_pid); 32 | DEBUG_PRINT("hideroot init mount_list\n"); 33 | vec_init(&mount_list); 34 | DEBUG_PRINT("hideroot read file\n"); 35 | file_to_vector(buffer, &mount_list); 36 | 37 | DEBUG_PRINT("hideroot unmount dummy skeletons and /sbin links\n"); 38 | // Unmount dummy skeletons and /sbin links 39 | vec_for_each(&mount_list, line) { 40 | if (strstr(line, "tmpfs /system/") || strstr(line, "tmpfs /vendor/") || strstr(line, "tmpfs /sbin")) { 41 | sscanf(line, "%*s %4096s", buffer); 42 | lazy_unmount(buffer); 43 | } 44 | free(line); 45 | } 46 | vec_destroy(&mount_list); 47 | 48 | // Re-read mount infos 49 | snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", target_pid); 50 | vec_init(&mount_list); 51 | file_to_vector(buffer, &mount_list); 52 | 53 | DEBUG_PRINT("hideroot unmount everything under /system, /vendor\n"); 54 | // Unmount everything under /system, /vendor, and loop mounts 55 | vec_for_each(&mount_list, line) { 56 | if (strstr(line, "/dev/block/loop") || strstr(line, " /system/") || strstr(line, " /vendor/")) { 57 | sscanf(line, "%*s %4096s", buffer); 58 | lazy_unmount(buffer); 59 | } 60 | free(line); 61 | } 62 | vec_destroy(&mount_list); 63 | 64 | exit: 65 | // 4. 目标进程继续运行 66 | kill(target_pid, SIGCONT); 67 | 68 | sleep(100); 69 | 70 | return 0; 71 | } 72 | ``` 73 | 74 | switch_mnt_ns的实现如下: 75 | 76 | ```c 77 | int switch_mnt_ns(int pid) { 78 | char mnt[32]; 79 | snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid); 80 | if(access(mnt, R_OK) == -1) { 81 | DEBUG_PRINT("[switch_mnt_ns] %s can't access!", mnt); 82 | return 1; 83 | } 84 | 85 | int fd, ret; 86 | fd = open(mnt, O_RDONLY); 87 | if (fd < 0) { 88 | DEBUG_PRINT("[switch_mnt_ns] open %s failed!", mnt); 89 | return 1; 90 | } 91 | 92 | // setns的详细解释见 http://man7.org/linux/man-pages/man2/setns.2.html 93 | ret = setns(fd, 0); 94 | close(fd); 95 | return ret; 96 | } 97 | 98 | ``` 99 | 100 | --------------------------------------------------------------------------------