Android手机Root受权原理细节全解析

首先关于Root的方式,这里不作详解,能够有不少漏洞,好比利用uid溢出后归为0,获得Root权限,而后操做文件系统等。
 
手机Root后,最重要的是,给手机安装了su程序和superuser apk。 su通常被安装在/system/xbin 或者 /system/bin 下面,su文件的权限以下:
 
# ls su -l
ls su -l
-rwsr-sr-x    1 root     root         26336 Aug  1  2008 su
 
这里能够看到,su的Owner和Group分别为Root,Root。 Other用户具备exeute权限,另外,su设置了suid和sgid,这个很是重要,后面会详述,这个使得Su进程能够提高自身的EUID。
 
咱们这里以RootExplorer为例,看是如何申请和提高权限的:
 
首先,ps一下,能够看到root explorer的信息,这里第一列是UID,更加准确的,应该是EUID。
app_73    1143  103   301620 39944 ffffffff 400194c4 S com.speedsoftware.rootexp
lorer
 
这里能够看到,Root Explorer的EUID=10073(App Base是从10000开始的)
 
而后,Root Explorer经过Runtime方式,使用shell 命令行运行了“su”, 因此,会有一个Root Explorer启动的sh:
 
app_73    1159  1143  764    376   c003e454 4001cf94 S /system/bin/sh
 
default状况下,进程的UID是继承的,这里sh的PPID是1143,说明是RootExplorer启动的。
 
RootExplorer经过相似下面的代码运行“su”:

p = Runtime.getRuntime().exec("su");     linux

因此,1159  PID的sh会启动su,这里须要注意的是,su进程的Real UID是10073,由于继承自parent,可是,其EUID却提高为了ROOT,这就是因为SUID被设置的缘由。 因为su进程的EUID是ROOT,因此致使了su能够作不少高权限的事情。 android

su会经过: sql

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."); shell

启动SuperUser Request Activity来询问用户是否受权当前应用,若是否的话,则su将return 结束, 不然su会继续运行。 app

获得用户许可后(经过sqlite和SuperUser Request Activity交流用户许可),su会将本身的Real UID也设置为ROOT: ui

 if(setgid(0) || setuid(0)) 
  return permissionDenied(); spa

因为su的EUID是ROOT,因此su有权限执行以上代码,执行完后su进程的Real UID,effective UID都变成了ROOT。 这里为何要同时提高Real UID的权限,后面会说明。 .net

而后,su会启动一个额外的sh来运行用户的指令: 命令行

char *exec_args[argc + 1];
 exec_args[argc] = NULL;
 exec_args[0] = "sh";
 int i;
 for (i = 1; i < argc; i++)
 {
  exec_args[i] = argv[i]; orm

 }
 execv("/system/bin/sh", exec_args);
 return executionFailure("sh");


 这里很是关键的代码是execv! 这里没有使用fork,而是直接使用execv,这将致使不会建立新的进程运行sh image,而是直接在当前su的进程空间load并执行sh image。 因此,return executionFailure("sh"); 正常状况下是永远不会被执行的,执行完execv后,就开始直接sh的代码了。

这里能够看到,因为复用su当前进程来运行sh,使得sh运行的进程的EUID,Real UID都是ROOT,那么,任何操做均可以执行了。 若是不提高Real UID为Root的话,那么只要sh运行中经过启动外部程序的方式来完成操做的话,就会出现权限问题,由于新启动的进程只会继承父进程的Real UID,而不是EUID,那么新起的进程的Real UID=EUID= parent RealUID != ROOT,那么某些操做可能有问题。 因此,su把本身的Real UID也提高为ROOT后,则无论启动后的任何代后的子进程执行操做,都是以ROOT的权限运行。

RootExplorer经过获得sh流后,就能够经过该流执行任何shell指令了:

  • try {   
  •    // Preform su to get root privledges   
  •    p = Runtime.getRuntime().exec("su");    
  •   
  •    // Attempt to write a file to a root-only   
  •    DataOutputStream os = new DataOutputStream(p.getOutputStream());   
  •    os.writeBytes("echo \"Do I have root?\" >/system/sd/temporary.txt\n");   
  •   
  •    // Close the terminal   
  •    os.writeBytes("exit\n");   
  •    os.flush();   

     

    经过ps也能够明显看到su变成了sh,以下,

    app_73    1143  103   301620 39944 ffffffff 400194c4 S com.speedsoftware.rootexp
    lorer
    app_73    1159  1143  764    376   c003e454 4001cf94 S /system/bin/sh
    root      1161  1159  772    388   c012e760 4007c578 S sh

    这里的PID=1161的进程实际上是su(能够经过打log验证),当执行execv("/system/bin/sh", exec_args);后,进程名就变成了sh。固然进程EUID不变,仍然是ROOT。

    经过cat 进程的proc信息,咱们也能够看到其实RootExplorer自身的UID或者权限根本没有被提高(这是和linux上执行su不同的地方),

    root@android :/proc/1143 # cat status
    cat status
    Name:   re.rootexplorer
    State:  S (sleeping)
    Tgid:   1143
    Pid:    1143
    PPid:   103
    TracerPid:      0
    Uid:    10073(Real)   10073(Effecttive)   10073(saved)   10073
    Gid:    10073   10073   10073   10073

    而su演变成的sh,倒是具备全部的ROOT UID:

    root@android :/proc/1161 # cat status
    cat status
    Name:   sh
    State:  S (sleeping)
    Tgid:   1161
    Pid:    1161
    PPid:   1159
    TracerPid:      0
    Uid:    0       0       0       0
    Gid:    0       0       0       0

     

    总结: Android中App受权获取Root权限,其实不是App自身的权限提高了,而是经过具备ROOT权限的sh流来执行shell命令

    相关文章
    相关标签/搜索