写这篇文章前,首先要感谢Simon_fu,他的两篇关于root权限的文章对于我的工作起到了非常大的帮助,这篇文章可以说是对他的文章的一个补充。Simon_fu的文章可以参考如下两个网页:

Android程序的安全系统

Android应用程序获得root权限

 

       一般来说,Android下的应用程序可以“直接”得到的最大的权限为system,但是如果我们需要在程序中执行某些需要root权限的命令,如ifconfig 等,就需要 root权限了。按照Simon的文章中提到的,应用程序有以下两种办法临时获得root权限:

1)       实现一个init实现一个 Service ,来帮助 Android 应用程序执行 root 权限的命令。

2)       实现一个虚拟设备,这个设备帮助Android应用程序执行root 权限的命令。

 

第二种办法我这里没有尝试,暂时也不会。这里讲讲我在实现第一种办法的过程和遇到的一些问题。

 

1.      将我们要执行的命令写成脚本,或者可执行程序。

下面是我的脚本ifconfig_test.sh

# /system/bin/sh

ifconfig

       注意: 脚本的第一行必须为 # /system/bin/sh,否则无法执行,通过dmesg可以查看到信息内容为 cannot execve ./ifconfig_test.sh: Exec format error

 

也可以采用C/C++编写需要执行的命令或者程序,并在编译image的时候编译成可执行程序。

 

2.      init.rc中注册service

Android 中的 service 需要在init.rc中注册,Init.rc 中定义的 Service将会被init进程创建,这样将可以获得 root 权限。当得到相应的通知(通过属性设置)后,init进程会启动该service

本文中注册的内容如下:

service ifconfig_test /system/etc/ifconfig_test.sh

oneshot

disabled

       其中, oneshot表示程序退出后不再重新启动,disabled表示不在系统启动时启动。

 

注意:这里service name不能超过 16 个字符。我之前的service name由于定义的比较长,18个字符,设置属性通知 service 启动后查看 dmesg可以看到提示:init: no such service。查看/system/core/init/parser.c的源代码,在parse_service->valid_name函数中可以看到如下内容:if (strlen(name) > 16) { return 0; },证明service的名字的确不能超过 16 个字符。

 

3.      Android应用程序提升为system 权限

既然应用程序可以通过启动service获得root 权限,那么岂不是很不安全。 Android 考虑到了这点,规定只有 system 权限的应用程序才能设置属性,通知 service 启动。关于提升 system权限的文章网上已有很多,这里就不再细说,可以参考如下两篇文章:

http://blog.csdn.net/liujian885/archive/2010/03/22/5404834.aspx

http://labs.chinamobile.com/mblog/532767_73183

 

4.      在应用程序中添加属性设置代码

前面已经提到,对于Android来说,应用程序通知init 启动 service是通过设置系统属性来完成的,具体为设置System系统属性“ctl.start” “ifconfig_test”,这样Android系统将会帮我们运行 ifconfig_test 这个service了。

对该系统属性的设置有三种方法,分别对应三种不同的应用程序:  

1)  Java 代码

  Android 在 Java 库中提供 System.getProperty 和 System.setProperty 方法, Java 程序可以通过他们来设置和获得属性。代码如下:

SystemProperties.set("ctl.start", "ifconfig_test");

上面的代码是通知Android执行ifconfig_test service,如果需要查询当前service 执行的状态,如是否执行完毕,可以通过如下代码查询:

ret = SystemProperties.get("init.svc. ifconfig_test ", "");

if(ret != null && ret.equals("stopped"))

  {

      return true;

  }

        2) JNI代码

当编写NDK的程序时,可以使用property_get property_set这两个API来获得和设置属性。使用这两个 API 必须要包含头文件 cutils/properties.h和链接libcutil库。

 

3) Shell脚本

Android 提供了命令行 setprop getprop来设置和获取属性,他们可以在脚本中被使用。

 

由于我的程序是在JNI中调用脚本,脚本中又执行ifconfig,因此我将设置属性的部分放在了脚本中完成,代码如下:

setprop ctl.start ifconfig_test

 

#wait for the service until it stops

ret=1

while [ $ret -ne 0 ]

do

       getprop | grep "$ENABLE_MAPPER_SRV" | grep stopped

       ret=$?

done

       通过上面 4 个步骤,Android应用程序就获得了root 权限,更具体的说,是在执行我们需要执行的命令时临时获得了root 权限。

 

Android应用程序获得root权限

    我在博文《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属性系统》。

