说adb,所有做android开发的人都知道,我们敲这几行adb调试的命令都很娴熟。既然这样,为什么还要讲adb?不过话说在其那头,这里讲adb,还是有一定意义的,说不定能给予你一些灵感。
平时我们使用最多的命令无非:
adb connect
adb remount
adb push
adb install
这里四条命令,其中第1,3,4条都很直白:连接,推送,安装。可是,每当我们要往“system”目录推送东西的时候,都要做 adb remount 的动作。那么问题来了:为什么要adb remount ??在试行adb remount 这条命令的时候究竟做了些什么事情?
在解答这个问题之前,我们可以先来看看 system 的权限管理是怎样的。
System目录的权限由两个地方所决定:
首先是在init.rc 里面创建system目录的时候会指定system目录的权限和用户组:
mkdir /system 755 root root
从755 我们可以得知,只有root用户具备对system目录的写权限。
其次是vold里面去加载system分区的时候指定的挂载权限,其操作的目录是放在fstab.$VENDOR 里面:
dev/block/mmcblk1p5 /system ext4 ro
从上面我们可以看到位于flash的partion 5 的就是system 分区。Vold去挂载system的时候,把它挂载成 ro 了,也就是只读。
所以平时我们去往system 目录写东西的时候会被系统提示:
Read-only file system
但是为什么我们执行一下 adb remount 就可以往system 里面写东西了呢?既然有这个疑问,我们就先来看看 adb remount 这条命令做了写什么事情。
看到adb代码里面,正确来说是adbd:
106 void remount_service(int fd, void *cookie)
107 {
108 int ret = remount_system();
109
110 if (!ret)
111 write_string(fd, "remount succeeded\n");
我们执行adb remount 这条命令会触发到remount_service 这个函数,而在这个函数里面做了一个动作,就是“remount_system”从新挂载了system分区:
72 static int remount_system()
73 {
74 char *dev;
75 int fd;
76 int OFF = 0;
77
78 if (system_ro == 0) {
79 return 0;
80 }
81
82 dev = find_mount("/system");
83
84 if (!dev)
85 return -1;
86
87 fd = unix_open(dev, O_RDONLY);
88 if (fd < 0)
89 return -1;
90
91 ioctl(fd, BLKROSET, &OFF);
92 adb_close(fd);
这个函数的详细过程就不进行分析了,如果有兴趣的话可以跟进一下代码。
其实这里面做了一个动作,把system 分区从新挂载了一下,通过上面的分析我们也知道,就是把system挂载为可读写的了。
好,既然这样,那我们是否也可以这样联想:如果我们手动去从新从新挂载system,会不会也能把system分区挂载为读写呢?
于是在串口命令行执行:
mount -o rw,remount -t ext4 /dev/block/mmcblk1p5 /system
发现执行这条命令之后,我们也能拥有对system的读写权限了,证明猜想是对的。
但是,所有以上的操作都有一个约束条件:必须在root用户进行操作。也就是所adbd(这里要理清adb跟adbd的关系,一个是client端,一个是service端,具体情况可以网上查资料,这里不做详细说明)必须是运行在root用户组下面,和串口的shell必须是root。这样说,有点疑惑了吧?既然疑惑,那么接下来讲的内容是:
1、 adbd的执行与用户组管理
2、 串口终端的shell
adbd的执行与用户组管理
我们在pc机上对板子进行adb命令动作的前提条件是,板子上必须启动了adbd服务。不然会提示出错:
C:\Users\Administrator>adb connect172.21.78.160
error:
那么adbd是在哪里启动呢?是在init.rc 里面:
557 service adbd /sbin/adbd
558 class core
559 socket adbd stream 660 system system
560 disabled
561 seclabel u:r:adbd:s0
这里面可以做一个实验,如果把init.rc里面的adbd服务关闭了,那么当我们在pc机上使用adb命令对板子进行操作的时候,也会同样报错:
C:\Users\Administrator>adb connect172.21.78.160
error:
说到这里,不知道你有没有发现:在init.rc 执行的过程中都是具备所有的操作权限,但当机子启动起来,我们通过adb 或串口去操作的时候就不具备权限了呢?其实原因也在adbd启动的时候,把root权限改为shell权限了:
/* then switch user and group to "shell" */
setgid(AID_SHELL);
setuid(AID_SHELL);
好,adbd讲到这里,接下来讲:串口终端的shell
我们平时在操作串口的时候会看到:
root@$BOARD:/ #
上一行语句前面这个“root”是什么含义呢,是由哪里决定的呢?
其实这个“root”是说当前串口终端这个shell是运行在root权限下的。当然,决定这个权限的,还是我们强大的init.rc :
service console /system/bin/sh
class core
console
user root
group root
如果你把 user 和group 都换成 shell 的话,那么你在串口终端下会发现:
shell@$BOARD:/
用户组变为一般的shell 了,那边这时候在串口终端下执行:reboot,拷贝,adbd 等命令都会提示:
Operation not permitted
因为shell用户已经不具备相关的操作权限了。
但,如果执行一下su命令,再进行reboot动作,如:
shell@$BOARD:/ $ su
shell@$ BOARD:/ # reboot
那么命令及可以继续执行下去,因为su的动作让我们获取了root的权限了。
所以在网上一些提到android root 的方法:
a. adb push su /system/bin
b. adb push SuperUser.apk /system/app
c. adb shell chmod 4755 /system/bin/su
以及去除root的方法:
1. 你的Android设备上要有一个深度的文件管理器应用,如ES File Explorer等
2. 打开文件管理器
3. 去/system系统目录
4. 打开/bin目录,或/xbin目录
5. 找到su
6. 删除它
7. 回到/system系统目录
8. 去/app目录
9. 找到Superuser.apk
10. 删除它
11. 重启设备
12. 完成
不过这两种方式,都有一个相同的约束条件,就是串口的shell必须是root权限。
介绍就到这里,如果细细阅读你会发现,如果想在android的产品上做用户权限的管理:如限定用户的某些权限,又或者做一个完全封闭的系统。那么知道这些原理,做起来也很得心应手。