├── selinux ├── policy ├── supolicy ├── magiskpolicy ├── root.te └── reload.te ├── su ├── jni │ ├── Application.mk │ ├── CMakeLists.txt │ ├── Android.mk │ ├── su_service.h │ ├── su_client.h │ ├── main.cpp │ ├── pts.h │ ├── pts.cpp │ ├── su_client.cpp │ ├── tool.h │ └── su_service.cpp ├── Build.md ├── root.te └── root_backup.te ├── README.en.md └── README.md /selinux/policy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/android-jailbreak/HEAD/selinux/policy -------------------------------------------------------------------------------- /selinux/supolicy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/android-jailbreak/HEAD/selinux/supolicy -------------------------------------------------------------------------------- /selinux/magiskpolicy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Thehepta/android-jailbreak/HEAD/selinux/magiskpolicy -------------------------------------------------------------------------------- /su/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := arm64-v8a 2 | APP_STL := c++_static #ndk21 not need || in ndk25 need add 3 | 4 | #APP_ABI := armeabi-v7a arm64-v8a 目前只编译64位架构 -------------------------------------------------------------------------------- /su/Build.md: -------------------------------------------------------------------------------- 1 | ### 介绍 2 | 一个简易的,su程序。 3 | 4 | 参照supersu ,magisk 等等。用于学习su的原理,用最简单的的代码实现了一个su 5 | 6 | 7 | 8 | ndk-build 9 | 10 | 如果有问题,可以试试 11 | 12 | ndk-build NDK_PROJECT_PATH=. 13 | 14 | 15 | -------------------------------------------------------------------------------- /su/jni/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | project(socket) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | add_executable(su main.cpp su_client.cpp su_service.cpp tool.h pts.cpp pts.h) 7 | -------------------------------------------------------------------------------- /su/root.te: -------------------------------------------------------------------------------- 1 | allow kernel * * * 2 | allow shell kernel unix_stream_socket * 3 | allow shell devpts chr_file * 4 | allow untrusted_app shell_data_file file * 5 | allow untrusted_app kernel unix_stream_socket * 6 | 7 | -------------------------------------------------------------------------------- /su/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | include $(CLEAR_VARS) 3 | $(warning "su") 4 | 5 | 6 | LIBEVNET_SOURCES := main.cpp pts.cpp su_client.cpp su_service.cpp 7 | 8 | LOCAL_MODULE := su 9 | LOCAL_SRC_FILES := $(LIBEVNET_SOURCES) 10 | 11 | 12 | include $(BUILD_EXECUTABLE) -------------------------------------------------------------------------------- /su/jni/su_service.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by chic on 2022/8/22. 3 | // 4 | 5 | #ifndef SUPERSU_SERVICE_H 6 | #define SUPERSU_SERVICE_H 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int service_main(); 17 | 18 | #endif //SUPERSU_SERVICE_H 19 | -------------------------------------------------------------------------------- /su/jni/su_client.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by chic on 2022/8/22. 3 | // 4 | 5 | #ifndef SUPERSU_CLIENT_H 6 | #define SUPERSU_CLIENT_H 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | int client_main(int argc,char *argv[]); 18 | 19 | #endif //SUPERSU_CLIENT_H 20 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # Android-Jailbreak 2 | 3 | #### Description 4 | {**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} 5 | 6 | #### Software Architecture 7 | Software architecture description 8 | 9 | #### Installation 10 | 11 | 1. xxxx 12 | 2. xxxx 13 | 3. xxxx 14 | 15 | #### Instructions 16 | 17 | 1. xxxx 18 | 2. xxxx 19 | 3. xxxx 20 | 21 | #### Contribution 22 | 23 | 1. Fork the repository 24 | 2. Create Feat_xxx branch 25 | 3. Commit your code 26 | 4. Create Pull Request 27 | 28 | 29 | #### Gitee Feature 30 | 31 | 1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md 32 | 2. Gitee blog [blog.gitee.com](https://blog.gitee.com) 33 | 3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) 34 | 4. The most valuable open source project [GVP](https://gitee.com/gvp) 35 | 5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) 36 | 6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) 37 | -------------------------------------------------------------------------------- /su/jni/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "su_service.h" 4 | #include "su_client.h" 5 | #include "tool.h" 6 | 7 | //小功能测试demo 8 | void java_exec(){ 9 | 10 | int child = fork(); 11 | if (child != 0) { 12 | 13 | int status, code; 14 | printf("waiting for child exit\n"); 15 | printf("current parent pid = %d\n",getpid()); 16 | printf("current chiled pid = %d\n",child); 17 | if (waitpid(child, &status, 0) > 0) { 18 | code = WEXITSTATUS(status); 19 | } 20 | else { 21 | code = -1; 22 | } 23 | // Pass the return code back to the client 24 | printf("sending code\n"); 25 | printf("child exited\n"); 26 | return ; 27 | } 28 | 29 | if (setsid() == (pid_t) -1) { 30 | perror("setsid failed"); 31 | } 32 | 33 | char *bin_exec = "/system/bin/sh"; 34 | char* argument_list[4]; 35 | argument_list[0]="sh"; 36 | argument_list[1]= NULL; 37 | 38 | execvp(bin_exec, argument_list); 39 | 40 | 41 | } 42 | 43 | int main(int argc, char *argv[]) { 44 | 45 | if (argc == 2 && strcmp(argv[1], "--daemon") == 0) { 46 | return service_main(); 47 | } 48 | return client_main(argc, argv); 49 | 50 | // java_exec(); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /selinux/root.te: -------------------------------------------------------------------------------- 1 | allow kernel fwmarkd_socket sock_file { write } 2 | allow kernel netd unix_stream_socket { connectto } 3 | allow kernel kernel tcp_socket { read } 4 | allow kernel toolbox_exec file { getattr execute read open execute_no_trans } 5 | allow kernel devpts chr_file * 6 | allow kernel shell_data_file dir * 7 | allow kernel shell_data_file file * 8 | allow kernel vendor_file dir { read open } 9 | allow kernel vendor_toolbox_exec file { getattr execute } 10 | allow kernel adbd fd { use } 11 | allow kernel shell process { sigchld } 12 | allow kernel kernel capability { dac_override dac_read_search } 13 | allow kernel rs_exec file { execute getattr } 14 | allow kernel rs_exec file { execute getattr } 15 | allow shell kernel unix_stream_socket * 16 | allow kernel shell_exec file * 17 | allow shell devpts chr_file * 18 | allow untrusted_app shell_data_file file * 19 | allow untrusted_app kernel unix_stream_socket * 20 | allow kernel untrusted_app fd * 21 | allow kernel untrusted_app fifo_file * 22 | allow kernel init file * 23 | allow kernel vendor_init file * 24 | allow kernel init dir * 25 | allow kernel vendor_init dir * 26 | allow kernel ueventd dir * 27 | allow kernel logd dir * 28 | allow kernel servicemanager dir * 29 | allow kernel hwservicemanager dir * 30 | allow kernel vndservicemanager dir * 31 | allow kernel app_data_file dir * 32 | allow kernel system_data_file lnk_file * 33 | allow kernel system_data_file dir * 34 | allow kernel app_data_file file * 35 | allow kernel ueventd file * 36 | allow kernel proc_irq dir * 37 | allow system_app vendor_default_prop file * -------------------------------------------------------------------------------- /su/root_backup.te: -------------------------------------------------------------------------------- 1 | allow kernel fwmarkd_socket sock_file { write } 2 | allow kernel netd unix_stream_socket { connectto } 3 | allow kernel kernel tcp_socket { read } 4 | allow kernel toolbox_exec file { getattr execute read open execute_no_trans } 5 | allow kernel devpts chr_file * 6 | allow kernel shell_data_file dir * 7 | allow kernel shell_data_file file * 8 | allow kernel vendor_file dir { read open } 9 | allow kernel vendor_toolbox_exec file { getattr execute } 10 | allow kernel adbd fd { use } 11 | allow kernel shell process { sigchld } 12 | allow kernel kernel capability { dac_override dac_read_search } 13 | allow kernel rs_exec file { execute getattr } 14 | allow kernel rs_exec file { execute getattr } 15 | allow shell kernel unix_stream_socket * 16 | allow kernel shell_exec file * 17 | allow shell devpts chr_file * 18 | allow untrusted_app shell_data_file file * 19 | allow untrusted_app kernel unix_stream_socket * 20 | allow kernel untrusted_app fd * 21 | allow kernel untrusted_app fifo_file * 22 | allow kernel init file * 23 | allow kernel vendor_init file * 24 | allow kernel init dir * 25 | allow kernel vendor_init dir * 26 | allow kernel ueventd dir * 27 | allow kernel logd dir * 28 | allow kernel logd file * 29 | allow kernel servicemanager dir * 30 | allow kernel servicemanager file * 31 | allow kernel hwservicemanager dir * 32 | allow kernel hwservicemanager file * 33 | allow kernel vndservicemanager dir * 34 | allow kernel vndservicemanager file * 35 | allow kernel tee dir * 36 | allow kernel tee file * 37 | allow kernel vold dir * 38 | allow kernel vold file * 39 | allow kernel app_data_file dir * 40 | allow kernel system_data_file lnk_file * 41 | allow kernel app_data_file file * 42 | allow kernel system_file file * 43 | allow kernel ueventd file * 44 | allow kernel * * * -------------------------------------------------------------------------------- /selinux/reload.te: -------------------------------------------------------------------------------- 1 | 2 | 3 | type=1400 audit(0.0:1899): avc: denied { write } for name="fwmarkd" dev="tmpfs" ino=17348 scontext=u:r:kernel:s0 tcontext=u:object_r:fwmarkd_socket:s0 tclass=sock_file permissive=1 4 | 5 | allow kernel fwmarkd_socket sock_file { write } 6 | 7 | type=1400 audit(0.0:1900): avc: denied { connectto } for path="/dev/socket/fwmarkd" scontext=u:r:kernel:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket permissive=1 8 | 9 | allow kernel netd unix_stream_socket { connectto } 10 | 11 | type=1400 audit(0.0:1903): avc: denied { read } for path="socket:[193022]" dev="sockfs" ino=193022 scontext=u:r:kernel:s0 tcontext=u:r:kernel:s0 tclass=tcp_socket permissive=1 12 | 13 | allow kernel kernel tcp_socket { read } 14 | 15 | type=1400 audit(0.0:1910): avc: denied { getattr } for path="/system/bin/toybox" dev="dm-0" ino=518 scontext=u:r:kernel:s0 tcontext=u:object_r:toolbox_exec:s0 tclass=file permissive=1 16 | 17 | allow kernel toolbox_exec file { getattr } 18 | 19 | type=1400 audit(0.0:1911): avc: denied { execute } for name="toybox" dev="dm-0" ino=518 scontext=u:r:kernel:s0 tcontext=u:object_r:toolbox_exec:s0 tclass=file permissive=1 20 | 21 | allow kernel toolbox_exec file { execute } 22 | 23 | type=1400 audit(0.0:1912): avc: denied { read open } for path="/system/bin/toybox" dev="dm-0" ino=518 scontext=u:r:kernel:s0 tcontext=u:object_r:toolbox_exec:s0 tclass=file permissive=1 24 | 25 | allow kernel toolbox_exec file { read open } 26 | 27 | type=1400 audit(0.0:1914): avc: denied { ioctl } for path="/dev/pts/4" dev="devpts" ino=7 ioctlcmd=5401 scontext=u:r:kernel:s0 tcontext=u:object_r:devpts:s0 tclass=chr_file permissive=1 28 | 29 | allow kernel devpts chr_file { ioctl } 30 | 31 | type=1400 audit(0.0:1915): avc: denied { getattr } for path="/data/local/tmp" dev="sda45" ino=1130498 scontext=u:r:kernel:s0 tcontext=u:object_r:shell_data_file:s0 tclass=dir permissive=1 32 | 33 | allow kernel shell_data_file dir { getattr } 34 | -------------------------------------------------------------------------------- /su/jni/pts.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by chic on 2022/8/25. 3 | // 4 | 5 | #ifndef SOCKET_PTS_H 6 | #define SOCKET_PTS_H 7 | #include "tool.h" 8 | #include 9 | 10 | 11 | 12 | 13 | /** 14 | * pts_open 15 | * 16 | * Opens a pts device and returns the name of the slave tty device. 17 | * 18 | * Arguments 19 | * slave_name the name of the slave device 20 | * slave_name_size the size of the buffer passed via slave_name 21 | * 22 | * Return Values 23 | * on failure either -2 or -1 (errno set) is returned. 24 | * on success, the file descriptor of the master device is returned. 25 | */ 26 | int pts_open(char *slave_name, size_t slave_name_size); 27 | 28 | /** 29 | * set_stdin_raw 30 | * 31 | * Changes stdin to raw unbuffered mode, disables echo, 32 | * auto carriage return, etc. 33 | * 34 | * Return Value 35 | * on failure -1, and errno is set 36 | * on success 0 37 | */ 38 | int set_stdin_raw(void); 39 | 40 | /** 41 | * restore_stdin 42 | * 43 | * Restore termios on stdin to the state it was before 44 | * set_stdin_raw() was called. If set_stdin_raw() was 45 | * never called, does nothing and doesn't return an error. 46 | * 47 | * This function is async-safe. 48 | * 49 | * Return Value 50 | * on failure, -1 and errno is set 51 | * on success, 0 52 | */ 53 | int restore_stdin(void); 54 | 55 | /** 56 | * watch_sigwinch_async 57 | * 58 | * After calling this function, if the application receives 59 | * SIGWINCH, the terminal window size will be read from 60 | * "input" and set on "output". 61 | * 62 | * NOTE: This function blocks SIGWINCH and spawns a thread. 63 | * 64 | * Arguments 65 | * master A file descriptor of the TTY window size to follow 66 | * slave A file descriptor of the TTY window size which is 67 | * to be set on SIGWINCH 68 | * 69 | * Return Value 70 | * on failure, -1 and errno will be set. In this case, no 71 | * thread has been spawned and SIGWINCH will not be 72 | * blocked. 73 | * on success, 0 74 | */ 75 | int watch_sigwinch_async(int master, int slave); 76 | 77 | /** 78 | * watch_sigwinch_cleanup 79 | * 80 | * Cause the SIGWINCH watcher thread to terminate 81 | */ 82 | void watch_sigwinch_cleanup(void); 83 | 84 | /** 85 | * pump_stdin_async 86 | * 87 | * Forward data from STDIN to the given FD 88 | * in a seperate thread 89 | */ 90 | void pump_stdin_async(int outfd); 91 | 92 | /** 93 | * pump_stdout_blocking 94 | * 95 | * Forward data from the FD to STDOUT. 96 | * Returns when the remote end of the FD closes. 97 | * 98 | * Before returning, restores stdin settings. 99 | */ 100 | void pump_stdout_blocking(int infd); 101 | 102 | 103 | void pump_async(int input, int output); 104 | #endif //SOCKET_PTS_H 105 | -------------------------------------------------------------------------------- /su/jni/pts.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by chic on 2022/8/25. 3 | // 4 | #include 5 | #include "pts.h" 6 | 7 | 8 | 9 | struct termios old_stdin; 10 | int stdin_is_raw = 0; 11 | 12 | 13 | int pts_open(char *slave_name, size_t slave_name_size) { 14 | int fdm; 15 | char *sn_tmp; 16 | 17 | // Open master ptmx device 18 | fdm = open("/dev/ptmx", O_RDWR); 19 | if (fdm == -1) return -1; 20 | 21 | // Get the slave name 22 | sn_tmp = ptsname(fdm); 23 | if (!sn_tmp) { 24 | close(fdm); 25 | return -2; 26 | } 27 | 28 | strncpy(slave_name, sn_tmp, slave_name_size); 29 | slave_name[slave_name_size - 1] = '\0'; 30 | 31 | // Grant, then unlock 32 | if (grantpt(fdm) == -1) { 33 | close(fdm); 34 | return -1; 35 | } 36 | if (unlockpt(fdm) == -1) { 37 | close(fdm); 38 | return -1; 39 | } 40 | 41 | return fdm; 42 | } 43 | 44 | 45 | // Ensures all the data is written out 46 | int write_blocking(int fd, char *buf, size_t bufsz) { 47 | ssize_t ret, written; 48 | 49 | written = 0; 50 | do { 51 | ret = write(fd, buf + written, bufsz - written); 52 | if (ret == -1) return -1; 53 | written += ret; 54 | } while (written < bufsz); 55 | 56 | return 0; 57 | } 58 | 59 | 60 | void pump_ex(int input, int output, int close_output) { 61 | char buf[4096]; 62 | int len; 63 | while ((len = read(input, buf, 4096)) > 0) { 64 | if (write_blocking(output, buf, len) == -1) 65 | break; 66 | } 67 | close(input); 68 | if (close_output) close(output); 69 | } 70 | 71 | 72 | void pump(int input, int output) { 73 | pump_ex(input, output, 1); 74 | } 75 | 76 | 77 | 78 | void* pump_thread(void* data) { 79 | int* files = (int*)data; 80 | int input = files[0]; 81 | int output = files[1]; 82 | pump(input, output); 83 | free(data); 84 | return NULL; 85 | } 86 | 87 | 88 | void pump_async(int input, int output) { 89 | pthread_t writer; 90 | int* files = (int*)malloc(sizeof(int) * 2); 91 | if (files == NULL) { 92 | exit(-1); 93 | } 94 | files[0] = input; 95 | files[1] = output; 96 | pthread_create(&writer, NULL, pump_thread, files); 97 | } 98 | 99 | 100 | 101 | int set_stdin_raw(void) { 102 | struct termios new_termios; 103 | 104 | // Save the current stdin termios 105 | if (tcgetattr(STDIN_FILENO, &old_stdin) < 0) { 106 | return -1; 107 | } 108 | 109 | // Start from the current settings 110 | new_termios = old_stdin; 111 | 112 | // Make the terminal like an SSH or telnet client 113 | new_termios.c_iflag |= IGNPAR; 114 | new_termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); 115 | new_termios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); 116 | new_termios.c_oflag &= ~OPOST; 117 | new_termios.c_cc[VMIN] = 1; 118 | new_termios.c_cc[VTIME] = 0; 119 | 120 | if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) < 0) { 121 | return -1; 122 | } 123 | 124 | stdin_is_raw = 1; 125 | 126 | return 0; 127 | } 128 | 129 | 130 | int restore_stdin(void) { 131 | if (!stdin_is_raw) return 0; 132 | 133 | if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) { 134 | return -1; 135 | } 136 | 137 | stdin_is_raw = 0; 138 | 139 | return 0; 140 | } 141 | 142 | 143 | void watch_sigwinch_cleanup(void) { 144 | closing_time = 1; 145 | raise(SIGWINCH); 146 | } 147 | 148 | 149 | void pump_stdout_blocking(int infd) { 150 | // Pump data from stdout to PTY 151 | pump_ex(infd, STDOUT_FILENO, 0 /* Don't close output when done */); 152 | 153 | // Cleanup 154 | restore_stdin(); 155 | watch_sigwinch_cleanup(); 156 | } 157 | -------------------------------------------------------------------------------- /su/jni/su_client.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include "su_client.h" 7 | #include "pts.h" 8 | 9 | 10 | #define ATTY_IN 1 11 | #define ATTY_OUT 2 12 | #define ATTY_ERR 4 13 | 14 | 15 | 16 | // List of signals which cause process termination 17 | static int quit_signals[] = { SIGALRM, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 }; 18 | 19 | static void sighandler(int sig) { 20 | restore_stdin(); 21 | 22 | // Assume we'll only be called before death 23 | // See note before sigaction() in set_stdin_raw() 24 | // 25 | // Now, close all standard I/O to cause the pumps 26 | // to exit so we can continue and retrieve the exit 27 | // code 28 | close(STDIN_FILENO); 29 | close(STDOUT_FILENO); 30 | close(STDERR_FILENO); 31 | 32 | // Put back all the default handlers 33 | struct sigaction act; 34 | int i; 35 | 36 | memset(&act, '\0', sizeof(act)); 37 | act.sa_handler = SIG_DFL; 38 | for (i = 0; quit_signals[i]; i++) { 39 | if (sigaction(quit_signals[i], &act, NULL) < 0) { 40 | printf("Error removing signal handler"); 41 | continue; 42 | } 43 | } 44 | } 45 | 46 | 47 | static void setup_sighandlers(void) { 48 | struct sigaction act; 49 | int i; 50 | 51 | // Install the termination handlers 52 | // Note: we're assuming that none of these signal handlers are already trapped. 53 | // If they are, we'll need to modify this code to save the previous handler and 54 | // call it after we restore stdin to its previous state. 55 | memset(&act, '\0', sizeof(act)); 56 | act.sa_handler = &sighandler; 57 | for (i = 0; quit_signals[i]; i++) { 58 | if (sigaction(quit_signals[i], &act, NULL) < 0) { 59 | printf("Error installing signal handler"); 60 | continue; 61 | } 62 | } 63 | } 64 | 65 | void pump_stdin_async(int outfd) { 66 | // Put stdin into raw mode 67 | set_stdin_raw(); 68 | 69 | // Pump data from stdin to the PTY 70 | pump_async(STDIN_FILENO, outfd); 71 | } 72 | 73 | static void fork_for_samsung(void) 74 | { 75 | // Samsung CONFIG_SEC_RESTRICT_SETUID wants the parent process to have 76 | // EUID 0, or else our setresuid() calls will be denied. So make sure 77 | // all such syscalls are executed by a child process. 78 | int rv; 79 | 80 | switch (fork()) { 81 | case 0: 82 | return; 83 | case -1: 84 | ERR_EXIT("fork failed\n"); 85 | default: 86 | if (wait(&rv) < 0) { 87 | exit(1); 88 | } else { 89 | exit(WEXITSTATUS(rv)); 90 | } 91 | } 92 | } 93 | 94 | 95 | 96 | int client_main(int argc,char *argv[]) 97 | { 98 | 99 | // fork_for_samsung(); //可以不用,也可以写这个进程接受client进程退出状态 100 | 101 | 102 | int uid = getuid(); 103 | char pwd[256]; 104 | int ptmx; 105 | char pts_slave[PATH_MAX]; 106 | //socket 107 | int socketfd = socket(AF_LOCAL, SOCK_STREAM, 0); 108 | if (socketfd < 0) { 109 | ERR_EXIT("socket"); 110 | exit(-1); 111 | } 112 | if (fcntl(socketfd, F_SETFD, FD_CLOEXEC)) { 113 | ERR_EXIT("fcntl FD_CLOEXEC"); 114 | exit(-1); 115 | } 116 | struct sockaddr_un cliaddr; 117 | 118 | memset(&cliaddr, 0, sizeof(cliaddr)); 119 | cliaddr.sun_family=AF_UNIX; 120 | strcpy(cliaddr.sun_path+1,REQUESTOR_SOCKET); 121 | cliaddr.sun_path[0]='\0'; 122 | 123 | 124 | //客户端不需要绑定和监听 125 | //connect 用本地套接字连接服务器的地址 126 | if(connect(socketfd,(struct sockaddr*)&cliaddr,sizeof(cliaddr))<0) 127 | ERR_EXIT("connect"); 128 | 129 | int atty = 0; 130 | if (isatty(STDIN_FILENO))atty |= ATTY_IN; 131 | if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT; 132 | if (isatty(STDERR_FILENO)) atty |= ATTY_ERR; 133 | 134 | 135 | if (atty) { 136 | // We need a PTY. Get one. 终端运行 137 | ptmx = pts_open(pts_slave, sizeof(pts_slave)); 138 | if (ptmx < 0) { 139 | ERR_EXIT("pts_open failed\n"); 140 | } 141 | } else { 142 | // 不是通过终端调用,没有tty 143 | pts_slave[0] = '\0'; 144 | } 145 | 146 | //开始进程通讯 147 | 148 | 149 | write_int(socketfd, getpid()); 150 | write_int(socketfd, uid); 151 | write_string(socketfd, getcwd(pwd,256)); 152 | write_int(socketfd, argc-1); 153 | for(int i=1;i 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 | #define REQUESTOR_SOCKET "d63138f231" 23 | 24 | 25 | #define ERR_EXIT(m) \ 26 | do \ 27 | { \ 28 | perror(m); \ 29 | exit(EXIT_FAILURE); \ 30 | } while (0); 31 | 32 | volatile static int closing_time = 0; 33 | 34 | 35 | static int read_int(int fd) { 36 | int val; 37 | int len = read(fd, &val, sizeof(int)); 38 | if (len != sizeof(int)) { 39 | ERR_EXIT("unable to read_int"); 40 | 41 | } 42 | return val; 43 | } 44 | 45 | static void write_int(int fd, int val) { 46 | int written = write(fd, &val, sizeof(int)); 47 | if (written != sizeof(int)) { 48 | ERR_EXIT("unable to write int"); 49 | } 50 | } 51 | 52 | static char* read_string(int fd) { 53 | int len = read_int(fd); 54 | if (len > PATH_MAX || len < 0) { 55 | ERR_EXIT("invalid string length"); 56 | } 57 | char* val = static_cast(malloc(sizeof(char) * (len + 1))); 58 | if (val == NULL) { 59 | ERR_EXIT("unable to malloc string"); 60 | } 61 | val[len] = '\0'; 62 | int amount = read(fd, val, len); 63 | if (amount != len) { 64 | ERR_EXIT("unable to read string"); 65 | } 66 | return val; 67 | } 68 | 69 | static void write_string(int fd, char* val) { 70 | int len = strlen(val); 71 | write_int(fd, len); 72 | int written = write(fd, val, len); 73 | if (written != len) { 74 | ERR_EXIT("unable to write string"); 75 | } 76 | } 77 | static int recv_fd(int sockfd) { 78 | // Need to receive data from the message, otherwise don't care about it. 79 | char iovbuf; 80 | 81 | struct iovec iov = { 82 | .iov_base = &iovbuf, 83 | .iov_len = 1, 84 | }; 85 | 86 | char cmsgbuf[CMSG_SPACE(sizeof(int))]; 87 | 88 | struct msghdr msg = { 89 | .msg_iov = &iov, 90 | .msg_iovlen = 1, 91 | .msg_control = cmsgbuf, 92 | .msg_controllen = sizeof(cmsgbuf), 93 | }; 94 | 95 | if (recvmsg(sockfd, &msg, MSG_WAITALL) != 1) { 96 | ERR_EXIT("unable to read fd"); 97 | } 98 | 99 | // Was a control message actually sent? 100 | switch (msg.msg_controllen) { 101 | case 0: 102 | // No, so the file descriptor was closed and won't be used. 103 | return -1; 104 | case sizeof(cmsgbuf): 105 | // Yes, grab the file descriptor from it. 106 | break; 107 | default: 108 | ERR_EXIT("unable to read fd"); 109 | } 110 | 111 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 112 | 113 | if (cmsg == NULL || 114 | cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || 115 | cmsg->cmsg_level != SOL_SOCKET || 116 | cmsg->cmsg_type != SCM_RIGHTS) { 117 | error: 118 | ERR_EXIT("unable to read fd"); 119 | } 120 | 121 | return *(int *)CMSG_DATA(cmsg); 122 | } 123 | 124 | static void send_fd(int sockfd, int fd) { 125 | // Need to send some data in the message, this will do. 126 | struct iovec iov = { 127 | .iov_base = (void *) "", 128 | .iov_len = 1, 129 | }; 130 | 131 | struct msghdr msg = { 132 | .msg_iov = &iov, 133 | .msg_iovlen = 1, 134 | }; 135 | 136 | char cmsgbuf[CMSG_SPACE(sizeof(int))]; 137 | 138 | if (fd != -1) { 139 | // Is the file descriptor actually open? 140 | if (fcntl(fd, F_GETFD) == -1) { 141 | if (errno != EBADF) { 142 | goto error; 143 | } 144 | // It's closed, don't send a control message or sendmsg will EBADF. 145 | } else { 146 | // It's open, send the file descriptor in a control message. 147 | msg.msg_control = cmsgbuf; 148 | msg.msg_controllen = sizeof(cmsgbuf); 149 | 150 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 151 | 152 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 153 | cmsg->cmsg_level = SOL_SOCKET; 154 | cmsg->cmsg_type = SCM_RIGHTS; 155 | 156 | *(int *)CMSG_DATA(cmsg) = fd; 157 | } 158 | } 159 | 160 | if (sendmsg(sockfd, &msg, 0) != 1) { 161 | error: 162 | ERR_EXIT("unable to send fd"); 163 | } 164 | } 165 | 166 | static void *watch_sigwinch(void *data) { 167 | sigset_t winch; 168 | int sig; 169 | int master = ((int *)data)[0]; 170 | int slave = ((int *)data)[1]; 171 | 172 | sigemptyset(&winch); 173 | sigaddset(&winch, SIGWINCH); 174 | 175 | do { 176 | // Wait for a SIGWINCH 177 | sigwait(&winch, &sig); 178 | 179 | if (closing_time) break; 180 | 181 | // Get the new terminal size 182 | struct winsize w; 183 | if (ioctl(master, TIOCGWINSZ, &w) == -1) { 184 | continue; 185 | } 186 | 187 | // Set the new terminal size 188 | ioctl(slave, TIOCSWINSZ, &w); 189 | 190 | } while (1); 191 | 192 | free(data); 193 | return NULL; 194 | } 195 | 196 | static int watch_sigwinch_async(int master, int slave) { 197 | pthread_t watcher; 198 | int *files = (int *) malloc(sizeof(int) * 2); 199 | if (files == NULL) { 200 | return -1; 201 | } 202 | 203 | // Block SIGWINCH so sigwait can later receive it 204 | sigset_t winch; 205 | sigemptyset(&winch); 206 | sigaddset(&winch, SIGWINCH); 207 | if (sigprocmask(SIG_BLOCK, &winch, NULL) == -1) { 208 | free(files); 209 | return -1; 210 | } 211 | 212 | // Initialize some variables, then start the thread 213 | closing_time = 0; 214 | files[0] = master; 215 | files[1] = slave; 216 | int ret = pthread_create(&watcher, NULL, &watch_sigwinch, files); 217 | if (ret != 0) { 218 | free(files); 219 | errno = ret; 220 | return -1; 221 | } 222 | 223 | // Set the initial terminal size 224 | raise(SIGWINCH); 225 | return 0; 226 | } 227 | 228 | 229 | 230 | 231 | 232 | #endif //SOCKET_TOOL_H 233 | -------------------------------------------------------------------------------- /su/jni/su_service.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #include 5 | #include 6 | #include "su_service.h" 7 | #include "pts.h" 8 | 9 | int is_daemon = 0; 10 | int daemon_from_uid = 0; 11 | int daemon_from_pid = 0; 12 | 13 | int fork_zero_fucks() { 14 | int pid = fork(); 15 | if (pid) { 16 | int status; 17 | waitpid(pid, &status, 0); 18 | return pid; 19 | } 20 | else { 21 | if (pid = fork()) 22 | exit(0); 23 | return 0; 24 | } 25 | } 26 | 27 | 28 | static void cleanup_signal(int sig) { 29 | printf("cleanup_signal %d",sig); 30 | exit(128 + sig); 31 | } 32 | 33 | static int daemon_accept_handlr(int clientfd) { 34 | 35 | #ifdef __ANDROID__ 36 | char *bin_exec = "/system/bin/sh"; 37 | char *sh_exec = "sh"; 38 | #elif __linux__ 39 | char *bin_exec = "/bin/bash"; 40 | char *sh_exec = "bash"; 41 | #endif 42 | signal(SIGCHLD, SIG_IGN); // 忽略子进程结束信号,防止出现僵尸进程 43 | int pid = read_int(clientfd); 44 | printf("remote pid: %d \n", pid); 45 | daemon_from_uid = read_int(clientfd); 46 | printf("remote uid: %d \n", daemon_from_uid); 47 | std::string env_pwd = "PWD="; 48 | env_pwd = env_pwd + read_string(clientfd); 49 | printf("remote pwd: %s \n", env_pwd.c_str()); 50 | int argc = read_int(clientfd); 51 | printf("remote argc: %d \n", argc); 52 | std::string arg ; 53 | for(int i=0;i 0) { 88 | code = WEXITSTATUS(status); 89 | } 90 | else { 91 | code = -1; 92 | } 93 | 94 | // Pass the return code back to the client 95 | printf("sending code\n"); 96 | if (write(clientfd, &code, sizeof(int)) != sizeof(int)) { 97 | printf("unable to write exit code"); 98 | } 99 | 100 | close(clientfd); 101 | printf("child exited\n"); 102 | return code; 103 | } 104 | close (clientfd); 105 | 106 | //分离终端,将当前进程设置成当前进程组的控制终端,终端是以进程组为单位的 107 | if (setsid() == (pid_t) -1) { 108 | ERR_EXIT("setsid failed"); 109 | } 110 | // Opening the TTY has to occur after the 111 | // fork() and setsid() so that it becomes 112 | // our controlling TTY and not the daemon's 113 | int ptsfd = 0; 114 | 115 | if (pts_slave[0]) { 116 | printf("open TIOCSCTTY"); 117 | ptsfd = open(pts_slave, O_RDWR); 118 | if (ptsfd == -1) { 119 | ERR_EXIT("open(pts_slave) daemon"); 120 | } 121 | if (infd < 0) { 122 | printf("daemon: stdin using PTY"); 123 | infd = ptsfd; 124 | } 125 | if (outfd < 0) { 126 | printf("daemon: stdout using PTY"); 127 | outfd = ptsfd; 128 | } 129 | if (errfd < 0) { 130 | printf("daemon: stderr using PTY"); 131 | errfd = ptsfd; 132 | } 133 | }else{ 134 | //调用对象不是终端设备 135 | printf("not tty ,ioctl TIOCSCTTY su service\n"); 136 | if (isatty(infd)) { 137 | ioctl(infd, TIOCSCTTY, 1); 138 | } 139 | } 140 | free(pts_slave); 141 | //所有的输入输出都重定向到 /dev/pts 142 | if (-1 == dup2(outfd, STDOUT_FILENO)) { 143 | ERR_EXIT("dup2 child outfd"); 144 | } 145 | if (-1 == dup2(errfd, STDERR_FILENO)) { 146 | ERR_EXIT("dup2 child errfd"); 147 | } 148 | if (-1 == dup2(infd, STDIN_FILENO)) { 149 | ERR_EXIT("dup2 child infd"); 150 | } 151 | 152 | close(infd); 153 | close(outfd); 154 | close(errfd); 155 | char* argument_list[4]; 156 | 157 | if(arg.size() > 0){ 158 | argument_list[0]= sh_exec; 159 | argument_list[1]="-c"; 160 | argument_list[2]=(char *)arg.c_str(); 161 | argument_list[3]= NULL; 162 | 163 | } else{ 164 | argument_list[0]= sh_exec; 165 | argument_list[1]= NULL; 166 | } 167 | putenv((char*)env_pwd.c_str()); //设置环境变量 //我这里目前只设置了PWD 环境变量,shell会自动切换到这个目录 168 | execvp(bin_exec, argument_list); 169 | printf( "Cannot execute %s: %s\n", bin_exec, strerror(errno)); 170 | free(pts_slave); 171 | } 172 | 173 | 174 | 175 | 176 | int service_main(void) 177 | { 178 | //socket 179 | int listenfd; 180 | 181 | //listenfd=socket(PF_INET,SOCK_STREAM,0); 182 | if((listenfd=socket(AF_LOCAL,SOCK_STREAM,0))<0) 183 | { 184 | ERR_EXIT("socket"); 185 | } 186 | 187 | if (fcntl(listenfd, F_SETFD, FD_CLOEXEC)) { 188 | ERR_EXIT("fcntl FD_CLOEXEC"); 189 | } 190 | 191 | //填充地址结构 192 | struct sockaddr_un servaddr; 193 | memset(&servaddr, 0, sizeof(servaddr)); 194 | servaddr.sun_family=AF_LOCAL; 195 | strcpy(servaddr.sun_path+1,REQUESTOR_SOCKET); 196 | servaddr.sun_path[0]='\0'; 197 | 198 | //bind 绑定listenfd和本地地址结构 199 | if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0) 200 | { 201 | ERR_EXIT("bind"); 202 | } 203 | 204 | if(listen(listenfd,SOMAXCONN)<0) 205 | { 206 | ERR_EXIT("listen"); 207 | } 208 | 209 | 210 | int client; //已连接套接字(主动) 211 | printf("curren pid %d\n",getpid()); 212 | while ((client = accept(listenfd, NULL,NULL)) > 0) { 213 | if (fork_zero_fucks() == 0) { 214 | close(listenfd); 215 | return daemon_accept_handlr(client); 216 | } 217 | else { 218 | close(client); 219 | } 220 | } 221 | //关闭套接口 222 | close(client); 223 | close(listenfd); 224 | return 0; 225 | } 226 | 227 | 228 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 前言 3 | 感谢r0ysue,送了我手机,白嫖的快感。 4 | 上一篇发了如何写一个hook框架,但是还是依赖于root权限,而在android平台,magisk就是root的代名词。所以开始了。。。。。 5 | 6 | # 越狱概论 7 | 8 | 9 | + 软件安全的崛起 10 | 11 | 软件安全所要解决的问题提是什么: 防攻击、防代码泄露。 12 | 13 | 那么为什么会有软件安全的问题,因为软件运行平台不安全,而软件的载体是编译后的应用文件,不安全的平台导致了这些应用文件会被攻击者获得,因而产生了软件安全。 14 | 15 | 所以软件安全的崛起,本质还是因为,运行平台端的不安全。 16 | 17 | 为什么运行平台端不安全那,pc而言,本身不提供也不担任应用保护的角色。而移动端,有一定程度的保护,甚至我觉得,开始他们是想做起来的,但是后续发现,基本不可能,为什么,正是因为越狱。 18 | 19 | 目前软件保护做的最好的是ios,假如,ios手机系统无法越狱,你能拿到他的真正的编译程序吗? 20 | 21 | 加固混淆这些安全技术就是用来对抗静态分析的,符号执行、模拟执行、混合执行、动态运行,就是用来破解加固混淆的。 22 | 23 | 反调试、反反反调试,特征检测,可信任执行环境,就是用来对抗动态执行的。 24 | 25 | 26 | + 越狱种类: 27 | + 有根root:就是在使用越狱之后,root权限一直存在,随用随取,重启不消失 28 | + 无根root:使用了越狱之后,获取了root权限,但是重启后会消失,重启后想要再使用root权限,需要再次使用越狱工具获取权限 29 | 30 | 31 | + 越狱方式: 32 | + 漏洞提权(系统级别) 33 | 34 | 这一种比较普遍和通用,不仅仅是移动设备,pc设备也通用。能够提权的漏洞一般都是比较严重的品相较好的漏洞,一年也没几个,但是一般都有,例如ios,号称安全性比andorid高,但是从开始到现在,一直有漏洞,虽然最新版本的漏洞还没有更新出来,但是迟早会有,andriod也是一样。漏洞可能还分为硬件和软件漏洞,这种就不细说了,主要还是看严重程度。大部分漏洞提权,都只能获得无根root,ios听说以前有一些有根的root,这种很少。 35 | 36 | + 固件提权 37 | 38 | + 普通固件提权 39 | 40 | 这种在理论上来说,是百分之百可能,但是利用的门槛比较高,要过很多的坑,开源的可能比闭源的好做,如果遇到闭源的,例如无法修改固件,不知道格式等等,android 由于是开源的,解锁的手机,可以直接通过magisk修改rom,然后刷进行,也可以寻找源码直接编译一个新的有root权限的固件刷进去,或者ios,目前研究不多,后续有时间了,这部分会补上。 41 | 42 | 但是这种通过固件提权的方式,有个问题,就是有可能会被反击,也就是厂家内置了一些固件验证,必须要过了这些验证,才能正常启动,否则是不允许启动的。这就需要修改更多固件的信息,对固件足够了解。 43 | 44 | + 板载驱动提权(可能写的不是很严谨) 45 | 46 | 这个我开始没打算写的,只听过,没见过。但是参加mosec 看见大神亲手演示。 47 | 48 | 首先,要知道,有很多设备,是不提供刷写功能的,比如向android这种刷写系统的功能,华为,小米这些手机,都是需要申请解锁的,对了还有sony,很多年前sony就有这个功能,而且是网络锁,如果没有解开网络锁,就无法刷写系统。改了固件刷不进去,也没用。但是,在系统固件之上,还有一层程序,用于刷写镜像,管理板子是否解锁,板载驱动啥的,soc这些,芯片程序,这一部分可能也是有漏洞的,而你可以运用这部分漏洞,攻击他的保护程序,从而打到可以刷写固件的目的。去年的mosec就演示了这个。芯片那些程序是开源的,通过分析找到了漏洞,然后进行攻击。我还听说过闭源程序,直接通过硬件手段,将程序搞下来,然后用ida进行分析,寻找漏洞,或者寻找算法解锁的。 49 | 50 | 51 | + 安全性: 52 | 53 | 越狱的人,基本已经很少考虑这个问题。 54 | 55 | 目前ios越狱用的就是无根越狱,而android用的是magisk,就是修改固件越狱。就安全性而言,一般来说,固件越狱是比漏洞越狱更加安全。 56 | 57 | 漏洞越狱,预示这你的手机存在root提权漏洞,不仅你可以提权,任何一个第三方app,都有和你一样的提权机会,从而获取root权限。 58 | 59 | 而固件提权,只有手机主人才拥有固件提权的机会,只要管控好嵌入的root管理程序,保证root权限不外泄,就能保证安全问题。 60 | 61 | 62 | 63 | ## ios越狱 (研究中,后续会补上) 64 | 由于有些东西还并未全部弄懂,所以不敢乱写。 65 | ## android越狱和防护 66 | android的越狱,我们一般都是来说android系统的越狱。 67 | 68 | + 固件方面 69 | 70 | 目前普遍需要接bl锁,当然有的品牌目前已经禁止解锁了。 71 | 72 | + 1、根文件系统可以读写年代,并且没有selinux 73 | 74 | 权限管控,只有DAC,没有mac,也就是说,只要获取了root权限,就可以拥有一切,而且,根文件系统可读写,就可以修改文件系统,进行永久root 75 | 76 | 当时,根本不需要内核漏洞,只需要应用漏洞,提权到root,修改rom,就可以永久提权了。 77 | 78 | + 2、开启固件rom,加入selinux 79 | 80 | 在这个时期,效果一般,selinux ,杜绝了应用层root权限泄露,但是rom固件的效果一般,当时的手机普遍能解锁,然后刷机,而且android当时自带一个recovery,配合当年很火的supersu,直接就能刷进去一个su权限,获取root。他的原理就是把根文件系统重新打包,再刷进去,覆盖以前的rom。 81 | 82 | 83 | 84 | + 3、固件验证(avb)、回滚保护、system as root、 ab分区(更新较快,没跟上) 85 | 86 | 固件验证,Android Verified Boot(这个没仔细查,研究的时候已经是2.0了),多了一个vbmeta.img分区,用于校验 boot.img system.img vendor.img 这些分区,确保不被篡改(可以解锁,关闭)。 87 | 88 | system as root,原来的就是将system.img变成根文件系统,原来都是boot.img里面的ramdisk 是根文件系统的 89 | 90 | 回滚保护,直接修改system.img镜像,无效,数据在启动的时候如果有vbmeta验证,会将数据修正。 91 | 92 | ab分区,分区修改,反正就是将recovery 分区删掉了,要想使用twrp,没法刷写进recovery了,需要用fastboot 直接应道twrp镜像 93 | 94 | + 漏洞方面 95 | 96 | 97 | + selinux 98 | 99 | linux下有两种权限管控,传统的DAC(直接访问控制),和MAC(强制访问控制)安全机制(selinx)。 100 | 关于selinux,我看了很多资料,很长时间没弄明白,所以在这里就我自己的理解详细写一下。 101 | 102 | + MAC和DAC 103 | 104 | + DAC 105 | 106 | 是以用户为主体,比如,就像现实世界中的人,你运动,你学习,你考大学,你装修,把自己的房子烧了,没人管你的,你的这些行为,就是进程,而你烧房子,花钱,等等,就是操作文件,房子就是文件,钱,就是文件,你怎么操作,没人管你,因为他们是属于你的,你是主体。 107 | 108 | + MAC 109 | 110 | mac以进程为主体,文件,属性为客体,就像现实世界中,以事件为主体,物质为客体,这种情况下,假如你要烧你的房子,假如假如你要花你的钱,在DAC中是允许的,但是在mac中,会首先判断你的行为事件,比如你的行为事件是装修,是需要对房子进行操作的,但是你的操作确是烧房子,拆房子,就会被禁止,比如你要吃饭,是要花钱的,但是你的操作是花钱买法拉利了,这也是没有权限的。粒度为进程(有安全上下文标签),管控行为,为读写,执行,等等,被执行者(文件)贴上标签,进程操作被文件的时候,通过进程和文件的标签判断,是否可以操作。 111 | 112 | selinux,是在mac基础上进行的加强和扩展 113 | 114 | + selinux 工作原理 115 | 116 | + 在编译的时候,确定了大致的安全策略,尤其一些比较重要,权限高的文件。这些安全策略会将这些文件管理的死死的,不会给予他们任何多余的权限 117 | 118 | + 全文件加标签,会将每一个文件都加上安全上下文的标签,彻彻底底的管控 119 | 120 | + 主要工作点是内核模块,在内核各个操作点,添加钩子,当操作发生的时候,会首先通过安全策略进行判断。(只要钩子写的全,就不可能绕过去) 121 | 122 | + selinux 对于漏洞的防御 123 | 124 | selinux能防御应用层的root权限漏洞,对于内核层的root权限漏洞,存在直接从内核关闭selinux的可能。但是如果无法关闭selinux,root权限将无法外泄,因为selinux权限的策略对于root用户、权限的非root用户访问默认是禁止的。 125 | 126 | 对于selinux 而言,是不是root,没有任何区别,root这个概念是DAC所提供的的用户概念,而selinux是策略控制的,不会因为,你是root用户,就不对你进行策略管控 127 | 128 | root用户在DAC权限来说是有关闭selinux的权限的(android上,别的linux没搞过,不清楚),但是如果selinux打开了,你就要拥有关闭selinux权限的MAC策略权限。 129 | 130 | 内核漏洞如果无法直接从内核层面关闭selinux,那么即使获取了root,也是无法绕过selinux策略的,跟用户层的root提权漏洞会面临一样的结果。 131 | 132 | 133 | 134 | ### magisk和固件 135 | 关于magisk,本来写了一些的,但是架不住大佬是在写的太好了,我就删除了。 136 | https://bbs.kanxue.com/thread-276361.htm#msg_header_h3_4 137 | 138 | # 越狱实践 139 | 140 | ## 提权 141 | 142 | 从上面的分析,我们知道了,越狱只有两种,漏洞越狱和固件。而这两种都是有很多成品的。固件越狱就是magisk,漏洞越狱,我们需要一个android的内核漏洞,品相要好,要能关闭selinux。 143 | 144 | 漏洞:CVE-2019-2215。 145 | 146 | 这个漏洞品相极好,而且分析的人不少,另外宣传一下,买手机可以找r0ysue,我用的是手机就是pixel 147 | 148 | https://bbs.kanxue.com/thread-264932.htm#msg_header_h1_9 149 | 150 | https://bbs.kanxue.com/thread-266198.htm 151 | 152 | 提权之后,我们要做到无感知,还需要打开selinux,并让root权限泄露出来。 153 | 154 | 155 | 156 | ## selinux 和 root权限泄露 157 | 158 | + root权限泄露 159 | 160 | 这个肯定是su程序,或者supersu,这个是有android成品的。 161 | 162 | 但是很遗憾不行,在关闭selinux后,su就断开了。因为selinux,的策略阻止root权限外泄。 163 | 164 | 165 | + 修改selinux策略 166 | 167 | selinux测策略文件是在编译后产生的,也就是说文件不能更改,而启动的时候会加载这个策略文件。 168 | 有兴趣的可以看一下这个。 169 | 170 | https://source.android.google.cn/docs/security/features/selinux?hl=zh-cn 171 | 172 | 但是有些大神就是这么牛逼,他们开发了一种selinux 注入工具 173 | 174 | https://github.com/phhusson/sepolicy-inject 175 | 176 | 如果有幸能关闭selinxu,那么就可以使用这种工具修改selinux策略,而magisk 也是有这个工具叫supolicy,我使用的magisk的工具测试的。 177 | 178 | + supolicy工具操作: 179 | 180 | selinux如果报错,后台会显示如下日志 181 | 182 | avc: denied { execute } for name="12456" dev="tmpfs" ino=472904 scontext=u:r:untrusted_app:s0:c153,c256,c512,c768 tcontext=u:object_r:unlabeled:s0 tclass=file permissive=0 183 | 184 | 我们可以先根据上面的报错的信息,修改selinxu的规则,然后用supolicy加载进去。 185 | 加载字符串规则 186 | ``` 187 | ./magiskpolicy --live "allow untrusted_app shell_data_file file { execute execute_no_trans }" 188 | 189 | ``` 190 | 加载文件 191 | ``` 192 | supolicy --apply root.te --live 193 | ``` 194 | 195 | 我们根据selinux的报错信息,来一直不断的修改策略,直到成功为止。 196 | 197 | 198 | + 修改selinux策略 199 | 200 | 修改了很多次策略,自己都麻了,但是总结起来,就三个阶段 201 | + 1、adb 获取root权限 202 | 203 | 我们虽然在adb中运行漏洞获得了权限,但是我们想要把这个权限外泄给别的adb终端,在selinux打开的情况下,策略是关闭的,所以我一次一次的重启,一次一次的根据报错信息更新策略,最后通过了。 204 | + 2、应用获取root权限 205 | 206 | 前面搞完了我以为可以过年了,回头应用用的时候才发现,不能使用,因为,selinux限制非系统应用获取root权限。对于这个策略,目前有两种方案绕过,第一种,就是接着改策略,第二种是magisk使用的,就是在root权限和获取的应用之间在套一层shell,先用shell获取root权限,然后在用应用和shell通讯,由这个持有root的shell,执行操作。这也是magisk su 和 supersu 他们两个程序在源代码上的差别。 207 | 208 | + 3、梭哈 209 | 210 | 我照着supersu 和magisk 写了一个简洁的好读的su程序使用,但是我没用实现magisk间接调用的那一部分,所以在改第二个应用获取root权限的时候,改了一天,一直改策略,改到了怀疑人生,然后一条规则在我脑海中形成了 211 | ``` 212 | allow kernel * * * 213 | ``` 214 | 成品策略文件root.te 215 | ``` 216 | allow shell kernel unix_stream_socket * 217 | allow shell devpts chr_file * 218 | allow untrusted_app shell_data_file file * 219 | allow untrusted_app kernel unix_stream_socket * 220 | ``` 221 | 上面这个,直接把selinux 很多策略直接通过了,使用需谨慎。 222 | 223 | 224 | ## mount --bind 225 | 226 | 前面搞完了后,还有点问题,就是找不到可执行文件,因为rom是只读的,无法复制文件到系统可执行文件路径,所以应用无法找到,填绝对路径?又是否要修改策略那? 227 | 228 | 所以上我们的神器mount --bind 他可以将两个目录合并到一起。但是又不改变文件。这么形容那,就像文件系统的hook。而magisk也是使用了这个。magisk 虽然代码体量不小,而且有很多对系统启动修改,已经挂载这些,但是我觉得如果没有mount --bind,代码体量将会更大,所解决的问题可能也会更多,更麻烦。 229 | 230 | 在tmp2下有一个文件supersu 231 | 232 | ``` 233 | 1|walleye:/data/local/tmp/tmp2 # ls 234 | supersu 235 | walleye:/data/local/tmp/tmp2 # 236 | 237 | ``` 238 | 不能复制过去,只读文件系统 239 | ``` 240 | walleye:/data/local/tmp/tmp2 # cp supersu /sbin/ 241 | cp: /sbin//supersu: Read-only file system 242 | 1|walleye:/data/local/tmp/tmp2 # 243 | ``` 244 | 245 | mount --bind 246 | ``` 247 | 1|walleye:/data/local/tmp # mount --bind tmp2 / sbin 248 | walleye:/data/local/tmp # cd /sbin/ 249 | walleye:/sbin # ls 250 | supersu 251 | walleye:/sbin # 252 | 253 | ``` 254 | 然后就可以使用了 255 | 256 | 257 | 258 | 259 | ## 最后 260 | 261 | 无感知的root工具,你可以将su文件的名字随意更改,然后配合上一篇的rxposed完成全局hook工作。 262 | 263 | 264 | 265 | --------------------------------------------------------------------------------