像极客一样提取Android的Root权限_busybox require superuser privileges (root)

adb reboot recovery


3. su命令源代码分析


刷完了Recovery后,就需要将su文件放到Android设备中的/system/bin或/system/xbin目录中,然后直接执行su命令即可使当前的Shell获得root权限(Shell提示符从$变成了#),以前很多不能做的事也可以做了,例如,普通用户不能查看/data/data目录中的内容,使用su命令提取root权限后也可以使用ls命令查看/data/data目录的内容了。


读者可以从网上下载合适的su文件,或直接从Android源代码中获取su文件。如果Android源代码还没有编译,需要按着1.3.2节的步骤编译整个Android源代码。成功编译Android源代码后,就可以在如下的目录找到编译好的su文件。


<Android源代码本目录>/out/target/product/generic/system/xbin


实际上这个su命令完全可以满足目前的需求,也就是提取Android设备当前Shell的root权限。不过先别忙将su文件弄到Android设备上。接下来先看一下su文件的源代码,了解一下su文件的运行原理以及为什么能在Android设备上成功执行。


读者可以从如下的目录找到su命令的源代码。


<Android源代码根目录>/system/extras/su


su是用C语言编写的普通可执行文件,主文件是su.c。读者可以打开该文件看一下su的源代码。


su.c文件中除了引用的一些头文件外,就只有一个main函数,代码如下:


源代码文件:<Android源代码根目录>/system/extras/su/su.c



#define LOG_TAG “su”
… …
/* 此处省略了#include … 语句 */
int main(int argc, char **argv)
{
struct passwd pw;
int uid, gid, myuid;
/
获取用户ID,只有root和当前的Shell能执行su命令 */
myuid = getuid();
if (myuid != AID_ROOT && myuid != AID_SHELL) {
fprintf(stderr,“su: uid %d not allowed to su\n”, myuid);
return 1;
}

if(argc < 2) {
    uid = gid = 0;

} else {
/* 根据参数指定的用户名获取用户属性,如果getpwnam函数返回0,表示参数指定的是用户ID
而不是用户名
*/
pw = getpwnam(argv[1]);

    if(pw == 0) {
        uid = gid = atoi(argv[1]);
    } else {
        uid = pw->pw_uid;
        gid = pw->pw_gid;
    }
}

/*
setgid函数要设置一个用户组ID,使用属于该用户组的用户执行任何文件时都拥有该文件所有者
的权限。setuid函数与setgid函数类似,需要设置一个用户ID。使用该用户执行任何可执行文件都
会拥有该文件所有者的权限。例如,sh命令的所有者是root用户,而当前登录用户是user,这时
使用setuid函数设置user的ID后,再执行sh命令,就相当于以root用户的身份执行sh命令,
所以进入新的Shell后就会拥有root权限
*/
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;
    }
}

/* 执行sh命令进入新的Shell,如果成功执行,当前程序会立刻退出,如果执行失败,会继续
执行下面的语句
*/
execlp(“/system/bin/sh”, “sh”, NULL);

fprintf(stderr, "su: exec failed\n");
return 1;

}


从su.c文件的代码可以看出,su命令支持多个命令行参数。这些命令行参数分为如下两类。


第1类:su的第一个参数,该参数指定了要提升权限的用户ID或用户名,如果不指定,就是当前的用户。


第2类:其余的参数。表示提升权限后要立刻执行的命令和该命令的参数。


下面都是合法的su命令调用形式。


# su


# su user


# su user ls –al /data/data/


su提升权限的核心有如下两个。


* 通过setgid和setuid函数提升权限,也就是使得任何用户在执行sh命令时都会拥有与sh命令拥有者同样的权限。由于sh命令的拥有者是root用户,所以自然就将新的Shell提升到了root权限。
* 通过execlp函数执行sh命令。由于前面已经调用了setgid和setuid函数,所以执行sh命令会进入新的Shell,并且该Shell与sh命令文件的所有者(root用户)拥有同样的权限。执行exit命令会退出拥有root权限的Shell,并重新回到原来没有root权限的Shell。再次执行exit命令后,就会退出Android Shell,回到Ubuntu Linux的终端。


4. 制作第一个Recovery刷机包(编写脚本文件)


