目錄結構 ─ hello
├── jni
├── Android.mk
└── hello.c
編譯步驟:
# cd hello # export NDK_PROJECT_PATH=`pwd` # ndk-build # adb push libs/armeabi/helloworld /data # adb shell # cd /data # ls -l # ./helloworld Hello World!
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE
LOCAL_MODULE := sur
LOCAL_SRC_FILES := su.c
# LOCAL_STATIC_LIBRARIES := \
# liblog \
# libc \
LOCAL_LDLIBS := \
-llog \
-lc \
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := eng debug
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
su.c (刪除權限檢查部分)
/* ** ** Copyright 2008, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #define LOG_TAG "su" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <dirent.h> #include <errno.h> #include <unistd.h> #include <time.h> //#include <pwd.h> //#include <private/android_filesystem_config.h> /* * SU can be given a specific command to exec. UID _must_ be * specified for this (ie argc => 3). * * Usage: * su 1000 * su 1000 ls -l */ int main(int argc, char **argv) { //struct passwd *pw; int uid, gid; //myuid; uid = 0; gid = 0; /* if(argc < 2) { uid = gid = 0; } else { pw = getpwnam(argv[1]); if(pw == 0) { uid = gid = atoi(argv[1]); } else { uid = pw->pw_uid; gid = pw->pw_gid; } } //Until we have something better, only root and the shell can use su. myuid = getuid(); if (myuid != AID_ROOT && myuid != AID_SHELL) { fprintf(stderr,"su: uid %d not allowed to su\n", myuid); return 1; }*/ if(setgid(gid) || setuid(uid)) { fprintf(stderr,"su: permission denied\n"); return 1; } /* User specified command for exec. */ if (argc == 3 ) { if (execlp(argv[2], argv[2], NULL) < 0) { fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], strerror(errno)); return -errno; } } else if (argc > 3) { /* Copy the rest of the args from main. */ char *exec_args[argc - 1]; memset(exec_args, 0, sizeof(exec_args)); memcpy(exec_args, &argv[2], sizeof(exec_args)); if (execvp(argv[2], exec_args) < 0) { fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], strerror(errno)); return -errno; } } /* Default exec shell. */ execlp("/system/bin/sh", "sh", NULL); fprintf(stderr, "su: exec failed\n"); return 1; }
代碼中使用su
Process process = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(process.getOutputStream()); os.writeBytes("mount -oremount,rw " + "/system\n"); os.writeBytes("busybox cp " + zlsuPath + " /system/bin/" + Constants.ROOT_SU + "\n"); os.writeBytes("busybox chown 0:0 /system/bin/" + Constants.ROOT_SU + "\n"); os.writeBytes("chmod 4755 /system/bin/" + Constants.ROOT_SU + "\n"); os.writeBytes("exit\n"); os.flush();
直接使用chmod出錯,很多版本的chmod不支持a+s # chmod a+s su Bad mode 使用busybox # busybox chmod a+s su
查找掛載位置(mmcblk0p10-->system)
# df
Filesystem Size Used Free Blksize
/dev 1006.3M 40.0K 1006.3M 4096
/sys/fs/cgroup 1006.3M 0.0K 1006.3M 4096
/mnt/asec 1006.3M 0.0K 1006.3M 4096
/mnt/obb 1006.3M 0.0K 1006.3M 4096
/system 1.5G 421.6M 1.1G 4096
/cache 122.0M 144.0K 121.8M 4096
/metadata 11.7M 40.0K 11.7M 4096
/data 991.9M 591.2M 400.7M 4096
/mnt/usb_storage 1006.3M 0.0K 1006.3M 4096
/mnt/internal_sd 11.7G 22.2M 11.7G 8192
/mnt/secure/asec 11.7G 22.2M 11.7G 8192
# cat /proc/partitions
major minor #blocks name
254 0 520912 zram0
179 0 15267840 mmcblk0
179 1 4096 mmcblk0p1
179 2 4096 mmcblk0p2
179 3 16384 mmcblk0p3
179 4 16384 mmcblk0p4
179 5 32768 mmcblk0p5
179 6 32768 mmcblk0p6
179 7 53248 mmcblk0p7
179 8 131072 mmcblk0p8
179 9 4096 mmcblk0p9
179 10 1572864 mmcblk0p10
179 11 16384 mmcblk0p11
179 12 4096 mmcblk0p12
179 13 1048576 mmcblk0p13
179 14 65536 mmcblk0p14
179 15 12257280 mmcblk0p15
adb shell mount -o rw,remount /system adb push su /system/xbin/su adb shell chmod 06755 /system adb shell chmod 06755 /system/xbin/su
最后已失敗告終!!
Android目前的ROOT的基本原理,是通過系統漏洞獲取ROOT SHELL權限,然后往手機里面push 最核心的兩個文件,su可執行文件和superUSer.apk。 后者用於管理對應用的授權,而前者則真正用來提升權限至ROOT。 當APK需要進行高權限操作時,使用Shell方式進行: su xxxxx 即可,此時(假設用戶授權了,通過superUser.apk)xxxx的命令就會以ROOT用戶權限方式執行。之所以push到手機中的su可以提升權限,其核心原理是su是Set-UID-Root的。具體原理分析,可參見本博客的 關於ROOT原理的文檔,說明的非常詳細了。
但是,在Android4.3中,從如下的open Source Code :dalvik_system_Zygote.cpp-》forkAndSpecializeCommon:
extern int gMallocLeakZygoteChild; gMallocLeakZygoteChild = 1; /* keep caps across UID change, unless we're staying root */ if (uid != 0) { err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); if (err < 0) { ALOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno)); dvmAbort(); } } for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) { err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); if (err < 0) { if (errno == EINVAL) { ALOGW("PR_CAPBSET_DROP %d failed: %s. " "Please make sure your kernel is compiled with " "file capabilities support enabled.", i, strerror(errno)); } else { ALOGE("PR_CAPBSET_DROP %d failed: %s.", i, strerror(errno)); dvmAbort(); } } }
注意紅色斜體的代碼,這是4.3新加入的。新加入的這些代碼,會導致set-uid-root不再起作用,也就是,原先的ROOT方案中的su+superUser.apk的模式不再工作了!!黑客們需要重新考慮新的ROOT方案了!360,騰訊等等一系列使用了ROOT權限的應用需要等待新的ROOT方案然后基於新的ROOT方案來調整提升權限的方式了,SU不再起作用了。
這里簡單說明一下原因,詳細的請參見Linux的Capacity機制。
Android中每個APK的進程是Zygote Fork出來的,默認Fork出來的進程的Real UID和EUID都是繼承自Zygote,也就是ROOT,然后,Zygote會對子進程做一些特殊處理,上面的紅色斜體代碼的作用是,在新Fork的APK進程中Drop掉所有的BoundSet Capacity,原本Zygote的BoundSet Capacity是全1,然后在APK的子進程中主動的Drop掉所有的這些Capacity,接着會再調用setUID/SetGID將APK進程的UID/GID從ROOT修改為App安裝時分配的ID(權限退化)。
這樣,你會看到,APK所在的進程的UID/GID退化成非ROOT了,原本還能通過su來提升權限。但是,4.3里面連所有的BoundsetCapacity也全部Drop掉了,里面包含的SETUID的capacity也Drop了,這樣就導致,在APK所在的進程以及任意其子進程中,都無法修改UID了,即便通過set-UID-Root方式也無法修改了。
