应用获取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,对用户的行为进行管理。当用户允许之后,下次就不再提醒。