我在博文《Android程序的安全系统》中提到两种让root权限的办法。最近在网上发现很多朋友转载那篇文章,但是对那篇文章中提到的第一种方法怎样实现,不是很明白。本文将会以一个例子实现来演示怎样让一个Android应用程序获得root权限。
问题
我遇到的问题是我想在Java应用程序中动态mount一个NFS的系统,但是执行mount命令必须要要root权限才可以。一般情况下,在Android的Java层是不能获得root权限的。
思路
我在博文《Android程序的安全系统》中提到两种思路:
1、实现一个init实现一个Service,来帮助Android应用程序执行root权限的命令。
2、实现一个虚拟设备,这个设备帮助Android应用程序执行root权限的命令。
本文将会选择第一种来解决Android应用程序mount NFS文件系统的问题。
Init.rc Service
在Android系统init.rc中定义很多Service,具体定义格式可以参考《Android Platform Developer’s Guide》中的“Android Init Language”。Init.rc中定义的Service将会被Init进程创建,这样将可以获得root权限。
现在问题是Android应用程序怎样启动让init进程知道我们想运行那个进程呢?答案是设置系统属性“ctl.start”,把“ctl.start”设置为你要运行的Service,假设为“xxx”,Android系统将会帮你运行“ctl.start”系统属性中指定的Service。那么运行结果init进程将会将会写入“init.svc.”+Service名称,也就是“init.svc.xxx”,应用程序可以参考查阅这个值来确定Service执行的情况。想更深入了解Android property系统可以参考博文《[转]Android Property System》。
Android property权限
难道Android属性“ctl.start”是所有进程都可以设置的吗?那世界不就乱套了,谁都可以可以执行init.rc中Service了,查看property_service.c中的源码,设置Android系统属性的函数为handle_property_set_fd:
void handle_property_set_fd(int fd)
{
......
switch(msg.cmd) {
case PROP_MSG_SETPROP:
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
if(memcmp(msg.name,"ctl.",4) == 0) {
if (check_control_perms(msg.value, cr.uid, cr.gid)) {
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d/n",
msg.name + 4, msg.value, cr.uid, cr.pid);
}
}
......
}
}
从源码中我们发现如果设置“ctl.”开头的Android系统property,将会调用check_control_perms函数来检查调用者的权限,其定义如下:
static int check_control_perms(const char *name, int uid, int gid) {
int i;
if (uid == AID_SYSTEM || uid == AID_ROOT)
return 1;
/* Search the ACL */
for (i = 0; control_perms[i].service; i++) {
if (strcmp(control_perms[i].service, name) == 0) {
if ((uid && control_perms[i].uid == uid) ||
(gid && control_perms[i].gid == gid)) {
return 1;
}
}
}
return 0;
}
我们发现root权限和system权限的应用程序将会授权修改“ctl.”开头的Android系统属性。否则将会检查control_perms全局变量中的定义权限和Service。
如果想更深入的了解Android Init进程和Android Property的权限控制,请参考《Android Permission》。
实例
通过上面的介绍我们基本已经有思路了,下面以上面提出的mount nfs文件系统为例说明:
1、首先定义一个执行mount的脚本,我把它位于/system/etc/mount_nfs.sh,定义如下:
#!/system/bin/sh
/system/bin/busybox mount -o rw,nolock -t nfs 192.168.1.6:/nfs_srv /data/mnt
不要忘了把它加上可执行权限。
2、在init.rc中加入一个Service定义,定义如下:
service mount_nfs /system/etc/mount_nfs.sh
oneshot
disabled
3、让自己的应用程序获得system权限,博文《Android程序的安全系统》中提到了怎样获得system权限,请参考,这里就不赘述了。
4、在自己应用程序中设置System系统属性“ctl.start”为“mount_nfs”,这样Android系统将会帮我们运行mount_nfs系统属性了。这里需要强调的是不能够调用System.getProperty,这个函数只是修改JVM中的系统属性。而不能修改Android的系统属性。可以调用android.os.SystemProperties(Android 2.1 Eclair系统可以调用这个API),如果你的Android版本不能调用这个类,只能通过JNI,调用C/C++层的API property_get和property_set函数了。如果想详细了解请参考《android property system》。代码如下:
1
SystemProperties.set("ctl.start", "mount_nfs");
5、最后在自己应用程序中,读取“init.svc.mount_nfs”Android系统Property,检查执行结果。代码如下:
while(true)
{
mount_rt = SystemProperties.get("init.svc.mount_nfs", "");
if(mount_rt != null && mount_rt.equals("stopped"))
{
return true;
}
try
{
Thread.sleep(1000);
}catch(Exception ex){
Log.e(TAG, "Exception: " + ex.getMessage());
}
}
init进程维护一个service的队列,所以我们需要轮训来查询service的执行结果。
通过上面的这些步骤,Android应用程序就能够调用init.rc中定义的Service了。这样你的Android应用程序也就获得了root权限。
总结
通过上文可以看出,在Android获得root权限还是需要一些前提的,比如:
1、必须是Android系统开发人员,否则你无法修改init.rc等文件。 2、你的应用程序必须要获得system权限。
这样可以防止root权限被应用程序无限制的使用,最终危及Android系统安全。
希望本文对你能有所帮助,如果有错误之处敬请指正。
原文作者:Simon_fu
原文链接:http://my.unix-center.net/~Simon_fu/?p=951
转载编辑: flysolo
转载地址:http://disanji.net/2011/01/24/android-application-root/