摘要:经常有人问,你手机root了吗,那么root了到底是什么意思,root了之后能赶上吗?

ROOT 是什么?

在操作系统里,一般有两种用户,管理员 和普通用户,两者最大的区别在于 用户对系统的操作权限有高有低。管理员权限 对系统有着毁天灭地的能力,熟悉windows的人都知道 一般有个Administrator(管理员)用户,而在Linux系统里(包括Android)这个管理用户的统一名字 叫ROOT

怎么切换到ROOT用户

熟悉Linux的同学都知道 一般情况下,Linux下root用户默认是没有密码 可以 用su root 命令 切换到 root用户。

  • 但是在android中 没有su命令。但是我们可以通过adb 把su文件放到/system/bin/下面
    这样 我们就可以执行su命令了

    1
    adb push su /system/bin
  • 当我们在root的过程中,还会给系统装一个程序,用来作为运行提示,由用户来决定,是否给予最高权限。这个程序的名字叫做Superuser.apk。当某些程序执行su指令想取得系统最高权限的时候,Superuser就会自动启动,拦截该动作并作出询问,当用户认为该程序可以安全使用的时候,那么我们就选择允许,否则,可以禁止该程序继续取得最高权限 我们将这个APK放到system/app

    1
    adb push su /system/bin
  • 这还没完 ,我们还需要让 su 文件可以让任何用户都能执行,所系需要设置他的访问权限

    1
    adb shell chmod 4755 /system/bin/su

总结:手机ROOT 实际上就是干了3件事

  • 1:把su文件放到/system/bin/下面
  • 2:把Superuser.apk放到/system/app/下面
  • 3:修改su文件的访问权限

一切都是那么完美,就差干了,但是 我们是不是想简单了 ROOT 真的这么容易吗

如果是eng 版本的android系统(先理解成开发版吧) 那么完成这3部我们就大工告成了,但是现实就是这么苟且,我们的手机不是eng版而是user版,最大的问题在于 user版system/bin只有只读权限。 我们需要ROOT权限才能 把 su文件push到system/bin目录下,然后获取ROOT权限 却需要把su文件,这里就出现了一个 逻辑的死循环。还有些系统 会在启动的时候 就删除su文件。哈哈哈

这就搞不下去了。那怎么办,只有去找每个厂商发布系统的漏洞了。

深入理解Root机制 来源 http://blog.csdn.net/chaihuasong/article/details/8463212/

其流程是:

  • Su 被用户调用

  • Su 创建了一个socket监听

  • Su 向Superuser发送了一个广播,说是有一个程序要请求root

  • Su 等待socket 数据接收。有超时处理。

  • Superuser 界面收到广播后,弹出一个对话框,询问用户

  • Superuser 向传来的数据中的socket写回用户应答结果。

  • Su 根据socket得到的结果处理应该不应该继续执行

  • 完成提权管理
    superuser.apk这个程序是root成功后,专门用来管理root权限使用的,防止被恶意程序滥用。

源码地址: http://superuser.googlecode.com/svn/trunk

我们有两点疑问:

  • superuser是怎么知道谁想用root权限?

  • superuser是如何把用户的选择告诉su程序的?

即superuser和su程序是如何通讯的,他们俩位于不通的时空,一个在java虚拟中,一个在linux的真实进程中。

superuser共有两个activity: SuperuserActivity和 SuperuserRequestActivity,其中SuperuserActivity主要是用来管理白名单的,就是记住哪个程序已经被允许使用root权限了,省的每次用时都问用户。

SuperuserRequestActivity 就是用来询问用户目前有个程序想使用root权限,是否允许,是否一直允许,即放入白名单。

这个白名单比较关键,是一个sqlite数据库文件,位置:

/data/data/com.koushikdutta.superuser/databases/superuser.sqlite