Recovery使用的刷机包就是zip格式的压缩文件。根据不同的需求,刷机包中包含的文件不同,一个完整的刷机包非常复杂,不过本节的目的只是将su文件复制到/system/xbin目录中,所以暂时用不着那么复杂的刷机包。关于Recovery刷机包的详细制作过程将在后面跟的章节深入探讨。本文只做最基本的刷机包。


其实要做一个Recovery刷机包并不困难。制作Recovery刷机包之前通常要考虑使用的是哪个Recovery。例如,本书主要使用了Clockwork Recovery,所以可以利用Clockwork Recovery中的一些特性。


本文要制作的刷机包中只有两个目录:system和META-INF。其中system就是编译Android源代码后,在<Android源代码根目录>/out/target/product/generic目录中的system目录,也是进入Android设备的Shell后看到的“/system”目录,在该目录下包含了Android的系统文件,其中包括很多命令文件以及系统应用程序。不过本文制作的Recovery刷机包没这么复杂。由于只需要将su文件复制到/system/xbin目录,所以在system目录中只要有一个xbin子目录,并且在该目录中放一个在上一节获得的su文件即可。


可能很多读者会问,将su文件放到/system/xbin目录中,Recovery中刷机时就会将su文件复制到Android系统的/system/xbin目录中吗?答案很简单,Recovery当然不知道自己要做什么,具体要完成什么工作,如何来完成,玄机全在META-INF目录中。


在META-INF/com/google/android目录中有一个updater-script脚本文件(纯文本文件)和一个update-binary可执行文件。别看这两个文件一共不到200KB,它们却是整个Recovery刷机包中最核心的部分。尤其是update-binary,该文件通常在190KB上下,别看文件尺寸不大,这可是内嵌于Recovery的一种轻量级脚本语言的解析器,而updater-script脚本文件就是使用这种脚本语言写的。这种脚本语言就是edify。该语言除了定义一些简单的语句外,还定义了几十个用于各种操作的函数,例如,复制文件、删除文件、建立链接等。


Edify语言会在后面的章节介绍,在本文只介绍该语言的几个常用的函数。这些函数的原型、含义及其用法如下:


ui\_print


原型:ui\_print(msg1, ..., msgN);


含义:该函数用于在Recovery界面输出字符串。其中msg1、…、msgN表示N个字符串参数,该函数至少需要指定一个参数。如果指定多个参数,会将这些参数值连接起来输出。


用法:ui\_print(" hello world ");


run\_program


原型:run\_program(prog, arg1, .., argN);


含义:该函数用于执行程序,其中prog参数表示要执行的程序文件(要写完整路径),arg1、…、argN表示要执行程序的参数。prog参数是必须的,其他参数都是可选的。


用法:run\_program("/sbin/busybox","mount", "/system");


delete


    原型:delete(file1, file2, ..., fileN);


含义:该函数用于删除一个或多个文件。其中file1、file2、…、fileN表示要删除文件的路径,至少需要指定一个文件。


用法:delete("/system/xbin/su");


 


package\_extract\_dir


原型:package\_extract\_dir(package\_path, destination\_path);


含义:用于提取刷机包中package\_path指定目录的所有文件到destination\_path指定的目录。其中package\_path参数表示刷机包中的目录,destination\_path参数表示目标目录。


用法:package\_extract\_dir("system", "/system");


 


set\_perm


原型:set\_perm(uid, gid, mode, file1, file2, ..., fileN);


含义:用于设置一个或多个文件的权限。其中uid参数表示用户ID,gid参数表示用户组ID。如果想让文件的用户和用户组都是root,uid和gid需要都为0。mode参数表示设置的权限,这个权限与chmod命令设置的权限完全一样,例如,如果将一个文件设为任何用户都可以读写和执行的权限值是0777。file1、file2、…、fileN表示要设置权限的文件的路径。


用法:set\_perm(0, 0, 0777, "/system/xbin/su");


 


unmount


原型:unmount(mount\_point);


含义:用于解除文件系统的挂载。其中mount\_point参数表示文件系统。


用法:unmount("/system");


 


本文要编写的updater-script文件只会使用上面的函数。该脚本文件主要实现如下基本功能。


* 以读写模式挂载/system。
* 删除旧的su文件。
* 复制新的su文件。
* 修改su文件的权限。
* 卸载/system。


