首页 > 代码库 > 应用获取root权限分析及总结

应用获取root权限分析及总结

ROM授权root权限,主要技术点在哪里?如何实现?带着这些问题,边实验边分析,并将过程和犯的错误记录如下。


1、rom支持root授权,需要包含su

简单点说,就是rom中支持su指令;必须包含su可执行程序,对应的代码/system/su目录下代码; 编译生成su程序后,再将其push到/system/xbin目录下;
注意:此时需要修改该文件的执行权限, chmod 755 su

 

2、应用程序如何获取root权限?

关键点在于下面这句,通过执行su产生一个具有root权限的进程:
Process p = Runtime.getRuntime().exec("su"); 其中,Runtime.getRuntime().exec可以直接调用底层linux的程序或脚本;

 1 // 执行命令并且输出结果 
 2     public static String execRootCmd(String cmd) { 
 3         String result = ""; 
 4         DataOutputStream dos = null; 
 5         DataInputStream dis = null; 
 6          
 7         try { 
 8             Process p = Runtime.getRuntime().exec("su");// 经过Root处理的android系统即有su命令 
 9             dos = new DataOutputStream(p.getOutputStream()); 
10             dis = new DataInputStream(p.getInputStream()); 
11  
12             dos.writeBytes(cmd + "\n"); 
13             dos.flush(); 
14             dos.writeBytes("exit\n"); 
15             dos.flush(); 
16             String line = null; 
17             while ((line = dis.readLine()) != null) { 
18                 Log.d("result = ", line); 
19                 result += line; 
20             } 
21             p.waitFor(); 
22         } catch (Exception e) { 
23             e.printStackTrace(); 
24         } finally { 
25             if (dos != null) { 
26                 try { 
27                     dos.close(); 
28                 } catch (IOException e) { 
29                     e.printStackTrace(); 
30                 } 
31             } 
32             if (dis != null) { 
33                 try { 
34                     dis.close(); 
35                 } catch (IOException e) { 
36                     e.printStackTrace(); 
37                 } 
38             } 
39         } 
40         Log.d(TAG, "execRootCmd cmd = " + cmd + ", result = " + result); 
41         return result; 
42 } 

 

3、去掉su程序中的用户uid判断

执行包含execRootCmd("echo test")代码的apk应用后,result无返回; 检查su的代码发现在其main()函数中:
if (su_from.uid == AID_ROOT || su_from.uid == AID_SHELL)
allow(shell, orig_umask);
即只有root或shell用户可以执行,去掉此处的限制;再次运行,发现result = test 有回复了。

 

4、setresuid为什么失败?

 

这样就成功了吗? 执行execRootCmd("id"), 日志如下:
DEBUG/rtc_test(5732): execRootCmd cmd = id, result = uid=10081(app_81) gid=10081(app_81) groups=1015(sdcard_rw)
显然用户uid没有切换到root。
分析su中的代码,allow()中调用了
setresgid(to->uid, to->uid, to->uid);
setresuid(to->uid, to->uid, to->uid);
Setresuid设置真实的、有效的和保存过的uid, 要想切换到root用户,那么su必须是已root用户运行的进程。

 

5、liunx文件权限中的S位

R,W,X是基本权限 S、T是特殊权限;
r(Read,读取):对文件而言,具有读取文件内容的权限;对目录来说,具有浏览目 录的权限。
w(Write,写入):对文件而言,具有新增、修改文件内容的权限;对目录来说,具有删除、移动目录内文件的权限。
x(eXecute,执行):对文件而言,具有执行文件的权限;对目录了来说该用户具有进入目录的权限。
s或S(SUID,Set UID):可执行的文件搭配这个权限,便能得到特权,任意存取该文件的所有者能使用的全部系统资源。请注意具备SUID权限的文件,黑客经常利用这种权限,以SUID配上root帐号拥有者,无声无息地在系统中开扇后门,供日后进出使用。
T或T(Sticky):/tmp和、/var/tmp目录供所有用户暂时存取文件,亦即每位用户皆拥有完整的权限进入该目录,去浏览、删除和移动文件。
调用“chmod 6755 su”修改su文件的属性,让su的执行者具有root权限。

修改之后,再次运行execRootCmd("id"),日志如下:
DEBUG/rtc_test(3686): execRootCmd cmd = id, result = uid=0(root) gid=0(root) groups=1015(sdcard_rw)
显然,切换成功了。

 

6、深入一下:Linux根据文件的s位设置uid,代码在哪里实现的?

 先看一下创建进程的过程,先fork子进程,再exec加载新进程;

fork时和父进程一样,不可能在这里设置uid;那比较有可能在exec阶段了。
早期的linux版本,在 exec.c文件中do_execve函数:
int do_execve(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp) {
......
// 根据其属性(对应i 节点的uid 和gid),看本进程是否有权执行它
//如果设置了S_ISUID,则进程具有节点的uid
i = inode->i_mode;
e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;

 ......

// 重新设置进程的用户id 和组id
current->euid = e_uid;
current->egid = e_gid;

 新版本的linux代码中也有类似的实现:

do_execve ---> do_execve_common() ---> prepare_binprm()


7、linux为什么要有这种切换uid的功能?

A、最常用的需求是修改密码,在Linux系统中每个普通用户都可以更改自己的密码,这是合理的设置。问题是:用户的信息保存在文件/etc/passwd中,用户的密码保存在文件/etc/shadow中,也就是说用户更改自己密码时是修改了/etc/shadow文件中的加密密码,这个时候就需要用到S位了;
B、另外,就是比较常用的网络指令。


8、回头看下第三步的修改(去掉su程序中的uid判断)是否必要?

既然设置S位后,新进程以文件所有者(root)的权限在运行,那么是否可以去掉第三步的修改?
在main()函数中,还原第三步修改,发现无法执行"echo test";在su中加log信息
LOGD("su.c main() from.uid = %d, euid = %d, to.uid = %d.", su_from.uid, geteuid(), su_to.uid);
打印日志如下:
DEBUG/su(3642): su.c main() from.uid = 10081, euid = 0, to.uid = 0.

 linux系统中每个进程都有2个ID,分别为用户ID(uid)和有效用户ID(euid),UID一般表示进程的创建者(属于哪个用户创建),而EUID表示进程对于文件和资源的访问权限(具备等同于哪个用户的权限)。C语言中,可以通过函数getuid()和geteuid()来获得进程的两个ID值。

在exec中修改的只是euid,uid还是应用的uid。


9、Superuser超级用户权限授权程序介绍

解压其升级包Superuser-3.2-arm-signed.zip,主要包含su和Superuser.apk及一些升级辅助文件;su和我们编译的差不多,apk提供了一些供用户操作的界面;当有用户调用su指令时,即通知到Superuser.apk,对用户的行为进行管理。当用户允许之后,下次就不再提醒。