玩智能手机的人都会存在两大烦恼,一个是iPhone上的“越狱”,另一个就是Android上的”Root”。在Android设备中获得超级用户(Super User)权限的过程中我们称为Root,即类似Linux系统下的Root账户。所以,超级用户权限我们又称为Root权限。这种特殊的账户,在以Unix为核心的操作系统上,拥有所有文件和程序的所有权限。换句话说,拥有Root权限后,你能够完全控制整个操作系统。当然,系统处于安全考虑,在出厂之前都会关闭Root权限。
拿到了Root权限,应用程序就可以在Android系统上为所欲为了。所以,很多恶意程序与很多的安全类软件都希望获取到Root权限。恶意程序拿到了Root权限之后,发送短信、窃取个人隐私、偷跑流量、安装软件等操作都能在静默状态下完成,个人手机毫无安全可言。根据这个理论我们最好还是别Root自己的Android设备是最安全的。但是,我们清楚Android设备的第三方Rom很多,不同的Rom会有自己的不同的预装应用软件(大部分这些预装应用软件都相当流氓),我们需要卸载这些预装软件就需要Root权限。当然,我们一些拥有了Root权限的应用,如安全卫士,安全管家类,应用我们就能屏蔽掉其他应用中的广告、监管其他应用、系统垃圾清理等等。
很多人在Root失败的情况下,往往会选择去刷机。所谓刷机,就是给手机重新装一遍系统。但是,细分又可以分为刷第三方系统和刷官方底包,我们常说的刷机实际上是指,刷第三方的ROM包。不管你Root的理由是什么,你应该明白一个道理,一旦设备Root了之后你的设备的文件将会完全暴露出来而产生的安全影响。
Android获取Root其实和Linux切换Root用户是一样的。在Linux下我们只需要执行“su”或者“sudo”,然后,输入Root账户的密码就可以获得Root权限了(其实就是将uid与gid设置为0,Root用户)。Android 5.0之前的系统都不支持多用户切换,对“su”也没有做密码验证。Android系统没有“sudo”命令,且很多厂商从手机的安全上考虑,一般都是去掉了“su”命令的。
其实,远没有这么简单。已经获取了Root后的系统与为Root的系统其实就是多了两个东西。“su”与“SuperUser.apk”。“su”是用来获取Root权限的命令。“SuperUser.apk”其实就是一个管理Root权限的应用,负责对整个系统中Root权限的管理与安全提示,让Root后的系统相对安全一些。
既然,一个“su”命令就能将我们切换到超级用户状态下,那么我们具体看看这个神奇的“su”命令做了些什么。“su”命令文件的源码在系统目录“/system/extras/su/”下,我们定位到它的main函数。
int main(intargc, char **argv)
{
struct passwd *pw;
int uid, gid, myuid;
/* 只有root用户与shell用户可以使用Root*/
myuid = getuid();
if (myuid != AID_ROOT && myuid !=AID_SHELL) {
fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
return 1;
}
// 直接将 uid 与gid 置为0,即Root账户的uid与gid
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;
}
}
// 设置uid 与 gid
if(setgid(gid) || setuid(uid)) {
fprintf(stderr,"su: permission denied\n");
return 1;
}
/* 执行用户指定的命令 */
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;
}
要知道怎么Root我们还是先看看一个已经获得了Root权限的设备,应为su文件才是主要的我们重点看看su。使用adb shell命令查看系统中“su”文件的文件属性。
shell@android:/system/xbin$ ls -l
ls -l
-rwxr-xr-xroot shell 1165484 2014-10-17 16:49 busybox
-rwxr-xr-xroot shell 55664 2014-10-17 16:49 dexdump
-rwxr-xr-xroot shell 39796 2014-10-17 16:49 hcitool
-rwsr-sr--root 9802 42276 2014-10-17 16:49 ota
-rwxr-xr-xroot root 9400 2014-10-17 16:49 procmem
-rwxr-xr-xroot root 9508 2014-10-17 16:49 procrank
-rwxr-xr-xroot shell 30208 2014-10-17 16:49 sb
-rwsr-sr--root system 9396 2014-10-17 16:49 shelld
-rwxr-xr-xroot shell 255412 2014-10-17 16:49 strace
-rwsr-sr-xroot root 30208 2014-10-17 16:49 su
发现su的命令文件已经变为了 “-rwsr-sr-x”,那么我们获取Root思路就来了。
根据上面的分析,很多同学应该都会想到,其实我们获取Root权限就是要完成以下几步:
1、对于无“su”的系统,我们先通过其他方式将编译好的“su”放置如系统环境下(/system/bin/su)
2、将文件的Owner设置为Root(chown root:root system/bin/su)
3、将系统中的“su”文件权限切换至“-rwsr-sr-x”(chmod 6755 /system/bin/su)
4、安装SuperUser.apk
那么,现在问题又来了,对/system/bin目录的操作需要Root权限,这个貌似让我们进入一个死循环。所以,我们就需要其他特殊的方式来完成以上步骤。
总结的目前的思路有两种:
1. 找一个已经有Root权限的进程来完成以上步骤
思路:通过系统漏洞提升权限到Root
问题:如何找到Root的漏洞,目前所找到的漏洞有哪些?
思路:init进程启动的服务进程,如adbd、rild、mtpd、vold等都有Root权限,找他们的漏洞。
2. 通过系统之外的某些方法植入
思路:通过Recovery刷机方式刷入“su”
问题:如何刷入Recovery
系统漏洞是指应用软件或操作系统软件在逻辑设计上的缺陷或在编写时产生的错误,且这个错误容易被不法分子利用。任何系统都会存在漏洞,但是漏洞却不是与生俱来的,是无数个计算机爱好者与黑客们努力出来的结果。Android系统漏洞,伴随着Android的发展而不断的发现。虽说,Google官方每发一次版本都会修复很多系统漏洞,但不意味着很多漏洞在我们今天就没用了。因为,Android的系统版本碎片化,较大很多历史版本在市场上分布量还较广。再就是,国内的很多第三方ROM为了提高自己的吸引力故意不修复Root漏洞,甚至有些直接留下自己ROM的Root后门。
l RageAgainstTheCage漏洞
RageAgainstTheCage漏洞,是早在2010年的时候,由Sebastian Krahmer发现的。之后这个漏洞广泛被利用在广大的应用中,其中最著名的就是被用于z4root等提升权限工具、Trojan.Android.Rootcager等恶意代码之中。
要想理解RageAgainstTheCage破解过程我们首先需要了解一下adb工具,SDK中包含adb工具,设备端有adbd服务程序后台运行,为开发机的adb程序提供服务,adbd的权限,决定了adb的权限。具体用户可查看/system/core/adb下的源码,查看Android.mk你将会发现adb和adbd其实是一份代码,然后通过宏来编译。
查看adb.c的adb_main函数你将会发现adbd中有如下代码:
int adb_main(int is_daemon)
{
......
//执行若干程序后,提升权限为root权限
property_get("ro.secure", value, "");
if (strcmp(value, "1") == 0) {
// don't run as root if ro.secureis set...
secure = 1;
......
}
//setgid、setuid决定是否为root权限,一旦secure为1,则降级用户权限。
if (secure) {
......
setgid(AID_SHELL);
setuid(AID_SHELL);
......
}
}
从中我们可以看到adbd会检测系统的ro.secure属性,如果该属性为1则将会把自己的用户权限降级成shell用户。一般设备出厂的时候在/default.prop文件中都会有:
ro.secure=1
这样将会使adbd启动的时候自动降级成shell用户。
然后我们再介绍一下adbd在什么时候启动的呢?答案是在init.rc中配置的系统服务,由init进程启动。我们查看init.rc中有如下内容:
# adbd is controlled by thepersist.service.adb.enable system property
service adbd /sbin/adbd
disabled
对Android系统属性有了解的朋友将会知道,在init.rc中配置的系统服务启动的时候都是root权限(因为init进行是root权限,其子程序也是root)。由此我们可以知道在adbd程序在执行:
/* then switch user and group to"shell" */
setgid(AID_SHELL);
setuid(AID_SHELL);
/*setgid和setuid是降级命令,执行此语句之后,用户权限被降级为普通权限,没有root功能*/
代码之前都是Root权限,只有执行这两句之后才变成shell权限的。
这样我们就可以引出Root破解过程中获得Root权限的方法了,那就是让以上面setgid和setuid函数执行失败,也就是降级失败,那就继续在Root权限下面运行了。
思路也比较简单,我们具体就细分为下面几个步骤,即:
1. 出厂设置的ro.secure属性为1,则adbd也将运行在shell用户权限下;
2. adb工具创建的进程ratc也运行在shell用户权限下;
3. ratc一直创建子进程(ratc创建的子程序也 将会运行在shell用户权限下),紧接着子程序退出,形成僵尸进程,占用shell用户的进程资源,直到到达shell用户的进程数为 RLIMIT_NPROC的时候(包括adbd、ratc及其子程序),这是ratc将会创建子进程失败。这时候杀掉adbd,adbd进程因为是 Android系统服务,将会被Android系统自动重启,这时候ratc也在竞争产生子程序。在adbd程序执行上面setgid和setuid之前,ratc已经创建了一个新的子进程,那么shell用户的进程限额已经达到,则adbd进程执行setgid和setuid将会失败。根据代码我们发现失败之后adbd将会继续执行。这样adbd进程将会运行在root权限下面了。
4. 这是重新用adb连接设备,则adb将会运行在root权限下面了。
这么一看,如过我们修复此漏洞也比较简单的,只需要做一个简单的判断,如下:
/* then switch user and group to "shell" */
if (setgid(AID_SHELL) != 0) {
exit(1);
}
if (setuid(AID_SHELL) != 0) {
exit(1);
}
如果发现setgid和setuid函数执行失败,则让adbd进程异常退出,就算把这个漏洞给堵上了。为什么这么多设备都没有堵上这个漏洞呢?我觉得是设备厂商的策略(不排除傻X的厂商存在哦),虽然知道怎么封堵漏洞,但是就是留着个后门给大家,让第三方给自己定制Rom,提高自己系统的易用性。
l 已经发现的Root漏洞
目前发现的较为经典的Root漏洞有,ZergRush、GingerBreak、RageAgainstTheCage、ASHMEM、Exploid、Levitator、Mempodroid、Zimperlich等。不同的Android设备可利用的漏洞不一样,这个就需要我们Root之前做一个漏洞检测。下面是一个知名的Root漏洞检测软件X-Ray的截图。
图
为了保护在Root后的Android设备的安全,于是乎SuperUser应用项目就理所当然的产生了。SuperUser应用是一个开源的项目,最著名的SuperUser项目是koush开发的(当然,其它的SuperUser项目也大同小异),我们能在https://github.com/koush/Superuser上看到SuperUser.apk的源码。
1. SuperUser的工作原理
理解完了Root的本质之后,用一句话来总结就是,Root就是往 /system/bin/ 下放一个带s位且不检查调用者权限的su文件。当然,任何一个应用程序都可以调用su命令来获取Root权限,这样对Android设备来说是相当不安全的。所以,SuperUser应用就需要完成以下几个工作来保证Root后的设备的安全。
l su文件是否是被替换,保证su的安全(替换后的su文件,估计都动过了手脚)
l 建立白名单,只允许白名单中的应用使用Root权限
l 应用程序调用su命令时,向用户弹出Root申请界面,如下图所示
系统原生的su对于所有的应用程序是平等的,所以原生的su是无法保证su的安全的,SuperUser必须安装入自定义的su,以及能够保证自身su不被替换的deamon进程。应用请求Root权限的时候,自定义的su则会通知SuperUser。由SuperUser来进行白名单存储于Root权限授予提示,让用户选择是否给予该应用Root权限。而su与SuperUser之间的通信是靠一个阻塞的Socket来完成的。
具体的操作流程图如下所示:
2. 谁来保证SuperUser的安全
那么问题来了,我们使用SuperUser来保证Root后的Android设备的安全,那么我们如何去保证SuperUser的安全。SuperUser也会存在自己的漏洞,如CVE-2013-6774漏洞就是通过攻击SuperUser应用来获取Root权限的。市面上的一键Root工具多不胜数,大多数都是根据开源的SuperUser项目修改而来。每个Root工具都有自己的SuperUser管理与su命令,在其中做了什么我们无法得知,他们针对SuperUser已知的漏洞修复情况也无法得知。所以,SuperUser的安全也就难以保证了。
由上面的过程我们得知,Root后的手机会将所有的权限暴露有应用程序。虽然,加入了SuperUser作为权限管理,提醒用户给予Root权限授予。但是,第三方SuperUser、su的安全性都没法保证,我们更不清楚SuperUser处了做权限管理还做了些什么,也不清楚su是不是在源码上留了后门。所以,很多企业与个人都推出自己的一键Root工具,这些工具都是存在一定的目的性的。不管你使用扫描工具进行Root,也不管Root的目的是声明,一旦Android设备Root之后的该设备也就毫无隐私、安全可言了。