其中挂载/system调用了busybox命令,该命令并不属于Android。不过该命令十分强大,常被人称为Android的瑞士军刀。busybox是一个开源的命令集合,将多达上百个命令集成在了一个大概2MB的文件中。例如,本文要用的mount命令就是其中之一。尽管Android从本质上也属于Linux系统,但较其他Linux系统集成的命令是很少的,所以如果想在Android中执行各种操作,通常就需要将busybox文件复制到Android系统的/system/xbin或其他存储命令文件的系统目录。这样只需要一个busybox命令就可以搞定一切。如果读者想知道busybox到底支持多少命令,只需要直接执行busybox命令即可。


读者可以在http://www.busybox.net下载busybox最新版本的源代码,并按着说明使用交叉编译器编译busybox即可(在ARM架构的设备上运行必须要使用交叉编译器),为了方便读者,在随书光盘中带了一个编译好的busybox文件,路径是/tools/busybox。该文件只能在ARM架构的设备上运行,不能在X86 PC上使用。读者需要将busybox文件上传到Android设备的/system/xbin目录中(需要root权限),并执行 chmod 777 /system/xbin/busybox命令修改其权限后即可执行该命令。


现在看一下updater-script文件的代码。



ui_print(““);
ui_print(“My First Recovery Update”);
ui_print(”
”);

ui_print(“----Mounting /system ----”);

以读写模式挂载/system

run_program(“/sbin/busybox”, “mount”,“-o”, “rw”, “/system”);

ui_print(“----Delete /system/xbin/su ----”);

删除旧的su文件

delete(“/system/xbin/su”);
ui_print(“- Extracting files”);

将刷机包中system目录的所有文件复制到/system目录中的相应位置

package_extract_dir(“system”, “/system”);
ui_print(“----- Setting permissions”);

设置ui命令为任何用户都可执行

set_perm(0, 0, 0777, “/system/xbin/su”);

卸载/system

unmount(“/system”);
ui_print(“finished”);


在updater-script文件中使用run\_program函数调用了busybox命令,并通过该命令执行了mount操作,实际上,相当于在Linux终端执行如下的命令。



busybox mount –o rw /system


如果执行mount操作时未指定“-o rw”,默认是只读挂载,也就是说/system目录及其子目录是只读的,为了将/system目录变成读写模式的,需要再次执行如下的命令。



busybox mount –o rw, remount /system


其中“-o rw, remount”中的rw和remount是mount命令的两个选项,表示重新将/system目录mount成可读写的。


如果需要修改默认的挂载点对应的路径,例如,将/system挂载到/my\_system目录(该目录必须存在),需要使用下面的命令。



busybox mount –o rw /system /my_system


现在updater-script脚本文件已经编写完了,接下来还需要一个用于解析updater-script脚本文件的update-binary程序。读者可以从网上找一个Recovery刷机包,将其中的update-binary文件放到自己的刷机包中,或到<Android源代码根目录> /out/target/product/generic/system/bin目录寻找一个updater文件,将该文件改名为update-binary即可。其实updater文件也是Android源代码的某个子程序编译生成的,在后面深入探讨Recovery时再详细分析updater的实现原理。


现在Recovery刷机包的所有文件(su、updater-script和update-binary)都搞定了,接下来完成最后一步,就是将system和META-INF目录用zip格式压缩(压缩文件名任意取)。在下一节会介绍如何将这个zip格式文件中的内容刷到Nexus 7上。为了方便读者,在随书光盘上已经带了这个zip压缩文件(/recovery/su\_update.zip),读者可以直接将该文件刷到Android设备上(除了Nexus 7外,其他使用Clockwork Recovery的Android设备也可以使用该刷机包)。


## 最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套**腾讯、头条、阿里、美团等公司19年的面试题**,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含**知识脉络 + 诸多细节**,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 **高级架构技术进阶脑图、Android开发面试专题资料**,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。


**【Android核心高级技术PDF文档,BAT大厂面试真题解析】**

![](https://img-blog.csdnimg.cn/img_convert/a3369bed8b6e60ad4196108ffbbe69f7.webp?x-oss-process=image/format,png)

**【算法合集】**

![](https://img-blog.csdnimg.cn/img_convert/6f0c7e457861b136bab3edd75aeb14f5.webp?x-oss-process=image/format,png)

**【延伸Android必备知识点】**

![](https://img-blog.csdnimg.cn/img_convert/fe590f1005f67c5656666a974be7870a.webp?x-oss-process=image/format,png)

**【Android部分高级架构视频学习资源】**

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 18
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值