Android property权限

    难道Android属性“ctl.start”是所有进程都可以设置的吗?那世界不就乱套了,谁都可以可以执行init.rc中Service了,查看property_service.c中的源码,设置Android系统属性的函数为handle_property_set_fd:

   1: void handle_property_set_fd(int fd)

   2: {

   3:     ......

   4:     switch(msg.cmd) {

   5:     case PROP_MSG_SETPROP:

   6:         msg.name[PROP_NAME_MAX-1] = 0;

   7:         msg.value[PROP_VALUE_MAX-1] = 0;

   8:  

   9:         if(memcmp(msg.name,"ctl.",4) == 0) {

  10:             if (check_control_perms(msg.value, cr.uid, cr.gid)) {

  11:                 handle_control_message((char*) msg.name + 4, (char*) msg.value);

  12:             } else {

  13:                 ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",

  14:                         msg.name + 4, msg.value, cr.uid, cr.pid);

  15:             }

  16:         }

  17:         ......

  18:     }

  19: }

    从源码中我们发现如果设置“ctl.”开头的Android系统property,将会调用check_control_perms函数来检查调用者的权限,其定义如下:

   1: static int check_control_perms(const char *name, int uid, int gid) {

   2:     int i;

   3:     if (uid == AID_SYSTEM || uid == AID_ROOT)

   4:         return 1;

   5:  

   6:     /* Search the ACL */

   7:     for (i = 0; control_perms[i].service; i++) {

   8:         if (strcmp(control_perms[i].service, name) == 0) {

   9:             if ((uid && control_perms[i].uid == uid) ||

  10:                 (gid && control_perms[i].gid == gid)) {

  11:                 return 1;

  12:             }

  13:         }

  14:     }

  15:     return 0;

  16: }

    我们发现root权限和system权限的应用程序将会授权修改“ctl.”开头的Android系统属性。否则将会检查control_perms全局变量中的定义权限和Service。

    如果想更深入的了解Android Init进程和Android Property的权限控制,请参考《Android Permission》。

实例

    通过上面的介绍我们基本已经有思路了,下面以上面提出的mount nfs文件系统为例说明:

1、首先定义一个执行mount的脚本,我把它位于/system/etc/mount_nfs.sh,定义如下:

   1: #!/system/bin/sh

   2:  

   3: /system/bin/busybox mount -o rw,nolock -t nfs 192.168.1.6:/nfs_srv /data/mnt

不要忘了把它加上可执行权限。

2、在init.rc中加入一个Service定义,定义如下:

   1: service mount_nfs /system/etc/mount_nfs.sh

   2:     oneshot

   3:     disabled

3、让自己的应用程序获得system权限,博文《Android程序的安全系统》中提到了怎样获得system权限,获得system用户权限,需要以下步骤:

(1). 在应用程序的AndroidManifest.xml中的manifest节点中加入android:sharedUserId="android.uid.system"这个属性。
(2). 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform这一行
(3). 使用mm命令来编译,生成的apk就有修改系统时间的权限了。

 

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属性系统。代码如下:

   1: SystemProperties.set("ctl.start", "mount_nfs");

5、最后在自己应用程序中,读取“init.svc.mount_nfs”Android系统Property,检查执行结果。代码如下:

   1: while(true)

   2: {

   3:     mount_rt = SystemProperties.get("init.svc.mount_nfs", "");

   4:     if(mount_rt != null && mount_rt.equals("stopped"))

   5:     {

   6:         return true;

   7:     }

   8:     

   9:     try

  10:     {

  11:         Thread.sleep(1000);

  12:     }catch(Exception ex){

  13:         Log.e(TAG, "Exception: " + ex.getMessage());

  14:     }

  15: }

    init进程维护一个service的队列,所以我们需要轮训来查询service的执行结果。

    通过上面的这些步骤,Android应用程序就能够调用init.rc中定义的Service了。这样你的Android应用程序也就获得了root权限。

总结

   通过上文可以看出,在Android获得root权限还是需要一些前提的,比如:

1、必须是Android系统开发人员,否则你无法修改init.rc等文件。 2、你的应用程序必须要获得system权限。

    这样可以防止root权限被应用程序无限制的使用,最终危及Android系统安全。

    希望本文对你能有所帮助,如果有错误之处敬请指正。

你可以通过RSS 2.0 来跟踪本文的所有评论。 你可以对本文发表评论,或者对本文进行评分,或者从你的网站引用 本文。

相关文章

COMMENTS

