├── Android.mk ├── LICENCE ├── LICENSE ├── README.md ├── build.sh └── rd-patch.c /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | APP_ABI := armeabi-v7a 6 | 7 | LOCAL_MODULE := rd-patch 8 | LOCAL_SRC_FILES := rd-patch.c 9 | 10 | LOCAL_CFLAGS += -std=c11 11 | 12 | include $(BUILD_EXECUTABLE) 13 | 14 | include $(CLEAR_VARS) 15 | 16 | APP_ABI := armeabi-v7a 17 | 18 | LOCAL_MODULE := rd 19 | LOCAL_SRC_FILES := rd-patch.c 20 | 21 | LOCAL_CFLAGS += -std=c11 -DSHARED 22 | 23 | include $(BUILD_SHARED_LIBRARY) 24 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 |  LICENCE PUBLIQUE RIEN À BRANLER 2 | Version 1, Mars 2009 3 | 4 | Copyright (C) 2009 Sam Hocevar 5 | 14 rue de Plaisance, 75014 Paris, France 6 | 7 | La copie et la distribution de copies exactes de cette licence sont 8 | autorisées, et toute modification est permise à condition de changer 9 | le nom de la licence. 10 | 11 | CONDITIONS DE COPIE, DISTRIBUTON ET MODIFICATION 12 | DE LA LICENCE PUBLIQUE RIEN À BRANLER 13 | 14 | 0. Faites ce que vous voulez, j’en ai RIEN À BRANLER. 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rd 2 | == 3 | **rd** is a proof-of-concept of sandboxing apps that performs root detection. 4 | 5 | Root detection is the cargo-cult of Android security. Everyone does it, nobody knows why. 6 | 7 | How does it work? 8 | ----------------- 9 | I use `ptrace` to call `dlopen` on the remote process. The loaded library has a constructor that replaces the code of `access` with its own. 10 | 11 | If you look at the Android source code, `File.exists` calls `access`. If an app tries to check the presence of `su`, I simply have to emulate its absence. 12 | 13 | --- 14 | ## FAQ ## 15 | 16 | * Does it…? 17 | 18 | RTFC 19 | 20 | --- 21 | ## LICENSE ## 22 | It is released under the WTFPL, so you are free to show that root detection is useless. 23 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!sh 2 | 3 | ndk-build NDK_PROJECT_PATH=`pwd` APP_BUILD_SCRIPT=`pwd`/Android.mk 4 | 5 | adb push libs/armeabi/rd-patch sdcard/tmp/ 6 | adb push libs/armeabi/librd.so sdcard/tmp/ 7 | 8 | # mount -t tmpfs none /sdcard/tmp 9 | adb shell "chmod 755 /sdcard/tmp/rd-patch" 10 | 11 | adb shell "su -c \"sdcard/tmp/rd-patch \`pgrep \`\"" 12 | -------------------------------------------------------------------------------- /rd-patch.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 | 15 | #define log(...) fprintf(stdout, __VA_ARGS__) 16 | #define err(...) fprintf(stderr, __VA_ARGS__) 17 | 18 | const unsigned long CPSR_T = 0x00000020u; 19 | 20 | int shim_access(const char * path, int mode) 21 | { 22 | if (strcmp(path, "/system/app/Superuser.apk") == 0) 23 | goto err_noent; 24 | 25 | if (strcmp(path, "/system/xbin/su") == 0) 26 | goto err_noent; 27 | 28 | return syscall(__NR_access, path, mode); 29 | 30 | err_noent: 31 | errno = ENOENT; 32 | return -1; 33 | } 34 | 35 | #if defined(SHARED) 36 | static void __attribute__((constructor)) init() 37 | { 38 | log("patching access@%#010x with shim@%#010x...\n", (unsigned) &access, (unsigned) &shim_access); 39 | 40 | uint32_t * src = (uint32_t *) &access; 41 | uint32_t * dst = (uint32_t *) &shim_access; 42 | 43 | size_t range = sysconf(_SC_PAGE_SIZE); 44 | void * page_base = (void *) ((uint32_t) src & ~(range - 1)); 45 | void * page_end = page_base + range; 46 | 47 | log("reset memory protection for [%#010x, %#010x[...\n", (unsigned) page_base, (unsigned) page_end); 48 | 49 | if (mprotect(page_base, range, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) 50 | exit(EXIT_FAILURE); 51 | 52 | 0x0[src] = ( 0xe59f3000); // ldr r3, [pc] 53 | 0x1[src] = ( 0xea000000); // b +4 54 | 0x2[src] = ((uint32_t) dst); // .word dst 55 | 0x3[src] = ( 0xe12fff13); // bx r3 56 | 57 | if (mprotect(page_base, range, PROT_READ | PROT_EXEC) != 0) 58 | exit(EXIT_FAILURE); 59 | 60 | log("syscall patched!\n"); 61 | } 62 | #endif // defined(SHARED) 63 | 64 | static char * get_image_path() 65 | { 66 | char buffer[1024]; int length = readlink("/proc/self/exe", buffer, sizeof buffer); buffer[length] = '\0'; 67 | 68 | char * image_path; asprintf(&image_path, "%s", buffer); 69 | 70 | memcpy(&(strlen(image_path) - strlen("rd-patch"))[image_path], "librd.so", strlen("librd.so") + 1); 71 | 72 | return image_path; 73 | } 74 | 75 | static void dump_registers(struct user_regs * regs) 76 | { 77 | log(" r0=%#010lx r1=%#010lx r2=%#010lx r3=%#010lx\n", 0x0[regs->uregs], 0x1[regs->uregs], 0x2[regs->uregs], 0x3[regs->uregs]); 78 | log(" r4=%#010lx r5=%#010lx r6=%#010lx r7=%#010lx\n", 0x4[regs->uregs], 0x5[regs->uregs], 0x6[regs->uregs], 0x7[regs->uregs]); 79 | log(" r8=%#010lx r9=%#010lx r10=%#010lx r11=%#010lx\n", 0x8[regs->uregs], 0x9[regs->uregs], 0xa[regs->uregs], 0xb[regs->uregs]); 80 | log("r12=%#010lx sp=%#010lx lr=%#010lx pc=%#010lx\n", 0xc[regs->uregs], 0xd[regs->uregs], 0xe[regs->uregs], 0xf[regs->uregs]); 81 | log(" cpsr=%#010lx\n", 0x10[regs->uregs]); 82 | } 83 | 84 | static unsigned long find_mapping(pid_t pid, char * image) 85 | { 86 | char * map_file; if (asprintf(&map_file, "/proc/%u/maps", pid) == -1) 87 | exit(EXIT_FAILURE); 88 | 89 | FILE * map_fd; if ((map_fd = fopen(map_file, "r")) == NULL) 90 | exit(EXIT_FAILURE); 91 | 92 | char buffer[1024]; while (fgets(buffer, sizeof buffer, map_fd) != NULL) 93 | { 94 | // split line into null terminated srings (offsets are constants, except the remaining \n 95 | // 2aaaf000-2aaf1000 r-xp 00000000 1f:00 1474 /system/lib/libc.so 96 | char * begin = buffer; 97 | char * end = buffer + strlen("2aaaf000-"); 98 | char * perms = buffer + strlen("2aaaf000-2aaf1000 "); 99 | char * offset = buffer + strlen("2aaaf000-2aaf1000 r-xp "); 100 | char * path = buffer + strlen("2aaaf000-2aaf1000 r-xp 00000000 1f:00 1474 "); 101 | buffer[strlen(buffer) - 1] = '\0'; 102 | buffer[strlen("2aaaf000-2aaf1000 r-xp 00000000")] = '\0'; 103 | buffer[strlen("2aaaf000-2aaf1000 r-xp")] = '\0'; 104 | buffer[strlen("2aaaf000-2aaf1000")] = '\0'; 105 | buffer[strlen("2aaaf000")] = '\0'; 106 | 107 | if (strcmp(path, image) == 0) 108 | { 109 | if (strcmp(perms, "r-xp") == 0) 110 | { 111 | return atol(begin); 112 | } 113 | } 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | int main(int argc, char * argv[]) 120 | { 121 | int euid; if ((euid = geteuid()) != 0) 122 | { 123 | err("not run as root (%i)\n", euid); 124 | exit(EXIT_FAILURE); 125 | } 126 | 127 | if (argc != 2) 128 | { 129 | err("invalid number of arguments (%i, expected 1)\n", argc - 1); 130 | exit(EXIT_FAILURE); 131 | } 132 | 133 | pid_t tracee_pid; if (sscanf (1[argv], "%u", &tracee_pid) != 1) 134 | { 135 | err("invalid argument (%s)\n", 1[argv]); 136 | exit(EXIT_FAILURE); 137 | } 138 | 139 | log("starting patching process %u...\n", tracee_pid); 140 | 141 | if (ptrace(PTRACE_ATTACH, tracee_pid, NULL, NULL) != 0) 142 | exit(EXIT_FAILURE); 143 | waitpid(tracee_pid, NULL, __WALL); 144 | 145 | log("injecting frame...\n"); 146 | 147 | struct user_regs patch_regs, backup_regs; 148 | 149 | ptrace(PTRACE_GETREGS, tracee_pid, NULL, &patch_regs); 150 | memcpy(&backup_regs, &patch_regs, sizeof (struct user_regs)); 151 | 152 | char * image_path; 153 | unsigned long * target; 154 | size_t image_path_size; 155 | 156 | image_path = get_image_path(); 157 | image_path_size = strlen(image_path); 158 | target = (unsigned long *) patch_regs.ARM_sp; 159 | for (int count = (image_path_size + 3) >> 2; count >= 0; --count) 160 | ptrace(PTRACE_POKEDATA, tracee_pid, (void *) --target, (void *) count[(unsigned long *) image_path]); 161 | 162 | patch_regs.ARM_r0 = (unsigned long) target; 163 | patch_regs.ARM_r1 = RTLD_LAZY; 164 | patch_regs.ARM_sp = (unsigned long) target; 165 | patch_regs.ARM_lr = 0; 166 | patch_regs.ARM_pc = (unsigned long) &dlopen; 167 | patch_regs.ARM_cpsr |= CPSR_T; 168 | 169 | ptrace(PTRACE_SETREGS, tracee_pid, NULL, &patch_regs); 170 | 171 | ptrace(PTRACE_CONT, tracee_pid, NULL, NULL); 172 | log("resume process %u...\n", tracee_pid); 173 | waitpid(tracee_pid, NULL, __WALL); 174 | 175 | ptrace(PTRACE_GETREGS, tracee_pid, NULL, &patch_regs); 176 | ptrace(PTRACE_SETREGS, tracee_pid, NULL, &backup_regs); 177 | 178 | if (patch_regs.ARM_r0 != 0 && patch_regs.ARM_pc == 0 && find_mapping(tracee_pid, get_image_path()) != 0) 179 | log("successfully patched %u!!!\n", tracee_pid); 180 | 181 | ptrace(PTRACE_DETACH, tracee_pid, NULL, NULL); 182 | 183 | exit(EXIT_SUCCESS); 184 | } 185 | --------------------------------------------------------------------------------