在普通的 Linux 系统中,只要可执行文件设置了 s 权限,就可以调用 setuid(0) 将自身提升到 root。例如,我们编写下面的一段程序:
#include <sys/types.h> #include <unistd.h> #include <stdio.h> void print(void) { printf( " UID GID \n" "Real %d Real %d \n" "Effective %d Effective %d \n", getuid (), getgid (), geteuid(), getegid() ); } int main(void) { print(); setuid(0); print(); return 0; }
该程序会先打印出自己的 uid 和 gid,然后执行 setuid(0) 提升权限,然后再打印 uid 和 gid 看效果。
假设编译出来的文件是 test,通过 chown 和 chmod 将其设置 s 权限:
ls -l test
-rwsr-sr-x root root 2636 2012-02-07 11:28 test
此时,执行 test 的结果可能是:
UID GID Real 501 Real 20 Effective 0 Effective 0 UID GID Real 0 Real 20 Effective 0 Effective 0
也就是说,在 setuid(0) 之前,uid = 501,执行 setuid(0) 之后,uid = 0,已经提升到 root 权限了。
但是,同样的程序,放到 Android 系统里,却遇到了问题。我们首先用下面的命令把编译好的 Android binary 复制到手机里:
adb push test /data/local
然后设置 /data/local/test 文件的 owner,可执行权限和 s 权限。然后执行 test,结果成了:
UID GID Real 2000 Real 2000 Effective 2000 Effective 2000 UID GID Real 2000 Real 2000 Effective 2000 Effective 2000
可见,uid 和 gid 都没有变化,setuid() 没有生效。为什么呢?检查了半天,最后发现,是 Android kernel 对 setuid() 进行了修改,只有在 /system 分区下,s 权限才起作用。将 test 文件复制到 /system 分区下,设置 s 权限后,执行的结果就成了:
UID GID Real 2000 Real 2000 Effective 0 Effective 0 UID GID Real 0 Real 2000 Effective 0 Effective 0