52 条评论

  1. bevis

    2011-01-20 14:51:20

    谢谢simon,每次定期的来你这里看看,总会学到很多知识。前几天就刚好遇到你说的这个问题,今天来这里看了一下,问题看来可以解决了,呵呵,谢谢!

    [回复]

    Simon_fu 回复:

    欢迎你多来关注Simon,也欢迎你对Simon的文章多提宝贵意见!大家共同进步!

    [回复]

  2. flye03

    2011-01-21 16:12:07

    后来我更改service 为
    service mtest /system/bin/logwrapper /system/etc/a.sh
    后程序就能正确运行了,不知道是什么原因。总体上是通过了,感谢simon的分享。

    [回复]

  3. tang

    2011-02-24 13:44:13

    Simon,你真棒

    [回复]

    Simon_fu 回复:

    欢迎大家多指正!
    共同进步~!

    [回复]

  4. gqdy365

    2011-02-24 18:11:04

    我有个问题想请教一下楼主,我在做fm模块时遇到了一个权限问题,驱动层生成的文件节点在dev目录下面,上层打开它时说没有权限,我通过串口修改权限后是可以打开的,我网上查了一下,有人说是在init.rc里面做初始化修改,可以指点一下吗?

    [回复]

    Simon_fu 回复:

    确实可以在init.rc中做,init.rc提供了chown和chmod命令,你可以参考system/core/init/readme.txt文件,里面对init.rc的命令进行了详细的描述,网上也有人已经翻译成了中文。

    [回复]

    gqdy365 回复:

    谢谢你,支持你的博客!

    [回复]

  5. billyc

    2011-03-10 03:06:19

    Thank you for this great article. The only one article talk about get app root access in an unrooted Android device.

    Great Job! and thank you again.

    [回复]

  6. 匿名

    2011-03-14 11:08:10

    总结
    通过上文可以看出,在Android获得root权限还是需要一些前提的,比如:

    1、必须是Android系统开发人员,否则你无法修改init.rc等文件。 2、你的应用程序必须要获得system权限。

    这样可以防止root权限被应用程序无限制的使用,最终危及Android系统安全。

    不等于没说么,如何是Android系统开发人员的话,难道他就没有root权限么? 没有Root权限,他还叫系统开发人员么?

    最核心的问题是,大家都想知道,一般的应用程序如何能够请求到root权限,我目前system权限已经有了,就是没有root权限,因为我不是系统开发人员。

    [回复]

    Simon_fu 回复:

    你不能修改init.rc呢?
    如果能修改就可以了~!
    Android系统设计目标就是不把root暴露给应用程序的,需要root权限的操作都已经封装成Service了,当然我的实现也是这样的。
    如果你已经获得了system权限,那么你可以调用系统中定义的各种service,由各种service来执行需要root权限的操作。如果你要的操作没有对应的service,那么这就是你的系统提供商不想暴露给你。你唯一能做的就是破解它,来获得root权限了。
    Android系统开发人员有root权限,但是他们要把需要root权限的操作暴露给上层的应用程序也是需要考虑的一个方面。也是Android系统安全的一个考虑的方面。

    [回复]

  7. 匿名

    2011-03-20 12:36:49

    simon您好!
    您提到“实现一个虚拟设备,这个设备帮助Android应用程序执行root权限的命令”

    我现在已经写了一个相应驱动,但不知道android app如何跟底层的linux驱动通信,一直找不到头绪,能指导下我吗?
    万分感谢 :-)

    [回复]

    Simon_fu 回复:

    实现一个字符设备驱动,然后app打开这个设备驱动文件,然后向里面写入需要执行的命令,由驱动程序代理执行相应的命令。

    [回复]

    匿名 回复:

    谢谢您!
    祝博主天天开心!

    [回复]

  8. 匿名

    2011-04-07 17:09:36

    simon您好!
    看了您的博客,收益很大,想问问您两个问题
    1,您说的使用虚拟字符驱动的方法,在方法里怎样调用eth0让它开启,设置ip等呀,可以提供些函数嘛?
    2,使用服务的方法,怎样把参数传进去呀?运用一个脚本是ok的。
    希望指导下,万分感谢

    [回复]

    Simon_fu 回复:

    首先,实现字符设备的方法,我是这样做的,就和打开普通文件一样打开虚拟字符设备,然后把需要执行的命令一次性写入该设备文件,设备驱动就开始代理执行这些命令;你可以向做驱动的同事请教这个问题。
    其次:服务的方法,脚本应该也是可以处理参数的,当然你也可以通过C/C++编写一个程序来完成服务也是一样的。你可以查阅一下脚本的实现,另外init服务也是可以加参数的,请参考Android init readme.

    [回复]

    匿名 回复:

    很谢谢您的及时回复
    驱动也是我自己写的,现在问题就在于设备怎样去执行那些命令,驱动中没有system函数的调用,ifc_up也用不了,如何把ifconfig这个文件加入的话好像就麻烦了点,您说的“设备驱动就开始代理执行这些命令”是怎样代理的,能指点下嘛,非常感谢

    [回复]

    Simon_fu 回复:

    请参考一下这个帖子:
    http://www.unixresources.net/linux/clf/linuxK/archive/00/00/57/83/578302.html
    另外,虚拟设备方案不是Android平台下面常规方案,主要涉及到安全性;只有万不得已的时候再用。优先选用init服务的方案会比较好。

    [回复]

    匿名 回复:

    谢谢您如此热心。
    现在就是在考虑init服务写的那个socket如何能从用户手中获得参数,而不是在init中配置参数。
    非常感谢您的回答,祝您工作顺利,身体健康

    [回复]

  9. byfrancis

    2011-04-13 10:23:08

    Simon您好,看了你的博文感觉受益匪浅,按照我的理解,文中的方法好像没有不能让用户“一键获取root的权限”,或者是让应用程序自动获取root权限。我知道网上有个应用程序Z4root,可以让用户一键获取root权限,但我不知道是怎么实现的。如果是我对您的方法没有理解,或者是您有一键获取root权限的方法,能指点一下吗?不胜感激!

    [回复]

    Simon_fu 回复:

    这个就是传说中root破解,我没有研究过这种破解方式,没有发言权。不好意思!

    [回复]

    byfrancis 回复:

    非常感谢,期待您后续的android博文

    [回复]

  10. chaos

    2011-04-15 08:03:01

    simon牛,问你一个比较傻的问题啊

    像RE之类的文件甚至能有重新把根目录挂载为RW的权限

    这种权限也是通过这种方法实现的吗

    还是说已经root的手机应用程序即可以通过

    process = Runtime.getRuntime().exec(“su”);

    这种语句来获取root权限??

    [回复]

    Simon_fu 回复:

    我在网上看到很多人都是这样获得root权限的,但是这必须要是rooted过的手机,否则没戏!所以这种方法不通用。rooted手机本身也是利用了系统漏洞,如果那一天把漏洞堵上,也就没戏了!

    [回复]

    chaos 回复:

    谢谢simon牛

    我看了下android 那些terminal模拟器的代码,自己编译了,发现他们在没有root权限的机器上是没有办法执行su的,也看了下superuser的代码,它的思路是修改机器上的su程序,替换成自己编译的su程序,大致明白了通过process = Runtime.getRuntime().exec(“su”);怎么获取root权限的。

    ps:

    有一个小问题,我看了你上文提到的请参考《Android Permission》。里面我尝试着对init.rc文件进行修改,但是每次修改后重启机器init.rc文件都恢复成为修改前的状态了,请问你有修改过该文件吗?

    [回复]

    Simon_fu 回复:

    因为你的根分区是是只读的,试试adb remount行不行?

    [回复]

    chaos 回复:

    对 我是root权限 mount -o rw,remount -t rootfs /

    [回复]

    匿名 回复:

    init.rc 不是在ramdisk裡面嗎?ramdisk運行在內存裡面,
    每次重開機當然會恢復之前的狀態。

    [回复]

    chaos 回复:

    谢谢了 那init.rc未启动时具体在rom的什么文件里面呢?

    是不是在启动之后再映射到内存内吗

    如果要修改是不是得修改rom然后重新刷机才可以成功??

    [回复]

    Simon_fu 回复:

    呵呵!我只是研究了这个技术,刷机这种事儿我可没干过!
    这个帮不了你啊!
    你可以到刷机网站上面请教这个问题。

    [回复]

    chaos 回复:

    那在为映射到内存之前init.rc对应哪个文件呢?

    是否修改init.rc必须先修改rom

    然后重新刷机才能生效??

    [回复]

  11. pcsuite

    2011-04-16 12:15:53

    楼主,你好,我看了你很多篇文章,写的都很好,而且对我的帮助也很大,在此表示感谢。

    [回复]

  12. dxz

    2011-04-22 18:18:20

    你好,我要是想用我的应用在system目录下创建一个文件,用你说的那方法好像不可行吧。能不能给个思路。谢谢

    [回复]

    Simon_fu 回复:

    你遇到什么问题了吗?
    我觉得是可以做到的!因为已经有root权限了,在系统中创建文件没有理由不成功的。
    system分区是只读的,你可能需要remount一下吧。

    [回复]

    dxz 回复:

    您好,我是初学者,您说的那个服务该怎么写呢 ?我还是没明白那服务是怎么回事?为什么掉声明一个服务就可以应用就可以获取root权限呢 ?还是应用只能调用声明的那个服务里的方法?

    [回复]

  13. chaos

    2011-04-24 01:34:49

    simon牛

    最近我打算详细阅读以下android框架的源代码

    ActivityManagerService.java

    对ActivityManagerService的大致流程有所了解了

    但是对于细节很难看懂

    能不能介绍一下你阅读android源码的技巧吗

    或者在国外有没有对android相关源码剖析的文章或blog吗?

    尤其是对ActivityManagerService.java部分的??

    非常感谢

    [回复]

    Simon_fu 回复:

    说实话,上万行的代码我也不看!
    只是说自己需要的时候,顺藤摸瓜摸到这里而已!
    另外既然是service,熟悉了Android service对你理解它也很有帮助。

    [回复]

  14. chaos

    2011-04-24 19:20:04

    simon牛,请教一个问题

    请问一下能不能指点一下阅读android框架源代码的技巧啊

    比如我最近在阅读ActivityManagerService.java 光这一个模块就一万多行的代码

    大体知道是一个什么它流程 但细节深究实在是很乱啊 看不出头绪

    或者外国有没有相关剖析android源代码的blog或文章吗??

    [回复]

  15. aplikacje na telefon

    2011-04-30 08:58:52

    The most comprehensive and very well thought out write up I have found on this subject on the net. Keep on writing, I will keep on coming by to read your new content. This is my fourth time coming by your blog.

    [回复]

  16. soros

    2011-05-25 10:04:59

    谢谢楼主共享,不知道楼主是否研究过Runtime.exec() 方法执行shell 指令呢? 这样是不是也能够获取到root 执行权限呢? 我正在使用这种方法,但是却不能够正常执行shell 脚本。

    [回复]

    Simon_fu 回复:

    可以肯定的告诉你,绝对不可能拥有root权限。因为只有在java层才能调用Runtime.exec(),这样的进程不可能是root权限的进程,最高是system权限。

    [回复]

  17. abc

    2011-05-28 13:57:52

    理解错,谢谢,问题已经解决了

    [回复]

  18. abc

    2011-06-03 09:55:44

    解决了,是线程并发问题。

    [回复]

  19. 匿名

    2011-07-10 16:35:53

    simon_fu;你好,我想请问一下,我要通过jni方式去操作/dev目录下的设备文件,我看您的博文说只要获得system权限就可以操作设备了,但是我获得了system权限还是不能访问设备文件,请问访问设备文件ap必须要具备root权限吗?

    [回复]

    Simon_fu 回复:

    这个就要看你的设备驱动要求的权限了!
    有的设备驱动必须要root才可以!
    需要驱动哦你工程师商议!

    [回复]

  20. lufengdie

    2011-08-01 15:01:21

    “首先定义一个执行mount的脚本,我把它位于/system/etc/mount_nfs.sh”

    这一步不能操作啊。

    请教楼主、

    [回复]

    Simon_fu 回复:

    说明你没有设备的root权限,或者你没有remount system分区。

    [回复]

  21. 匿名

    2011-08-19 22:34:30

    你好simon,我最近遇到一个问题,我的apk要执行一些需要root权限的命令.看了你介绍的这种方法(第一种),可是这种方法怎么取得命令执行的输出呢?

    [回复]

    Simon_fu 回复:

    把命令行重定向到文件,然后执行结束之后,读取文件内容。

    [回复]

  22. vincent.yan

    2011-08-23 19:40:10

    simon牛,看了你的几篇文章,收益匪浅
    但是我现在遇到类似问题,希望能给予指导。

    首先我要实现的是一个android应用,但是我想要获取到root权限。(不是Android系统开发人员),这个情况该怎么做?是否用第二种方法可以实现

    或者你有什么好方法让应用在非root权限下,去抓屏呢?

    [回复]

    Simon_fu 回复:

    抓屏?
    我觉得应该是要用到framebuffer吧!
    没有root权限我觉得难!
    不好意思!

    [回复]

  23. Simon_fu

    2011-09-14 13:59:41

    研究一下init的帮助文档,里面解释了init service参数传递问题。

    [回复]