上文说过,root的本质就是往 /system/bin/下放一个su文件,不检查调用者权限的su文件。普通程序可以调用该su来运行root权限的命令。superuser.apk中就自带了一个这样的su程序。一开始superuser会检测/system/bin/su是否存在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
File su = new File("/system/bin/su");
// 检测su文件是否存在,如果不存在则直接返回
if (!su.exists()) {
Toast toast = Toast.makeText(this, "Unable to find /system/bin/su.", Toast.LENGTH_LONG);
toast.show();
return;
}
//如果大小一样,则认为su文件正确,直接返回了事。
if (su.length() == suStream.available())
{
suStream.close();
return; //
}
// 如果检测到/system/bin/su文件存在,但是不对头,则把自带的su先写到"/data/data/com.koushikdutta.superuser/su"
//再写到/system/bin/su。
byte[] bytes = new byte[suStream.available()];
DataInputStream dis = new DataInputStream(suStream);
dis.readFully(bytes);
FileOutputStream suOutStream = new FileOutputStream("/data/data/com.koushikdutta.superuser/su");
suOutStream.write(bytes);
suOutStream.close();
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n");
os.writeBytes("busybox cp /data/data/com.koushikdutta.superuser/su /system/bin/su\n");
os.writeBytes("busybox chown 0:0 /system/bin/su\n");
os.writeBytes("chmod 4755 /system/bin/su\n");
os.writeBytes("exit\n");
os.flush();
~~~
有进程使用root权限,superuser是怎么知道的呢,关键是句:
~~~c++
sprintf(sysCmd, "am start -a android.intent.action.MAIN
-n com.koushikdutta.superuser/com.koushikdutta.superuser.SuperuserRequestActivity
--ei uid %d --ei pid %d > /dev/null", g_puid, ppid);
if (system(sysCmd))
return executionFailure("am.");

原理是am命令,am的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
usage: am [subcommand] [options]
start an Activity: am start [-D] [-W] <INTENT>
-D: enable debugging
-W: wait for launch to complete
start a Service: am startservice <INTENT>
send a broadcast Intent: am broadcast <INTENT>
start an Instrumentation: am instrument [flags] <COMPONENT>
-r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)
-e <NAME> <VALUE>: set argument <NAME> to <VALUE>
-p <FILE>: write profiling data to <FILE>
-w: wait for instrumentation to finish before returning
start profiling: am profile <PROCESS> start <FILE>
stop profiling: am profile <PROCESS> stop
<INTENT> specifications include these flags:
[-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]
[-c <CATEGORY> [-c <CATEGORY>] ...]
[-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
[--esn <EXTRA_KEY> ...]
[--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
[-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
[-n <COMPONENT>] [-f <FLAGS>]
[--grant-read-uri-permission] [--grant-write-uri-permission]
[--debug-log-resolution]
[--activity-brought-to-front] [--activity-clear-top]
[--activity-clear-when-task-reset] [--activity-exclude-from-recents]
[--activity-launched-from-history] [--activity-multiple-task]
[--activity-no-animation] [--activity-no-history]
[--activity-no-user-action] [--activity-previous-is-top]
[--activity-reorder-to-front] [--activity-reset-task-if-needed]
[--activity-single-top]
[--receiver-registered-only] [--receiver-replace-pending]
[<URI>]<span style="font-family:Calibri;font-size:14px;"> </span>

还有个疑点,就是su怎么知道用户是允许root权限还是反对呢?原来是上面提到的白名单起来作用,superuser把用户的选择放入:

/data/data/com.koushikdutta.superuser/databases/superuser.sqlite 数据库中,然后su进程再去读该数据库来判断是否允许。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
static int checkWhitelist()
{
sqlite3 *db;
int rc = sqlite3_open_v2(DBPATH, &db, SQLITE_OPEN_READWRITE, NULL);
if (!rc)
{
char *errorMessage;
char query[1024];
sprintf(query, "select * from whitelist where _id=%d limit 1;", g_puid);
struct whitelistCallInfo callInfo;
callInfo.count = 0;
callInfo.db = db;
rc = sqlite3_exec(db, query, whitelistCallback, &callInfo, &errorMessage);
if (rc != SQLITE_OK)
{
sqlite3_close(db);
return 0;
}
sqlite3_close(db);
return callInfo.count;
}
sqlite3_close(db);
return 0;
}

以上就是关于ROOT 的一部知识